summaryrefslogtreecommitdiff
path: root/src/cargopacket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cargopacket.cpp')
-rw-r--r--src/cargopacket.cpp314
1 files changed, 291 insertions, 23 deletions
diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp
index 0b92de0c8..42eca0b55 100644
--- a/src/cargopacket.cpp
+++ b/src/cargopacket.cpp
@@ -12,6 +12,10 @@
#include "stdafx.h"
#include "core/pool_func.hpp"
#include "economy_base.h"
+#include "station_base.h"
+#include "cargodest_func.h"
+#include "cargodest_base.h"
+#include "settings_type.h"
/* Initialize the cargopacket-pool */
CargoPacketPool _cargopacket_pool("CargoPacket");
@@ -24,6 +28,12 @@ CargoPacket::CargoPacket()
{
this->source_type = ST_INDUSTRY;
this->source_id = INVALID_SOURCE;
+ this->dest_xy = INVALID_TILE;
+ this->dest_id = INVALID_SOURCE;
+ this->dest_type = ST_INDUSTRY;
+ this->flags = 0;
+ this->next_order = INVALID_ORDER;
+ this->next_station = INVALID_STATION;
}
/**
@@ -33,21 +43,33 @@ CargoPacket::CargoPacket()
* @param count Number of cargo entities to put in this packet.
* @param source_type 'Type' of source the packet comes from (for subsidies).
* @param source_id Actual source of the packet (for subsidies).
+ * @param dest_xy Destination location of the packet.
+ * @param dest_type 'Type' of the destination.
+ * @param dest_id Actual destination of the packet.
+ * @param next_order Desired next hop of the packet.
+ * @param next_station Station to unload the packet next.
+ * @param flags Routing flags of the packet.
* @pre count != 0
* @note We have to zero memory ourselves here because we are using a 'new'
* that, in contrary to all other pools, does not memset to 0.
*/
-CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) :
+CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id, TileIndex dest_xy, SourceType dest_type, SourceID dest_id, OrderID next_order, StationID next_station, byte flags) :
feeder_share(0),
count(count),
days_in_transit(0),
source_id(source_id),
source(source),
source_xy(source_xy),
- loaded_at_xy(0)
+ loaded_at_xy(0),
+ dest_xy(dest_xy),
+ dest_id(dest_id),
+ flags(flags),
+ next_order(next_order),
+ next_station(next_station)
{
assert(count != 0);
this->source_type = source_type;
+ this->dest_type = dest_type;
}
/**
@@ -61,20 +83,32 @@ CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, So
* @param feeder_share Feeder share the packet has already accumulated.
* @param source_type 'Type' of source the packet comes from (for subsidies).
* @param source_id Actual source of the packet (for subsidies).
+ * @param dest_xy Destination location of the packet.
+ * @param dest_type 'Type' of the destination.
+ * @param dest_id Actual destination of the packet.
+ * @param next_order Desired next hop of the packet.
+ * @param next_station Station to unload the packet next.
+ * @param flags Routing flags of the packet.
* @note We have to zero memory ourselves here because we are using a 'new'
* that, in contrary to all other pools, does not memset to 0.
*/
-CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share, SourceType source_type, SourceID source_id) :
+CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share, SourceType source_type, SourceID source_id, TileIndex dest_xy, SourceType dest_type, SourceID dest_id, OrderID next_order, StationID next_station, byte flags) :
feeder_share(feeder_share),
count(count),
days_in_transit(days_in_transit),
source_id(source_id),
source(source),
source_xy(source_xy),
- loaded_at_xy(loaded_at_xy)
+ loaded_at_xy(loaded_at_xy),
+ dest_xy(dest_xy),
+ dest_id(dest_id),
+ flags(flags),
+ next_order(next_order),
+ next_station(next_station)
{
assert(count != 0);
this->source_type = source_type;
+ this->dest_type = dest_type;
}
/**
@@ -87,7 +121,7 @@ inline CargoPacket *CargoPacket::Split(uint new_size)
if (!CargoPacket::CanAllocateItem()) return NULL;
Money fs = this->feeder_share * new_size / static_cast<uint>(this->count);
- CargoPacket *cp_new = new CargoPacket(new_size, this->days_in_transit, this->source, this->source_xy, this->loaded_at_xy, fs, this->source_type, this->source_id);
+ CargoPacket *cp_new = new CargoPacket(new_size, this->days_in_transit, this->source, this->source_xy, this->loaded_at_xy, fs, this->source_type, this->source_id, this->dest_xy, this->dest_type, this->dest_id, this->next_order, this->next_station, this->flags);
this->feeder_share -= fs;
this->count -= new_size;
return cp_new;
@@ -111,9 +145,16 @@ inline void CargoPacket::Merge(CargoPacket *cp)
*/
/* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
{
+ /* Clear next hop of those packets that loose their destination. */
+ StationCargoList::InvalidateAllTo(src_type, src);
+
CargoPacket *cp;
FOR_ALL_CARGOPACKETS(cp) {
if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
+ if (cp->dest_type == src_type && cp->dest_id == src) {
+ cp->dest_id = INVALID_SOURCE;
+ cp->dest_xy = INVALID_TILE;
+ }
}
}
@@ -126,6 +167,7 @@ inline void CargoPacket::Merge(CargoPacket *cp)
CargoPacket *cp;
FOR_ALL_CARGOPACKETS(cp) {
if (cp->source == sid) cp->source = INVALID_STATION;
+ if (cp->next_station == sid) cp->next_station = INVALID_STATION;
}
}
@@ -229,6 +271,7 @@ void CargoList<Tinst>::Truncate(uint max_remaining)
uint diff = local_count - max_remaining;
this->count -= diff;
this->cargo_days_in_transit -= cp->days_in_transit * diff;
+ static_cast<Tinst *>(this)->RemoveFromCacheLocal(cp, diff);
cp->count = max_remaining;
max_remaining = 0;
} else {
@@ -245,56 +288,121 @@ void CargoList<Tinst>::Truncate(uint max_remaining)
* - MTA_CARGO_LOAD: Sets the loaded_at_xy value of the moved packets.
* - MTA_TRANSFER: Just move without side effects.
* - MTA_UNLOAD: Just move without side effects.
+ * - MTA_NO_ACTION: Does nothing for packets without destination, otherwise either like MTA_TRANSFER or MTA_FINAL_DELIVERY.
* @param dest Destination to move the cargo to.
* @param max_move Amount of cargo entities to move.
* @param mta How to handle the moving (side effects).
- * @param data Depending on mta the data of this variable differs:
- * - MTA_FINAL_DELIVERY - Station ID of packet's origin not to remove.
- * - MTA_CARGO_LOAD - Station's tile index of load.
- * - MTA_TRANSFER - Unused.
- * - MTA_UNLOAD - Unused.
+ * @param st Station ID where we are loading/unloading or STATION_INVALID for move from vehicle to vehicle.
* @param payment The payment helper.
+ * @param cur_order The current order of the loading vehicle.
+ * @param did_transfer Set to true if some cargo was transfered.
*
* @pre mta == MTA_FINAL_DELIVERY || dest != NULL
* @pre mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL
+ * @pre st != INVALID_STATION || (mta != MTA_CARGO_LOAD && payment == NULL)
* @return True if there are still packets that might be moved from this cargo list.
*/
template <class Tinst>
template <class Tother_inst>
-bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, uint data)
+bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer)
{
assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL);
+ assert(st != INVALID_STATION || (mta != MTA_CARGO_LOAD && payment == NULL));
+restart:;
Iterator it(this->packets.begin());
while (it != this->packets.end() && max_move > 0) {
CargoPacket *cp = *it;
- if (cp->source == data && mta == MTA_FINAL_DELIVERY) {
- /* Skip cargo that originated from this station. */
+ MoveToAction cp_mta = mta;
+ OrderID current_next_order = cp->NextHop();
+ StationID current_next_unload = cp->NextStation();
+
+ if (cp_mta == MTA_CARGO_LOAD) {
+ /* Invalid next hop but valid destination? Recompute next hop. */
+ if (current_next_order == INVALID_ORDER && cp->DestinationID() != INVALID_SOURCE) {
+ if (!this->UpdateCargoNextHop(cp, Station::Get(st), cid)) {
+ /* Failed to find destination, drop packet. */
+ it = this->packets.erase(it);
+ continue;
+ }
+ current_next_order = cp->NextHop();
+ current_next_unload = cp->NextStation();
+ }
+
+ /* Loading and not for the current vehicle? Skip. */
+ if (current_next_order != cur_order) {
+ ++it;
+ continue;
+ }
+ }
+
+ /* Has this packet a destination and are we unloading to a station (not autoreplace)? */
+ if (cp->DestinationID() != INVALID_SOURCE && cp_mta != MTA_CARGO_LOAD && payment != NULL) {
+ /* Not forced unload and not for unloading at this station? Skip the packet. */
+ if (cp_mta != MTA_UNLOAD && cp->NextStation() != INVALID_STATION && cp->NextStation() != st) {
+ ++it;
+ continue;
+ }
+
+ Station *station = Station::Get(st);
+
+ bool found;
+ StationID next_unload;
+ RouteLink *link = FindRouteLinkForCargo(station, cid, cp, &next_unload, cur_order, &found);
+ if (!found) {
+ /* Sorry, link to destination vanished, make cargo disappear. */
+ static_cast<Tinst *>(this)->RemoveFromCache(cp);
+ delete cp;
+ it = this->packets.erase(it);
+ continue;
+ }
+
+ if (link != NULL) {
+ /* Not final destination. */
+ if (link->GetOriginOrderId() == cur_order && cp_mta != MTA_UNLOAD) {
+ /* Cargo should stay on the vehicle and not forced unloading? Skip. */
+ ++it;
+ continue;
+ }
+ /* Force transfer and update next hop. */
+ cp_mta = MTA_TRANSFER;
+ current_next_order = link->GetOriginOrderId();
+ current_next_unload = next_unload;
+ } else {
+ /* Final destination, deliver. */
+ cp_mta = MTA_FINAL_DELIVERY;
+ }
+ } else if (cp_mta == MTA_NO_ACTION || (cp->source == st && cp_mta == MTA_FINAL_DELIVERY)) {
+ /* Skip cargo that is not accepted or originated from this station. */
++it;
continue;
}
+ if (did_transfer != NULL && cp_mta == MTA_TRANSFER) *did_transfer = true;
+
if (cp->count <= max_move) {
/* Can move the complete packet */
max_move -= cp->count;
it = this->packets.erase(it);
static_cast<Tinst *>(this)->RemoveFromCache(cp);
- switch (mta) {
+ cp->next_order = current_next_order;
+ cp->next_station = current_next_unload;
+ switch (cp_mta) {
case MTA_FINAL_DELIVERY:
payment->PayFinalDelivery(cp, cp->count);
delete cp;
continue; // of the loop
case MTA_CARGO_LOAD:
- cp->loaded_at_xy = data;
+ cp->loaded_at_xy = Station::Get(st)->xy;
break;
case MTA_TRANSFER:
cp->feeder_share += payment->PayTransfer(cp, cp->count);
break;
- case MTA_UNLOAD:
+ default:
break;
}
dest->Append(cp);
@@ -302,7 +410,7 @@ bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta
}
/* Can move only part of the packet */
- if (mta == MTA_FINAL_DELIVERY) {
+ if (cp_mta == MTA_FINAL_DELIVERY) {
/* Final delivery doesn't need package splitting. */
payment->PayFinalDelivery(cp, max_move);
@@ -323,12 +431,14 @@ bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta
if (cp_new == NULL) return false;
static_cast<Tinst *>(this)->RemoveFromCache(cp_new); // this reflects the changes in cp.
+ cp_new->next_order = current_next_order;
+ cp_new->next_station = current_next_unload;
- if (mta == MTA_TRANSFER) {
+ if (cp_mta == MTA_TRANSFER) {
/* Add the feeder share before inserting in dest. */
cp_new->feeder_share += payment->PayTransfer(cp_new, max_move);
- } else if (mta == MTA_CARGO_LOAD) {
- cp_new->loaded_at_xy = data;
+ } else if (cp_mta == MTA_CARGO_LOAD) {
+ cp_new->loaded_at_xy = Station::Get(st)->xy;
}
dest->Append(cp_new);
@@ -337,6 +447,12 @@ bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta
max_move = 0;
}
+ if (max_move > 0 && mta == MTA_CARGO_LOAD && cur_order != INVALID_ORDER && Station::Get(st)->goods[cid].cargo.CountForNextHop(INVALID_ORDER) > 0) {
+ /* We loaded all packets for the next hop, now load all packets without destination. */
+ cur_order = INVALID_ORDER;
+ goto restart;
+ }
+
return it != packets.end();
}
@@ -396,6 +512,158 @@ void VehicleCargoList::InvalidateCache()
this->Parent::InvalidateCache();
}
+/** Invalidate next unload station of all cargo packets. */
+void VehicleCargoList::InvalidateNextStation()
+{
+ for (VehicleCargoList::ConstIterator it = this->packets.begin(); it != this->packets.end(); ++it) {
+ (*it)->next_station = INVALID_STATION;
+ }
+}
+
+/**
+ * Update the local next-hop count cache.
+ * @param cp Packet the be removed.
+ * @param amount Cargo amount to be removed.
+ */
+void StationCargoList::RemoveFromCacheLocal(const CargoPacket *cp, uint amount)
+{
+ this->order_cache[cp->next_order] -= amount;
+ if (this->order_cache[cp->next_order] == 0) this->order_cache.erase(cp->next_order);
+}
+
+/**
+ * Update the cached values to reflect the removal of this packet.
+ * Decreases count and days_in_transit.
+ * @param cp Packet to be removed from cache.
+ */
+void StationCargoList::RemoveFromCache(const CargoPacket *cp)
+{
+ this->RemoveFromCacheLocal(cp, cp->count);
+ this->Parent::RemoveFromCache(cp);
+}
+
+/**
+ * Update the cache to reflect adding of this packet.
+ * Increases count and days_in_transit.
+ * @param cp New packet to be inserted.
+ */
+void StationCargoList::AddToCache(const CargoPacket *cp)
+{
+ this->order_cache[cp->next_order] += cp->count;
+ this->Parent::AddToCache(cp);
+}
+
+/** Invalidates the cached data and rebuild it. */
+void StationCargoList::InvalidateCache()
+{
+ this->order_cache.clear();
+ this->Parent::InvalidateCache();
+}
+
+/**
+ * Recompute the desired next hop of a cargo packet.
+ * @param cp Cargo packet to update.
+ * @param st Station of this list.
+ * @param cid Cargo type of this list.
+ * @return False if the packet was deleted, true otherwise.
+ */
+bool StationCargoList::UpdateCargoNextHop(CargoPacket *cp, Station *st, CargoID cid)
+{
+ StationID next_unload;
+ RouteLink *l = FindRouteLinkForCargo(st, cid, cp, &next_unload);
+
+ if (l == NULL) {
+ /* No link to destination, drop packet. */
+ this->RemoveFromCache(cp);
+ delete cp;
+ return false;
+ }
+
+ /* Update next hop info. */
+ this->RemoveFromCache(cp);
+ cp->next_station = next_unload;
+ cp->next_order = l->GetOriginOrderId();
+ this->AddToCache(cp);
+
+ return true;
+}
+
+/**
+ * Recompute the desired next hop of all cargo packets.
+ * @param st Station of this list.
+ * @param cid Cargo type of this list.
+ */
+void StationCargoList::UpdateCargoNextHop(Station *st, CargoID cid)
+{
+ uint count = 0;
+ StationCargoList::Iterator iter;
+ for (iter = this->packets.begin(); count < this->next_start + _settings_game.economy.cargodest.route_recalc_chunk && iter != this->packets.end(); count++) {
+ if (count < this->next_start) continue;
+ if ((*iter)->DestinationID() != INVALID_SOURCE) {
+ if (this->UpdateCargoNextHop(*iter, st, cid)) {
+ ++iter;
+ } else {
+ iter = this->packets.erase(iter);
+ }
+ } else {
+ ++iter;
+ }
+ }
+
+ /* Update start counter for next loop. */
+ this->next_start = count;
+ if (this->next_start >= this->packets.size()) this->next_start = 0;
+}
+
+/**
+ * Invalidates the next hop info of all cargo packets with a given next order or unload station.
+ * @param order Next order to invalidate.
+ * @param st_unload Unload station to invalidate.
+ */
+/* static */ void StationCargoList::InvalidateAllTo(OrderID order, StationID st_unload)
+{
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
+ for (StationCargoList::Iterator it = st->goods[cid].cargo.packets.begin(); it != st->goods[cid].cargo.packets.end(); ++it) {
+ CargoPacket *cp = *it;
+ if (cp->next_order == order || cp->next_station == st_unload) {
+ /* Invalidate both order and unload station as both likely
+ * don't make sense anymore. */
+ st->goods[cid].cargo.RemoveFromCache(cp);
+ cp->next_order = INVALID_ORDER;
+ cp->next_station = INVALID_STATION;
+ st->goods[cid].cargo.AddToCache(cp);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Invalidates the next hop info of all cargo packets for a given destination.
+ * @param order Next order to invalidate.
+ */
+/* static */ void StationCargoList::InvalidateAllTo(SourceType type, SourceID dest)
+{
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
+ for (StationCargoList::Iterator it = st->goods[cid].cargo.packets.begin(); it != st->goods[cid].cargo.packets.end(); ++it) {
+ CargoPacket *cp = *it;
+ if (cp->dest_id == dest && cp->dest_type == type) {
+ /* Invalidate both next order and unload station as we
+ * want the packets to be not routed anymore. */
+ st->goods[cid].cargo.RemoveFromCache(cp);
+ cp->next_order = INVALID_ORDER;
+ cp->next_station = INVALID_STATION;
+ st->goods[cid].cargo.AddToCache(cp);
+ }
+ }
+ }
+ }
+}
+
/*
* We have to instantiate everything we want to be usable.
*/
@@ -403,8 +671,8 @@ template class CargoList<VehicleCargoList>;
template class CargoList<StationCargoList>;
/** Autoreplace Vehicle -> Vehicle 'transfer'. */
-template bool CargoList<VehicleCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
+template bool CargoList<VehicleCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer);
/** Cargo unloading at a station. */
-template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
+template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer);
/** Cargo loading at a station. */
-template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
+template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer);