# cargopacket.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "station_base.h"
    00014 #include "core/pool_func.hpp"
    00015 #include "core/random_func.hpp"
    00016 #include "economy_base.h"
    00017
    00018 /* Initialize the cargopacket-pool */
    00019 CargoPacketPool _cargopacket_pool("CargoPacket");
    00020 INSTANTIATE_POOL_METHODS(CargoPacket)
    00021
    00022
    00025 CargoPacket::CargoPacket()
    00026 {
    00027   this->source_type = ST_INDUSTRY;
    00028   this->source_id   = INVALID_SOURCE;
    00029 }
    00030
    00042 CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) :
    00043   feeder_share(0),
    00044   count(count),
    00045   days_in_transit(0),
    00046   source_id(source_id),
    00047   source(source),
    00048   source_xy(source_xy),
    00049   loaded_at_xy(0)
    00050 {
    00051   assert(count != 0);
    00052   this->source_type  = source_type;
    00053 }
    00054
    00069 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) :
    00070     feeder_share(feeder_share),
    00071     count(count),
    00072     days_in_transit(days_in_transit),
    00073     source_id(source_id),
    00074     source(source),
    00075     source_xy(source_xy),
    00076     loaded_at_xy(loaded_at_xy)
    00077 {
    00078   assert(count != 0);
    00079   this->source_type = source_type;
    00080 }
    00081
    00087 inline CargoPacket *CargoPacket::Split(uint new_size)
    00088 {
    00089   if (!CargoPacket::CanAllocateItem()) return NULL;
    00090
    00091   Money fs = this->feeder_share * new_size / static_cast<uint>(this->count);
    00092   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);
    00093   this->feeder_share -= fs;
    00094   this->count -= new_size;
    00095   return cp_new;
    00096 }
    00097
    00102 inline void CargoPacket::Merge(CargoPacket *cp)
    00103 {
    00104   this->count += cp->count;
    00105   this->feeder_share += cp->feeder_share;
    00106   delete cp;
    00107 }
    00108
    00114 /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
    00115 {
    00116   CargoPacket *cp;
    00117   FOR_ALL_CARGOPACKETS(cp) {
    00118     if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
    00119   }
    00120 }
    00121
    00126 /* static */ void CargoPacket::InvalidateAllFrom(StationID sid)
    00127 {
    00128   CargoPacket *cp;
    00129   FOR_ALL_CARGOPACKETS(cp) {
    00130     if (cp->source == sid) cp->source = INVALID_STATION;
    00131   }
    00132 }
    00133
    00134 /*
    00135  *
    00136  * Cargo list implementation
    00137  *
    00138  */
    00139
    00143 template <class Tinst, class Tcont>
    00144 CargoList<Tinst, Tcont>::~CargoList()
    00145 {
    00146   for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
    00147     delete *it;
    00148   }
    00149 }
    00150
    00155 template <class Tinst, class Tcont>
    00156 void CargoList<Tinst, Tcont>::OnCleanPool()
    00157 {
    00158   this->packets.clear();
    00159 }
    00160
    00166 template <class Tinst, class Tcont>
    00167 void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp)
    00168 {
    00169   this->count                 -= cp->count;
    00170   this->cargo_days_in_transit -= cp->days_in_transit * cp->count;
    00171 }
    00172
    00178 template <class Tinst, class Tcont>
    00179 void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
    00180 {
    00181   this->count                 += cp->count;
    00182   this->cargo_days_in_transit += cp->days_in_transit * cp->count;
    00183 }
    00184
    00195 void VehicleCargoList::Append(CargoPacket *cp, bool update_cache)
    00196 {
    00197   assert(cp != NULL);
    00198   if (update_cache) this->AddToCache(cp);
    00199   for (CargoPacketList::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
    00200     CargoPacket *icp = *it;
    00201     if (VehicleCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
    00202       icp->Merge(cp);
    00203       return;
    00204     }
    00205   }
    00206
    00207   /* The packet could not be merged with another one */
    00208   this->packets.push_back(cp);
    00209 }
    00210
    00216 template <class Tinst, class Tcont>
    00217 void CargoList<Tinst, Tcont>::Truncate(uint max_remaining)
    00218 {
    00219   for (Iterator it(packets.begin()); it != packets.end(); /* done during loop*/) {
    00220     CargoPacket *cp = *it;
    00221     if (max_remaining == 0) {
    00222       /* Nothing should remain, just remove the packets. */
    00223       it = this->packets.erase(it);
    00224       static_cast<Tinst *>(this)->RemoveFromCache(cp);
    00225       delete cp;
    00226       continue;
    00227     }
    00228
    00229     uint local_count = cp->count;
    00230     if (local_count > max_remaining) {
    00231       uint diff = local_count - max_remaining;
    00232       this->count -= diff;
    00233       this->cargo_days_in_transit -= cp->days_in_transit * diff;
    00234       cp->count = max_remaining;
    00235       max_remaining = 0;
    00236     } else {
    00237       max_remaining -= local_count;
    00238     }
    00239     ++it;
    00240   }
    00241 }
    00242
    00247 void VehicleCargoList::Reserve(CargoPacket *cp)
    00248 {
    00249   assert(cp != NULL);
    00250   this->AddToCache(cp);
    00251   this->reserved_count += cp->count;
    00252   this->reserved.push_back(cp);
    00253 }
    00254
    00260 void VehicleCargoList::Unreserve(StationID next, StationCargoList *dest)
    00261 {
    00262   Iterator it(this->reserved.begin());
    00263   while (it != this->reserved.end()) {
    00264     CargoPacket *cp = *it;
    00265     this->RemoveFromCache(cp);
    00266     this->reserved_count -= cp->count;
    00267     dest->Append(next, cp);
    00268     it = this->reserved.erase(it);
    00269   }
    00270 }
    00271
    00277 uint VehicleCargoList::LoadReserved(uint max_move)
    00278 {
    00279   uint orig_max = max_move;
    00280   Iterator it(this->reserved.begin());
    00281   while (it != this->reserved.end() && max_move > 0) {
    00282     CargoPacket *cp = *it;
    00283     if (cp->count <= max_move) {
    00284       /* Can move the complete packet */
    00285       max_move -= cp->count;
    00286       it = this->reserved.erase(it);
    00287       this->reserved_count -= cp->count;
    00288       this->Append(cp, false);
    00289     } else if (CargoPacket::CanAllocateItem()) {
    00290       cp->count -= max_move;
    00291       CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, cp->loaded_at_xy, 0, cp->source_type, cp->source_id);
    00292       this->Append(cp_new, false);
    00293       this->reserved_count -= max_move;
    00294       max_move = 0;
    00295     }
    00296   }
    00297   return orig_max - max_move;
    00298 }
    00299
    00310 template<class Tinst, class Tcont>
    00311 uint CargoList<Tinst, Tcont>::MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place, bool reserve)
    00312 {
    00313   CargoPacket *packet = this->RemovePacket(it, cap, load_place);
    00314   uint ret = packet->count;
    00315   if (reserve) {
    00316     dest->Reserve(packet);
    00317   } else {
    00318     dest->Append(packet);
    00319   }
    00320   return ret;
    00321 }
    00322
    00332 template<class Tinst, class Tcont>
    00333 uint CargoList<Tinst, Tcont>::MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap)
    00334 {
    00335   CargoPacket *packet = this->RemovePacket(it, cap);
    00336   uint ret = packet->count;
    00337   dest->Append(next, packet);
    00338   return ret;
    00339 }
    00340
    00350 template<class Tinst, class Tcont>
    00351 CargoPacket *CargoList<Tinst, Tcont>::RemovePacket(Iterator &it, uint cap, TileIndex load_place)
    00352 {
    00353   CargoPacket *packet = *it;
    00354   /* load the packet if possible */
    00355   if (packet->count > cap) {
    00356     /* packet needs to be split */
    00357     packet = packet->Split(cap);
    00358
    00359     /* We could not allocate a CargoPacket? Is the map that full?
    00360      * Just remove the whole packet and drop some cargo then.
    00361      */
    00362     if (packet == NULL) {
    00363       packet = *it;
    00364       uint dropped = packet->count - cap;
    00365       this->count -= dropped;
    00366       this->cargo_days_in_transit -= dropped * packet->days_in_transit;
    00367       packet->count = cap;
    00368       it = this->packets.erase(it);
    00369     } else {
    00370       assert(packet->count == cap);
    00371       ++it;
    00372     }
    00373   } else {
    00374     it = this->packets.erase(it);
    00375   }
    00376   static_cast<Tinst *>(this)->RemoveFromCache(packet);
    00377   if (load_place != INVALID_TILE) {
    00378     packet->loaded_at_xy = load_place;
    00379   }
    00380   return packet;
    00381 }
    00382
    00386 template <class Tinst, class Tcont>
    00387 void CargoList<Tinst, Tcont>::InvalidateCache()
    00388 {
    00389   this->count = 0;
    00390   this->cargo_days_in_transit = 0;
    00391
    00392   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
    00393     static_cast<Tinst *>(this)->AddToCache(*it);
    00394   }
    00395 }
    00396
    00400 VehicleCargoList::~VehicleCargoList()
    00401 {
    00402   for (Iterator it(this->reserved.begin()); it != this->reserved.end(); ++it) {
    00403     delete *it;
    00404   }
    00405 }
    00406
    00415 uint VehicleCargoList::DeliverPacket(Iterator &it, uint cap, CargoPayment *payment)
    00416 {
    00417   CargoPacket *p = *it;
    00418   uint unloaded = 0;
    00419   if (p->count <= cap) {
    00420     payment->PayFinalDelivery(p, p->count);
    00421     it = this->packets.erase(it);
    00422     this->RemoveFromCache(p);
    00423     unloaded = p->count;
    00424     delete p;
    00425   } else {
    00426     payment->PayFinalDelivery(p, cap);
    00427     this->count -= cap;
    00428     this->cargo_days_in_transit -= cap * p->days_in_transit;
    00429     this->feeder_share -= p->feeder_share;
    00430     p->feeder_share = 0;
    00431     p->count -= cap;
    00432     unloaded = cap;
    00433     ++it;
    00434   }
    00435   return unloaded;
    00436 }
    00437
    00444 uint VehicleCargoList::KeepPacket(Iterator &it)
    00445 {
    00446   CargoPacket *cp = *it;
    00447   this->reserved.push_back(cp);
    00448   this->reserved_count += cp->count;
    00449   it = this->packets.erase(it);
    00450   return cp->count;
    00451 }
    00452
    00464 uint VehicleCargoList::TransferPacket(Iterator &it, uint cap, StationCargoList *dest, CargoPayment *payment, StationID next)
    00465 {
    00466   CargoPacket *cp = this->RemovePacket(it, cap);
    00467   cp->feeder_share += payment->PayTransfer(cp, cp->count);
    00468   uint ret = cp->count;
    00469   dest->Append(next, cp);
    00470   return ret;
    00471 }
    00472
    00482 UnloadType StationCargoList::WillUnloadOld(byte flags, StationID source)
    00483 {
    00484   /* try to unload cargo */
    00485   bool move = (flags & (UL_DELIVER | UL_ACCEPTED | UL_TRANSFER)) != 0;
    00486   /* try to deliver cargo if unloading */
    00487   bool deliver = (flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && (source != this->station->index);
    00488   /* transfer cargo if delivery was unsuccessful */
    00489   bool transfer = (flags & (UL_TRANSFER | UL_DELIVER)) != 0;
    00490   if (move) {
    00491     if(deliver) {
    00492       return UL_DELIVER;
    00493     } else if (transfer) {
    00494       return UL_TRANSFER;
    00495     } else {
    00496       /* this case is for (non-)delivery to the source station without special flags.
    00497        * like the code in MoveTo did, we keep the packet in this case
    00498        */
    00499       return UL_KEEP;
    00500     }
    00501   } else {
    00502     return UL_KEEP;
    00503   }
    00504 }
    00505
    00519 UnloadType StationCargoList::WillUnloadCargoDist(byte flags, StationID next, StationID via, StationID source)
    00520 {
    00521   if (via == this->station->index) {
    00522     /* this is the final destination, deliver ... */
    00523     if (flags & UL_TRANSFER) {
    00524       /* .. except if explicitly told not to do so ... */
    00525       return UL_TRANSFER;
    00526     } else if (flags & UL_ACCEPTED) {
    00527       return UL_DELIVER;
    00528     } else if (flags & UL_DELIVER) {
    00529       /* .. or if the station suddenly doesn't accept our cargo, but we have an explicit deliver order... */
    00530       return UL_TRANSFER;
    00531     } else {
    00532       /* .. or else if it doesn't accept. */
    00533       return UL_KEEP;
    00534     }
    00535   } else {
    00536     /* packet has to travel on, find out if it can stay on board */
    00537     if (flags & UL_DELIVER) {
    00538       /* order overrides cargodist:
    00539        * play by the old loading rules here as player is interfering with cargodist
    00540        * try to deliver, as move has been forced upon us */
    00541       if ((flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && source != this->station->index) {
    00542         return UL_DELIVER;
    00543       } else {
    00544         /* transfer cargo, as delivering didn't work */
    00545         return UL_TRANSFER;
    00546       }
    00547     } else if (flags & UL_TRANSFER) {
    00548       /* transfer forced */
    00549       return UL_TRANSFER;
    00550     } else if (next == via && next != INVALID_STATION) {
    00551       /* vehicle goes to the packet's next hop or has nondeterministic order: keep the packet*/
    00552       return UL_KEEP;
    00553     } else {
    00554       /* vehicle goes somewhere else, transfer the packet*/
    00555       return UL_TRANSFER;
    00556     }
    00557   }
    00558 }
    00559
    00566 void VehicleCargoList::SwapReserved()
    00567 {
    00568   assert(this->packets.empty());
    00569   this->packets.swap(this->reserved);
    00570   this->reserved_count = 0;
    00571 }
    00572
    00594 uint StationCargoList::TakeFrom(VehicleCargoList *source, uint max_unload, OrderUnloadFlags order_flags, StationID next, bool has_stopped, CargoPayment *payment)
    00595 {
    00596   uint remaining_unload = max_unload;
    00597   uint unloaded;
    00598   byte flags = this->GetUnloadFlags(order_flags);
    00599   GoodsEntry *dest = &this->station->goods[this->cargo];
    00600   UnloadType action;
    00601
    00602   for (VehicleCargoList::Iterator c = source->packets.begin(); c != source->packets.end() && remaining_unload > 0;) {
    00603     StationID cargo_source = (*c)->source;
    00604     FlowStatMap::const_iterator flows_it = dest->flows.find(cargo_source);
    00605     StationID via;
    00606     if (flows_it != dest->flows.end()) {
    00607       via = flows_it->second.GetVia();
    00608       /* use cargodist unloading*/
    00609       action = this->WillUnloadCargoDist(flags, next, via, cargo_source);
    00610     } else {
    00611       via = INVALID_STATION;
    00612       /* there is no plan: use normal unloading */
    00613       action = this->WillUnloadOld(flags, cargo_source);
    00614     }
    00615
    00616     switch (action) {
    00617       case UL_DELIVER:
    00618         unloaded = source->DeliverPacket(c, remaining_unload, payment);
    00619         remaining_unload -= unloaded;
    00620         break;
    00621       case UL_TRANSFER:
    00622         /* TransferPacket may split the packet and return the transferred part */
    00623         if (via == this->station->index) via = flows_it->second.GetVia(via);
    00624         unloaded = source->TransferPacket(c, remaining_unload, this, payment, via);
    00625         remaining_unload -= unloaded;
    00626         break;
    00627       case UL_KEEP:
    00628         unloaded = source->KeepPacket(c);
    00629         break;
    00630       default:
    00631         NOT_REACHED();
    00632     }
    00633   }
    00634   return max_unload - remaining_unload;
    00635 }
    00636
    00640 void VehicleCargoList::OnCleanPool()
    00641 {
    00642   this->reserved.clear();
    00643   this->Parent::OnCleanPool();
    00644 }
    00645
    00651 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
    00652 {
    00653   this->feeder_share -= cp->feeder_share;
    00654   this->Parent::RemoveFromCache(cp);
    00655 }
    00656
    00662 void VehicleCargoList::AddToCache(const CargoPacket *cp)
    00663 {
    00664   this->feeder_share += cp->feeder_share;
    00665   this->Parent::AddToCache(cp);
    00666 }
    00667
    00674 uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
    00675 {
    00676   uint orig_cap = cap;
    00677   Iterator it = packets.begin();
    00678   while (it != packets.end() && cap > 0) {
    00679     cap -= MovePacket(dest, it, cap);
    00680   }
    00681   return orig_cap - cap;
    00682 }
    00683
    00687 void VehicleCargoList::AgeCargo()
    00688 {
    00689   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
    00690     CargoPacket *cp = *it;
    00691     /* If we're at the maximum, then we can't increase no more. */
    00692     if (cp->days_in_transit == 0xFF) continue;
    00693
    00694     cp->days_in_transit++;
    00695     this->cargo_days_in_transit += cp->count;
    00696   }
    00697 }
    00698
    00699 /*
    00700  *
    00701  * Station cargo list implementation
    00702  *
    00703  */
    00704
    00710 inline byte StationCargoList::GetUnloadFlags(OrderUnloadFlags order_flags)
    00711 {
    00712   byte flags = 0;
    00713   if (HasBit(this->station->goods[this->cargo].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) {
    00714     flags |= UL_ACCEPTED;
    00715   }
    00716   if (order_flags & OUFB_UNLOAD) {
    00717     flags |= UL_DELIVER;
    00718   }
    00719   if (order_flags & OUFB_TRANSFER) {
    00720     flags |= UL_TRANSFER;
    00721   }
    00722   return flags;
    00723 }
    00724
    00733 void StationCargoList::Append(StationID next, CargoPacket *cp)
    00734 {
    00735   assert(cp != NULL);
    00736   this->AddToCache(cp);
    00737
    00738   StationCargoPacketMap::List &list = this->packets[next];
    00739   for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
    00740     CargoPacket *icp = *it;
    00741     if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
    00742       icp->Merge(cp);
    00743       return;
    00744     }
    00745   }
    00746
    00747   /* The packet could not be merged with another one */
    00748   list.push_back(cp);
    00749 }
    00750
    00760 uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, bool reserve)
    00761 {
    00762   uint orig_cap = cap;
    00763   while (begin != end && cap > 0) {
    00764     cap -= this->MovePacket(dest, begin, cap, this->station->xy, reserve);
    00765   }
    00766   return orig_cap - cap;
    00767 }
    00768
    00777 uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID next, bool reserve)
    00778 {
    00779   uint orig_cap = cap;
    00780   std::pair<Iterator, Iterator> bounds(this->packets.equal_range(next));
    00781   cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
    00782   if (next != INVALID_STATION && cap > 0) {
    00783     bounds = this->packets.equal_range(INVALID_STATION);
    00784     cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
    00785   }
    00786   return orig_cap - cap;
    00787 }
    00788
    00793 void StationCargoList::RerouteStalePackets(StationID to)
    00794 {
    00795   std::pair<Iterator, Iterator> range(this->packets.equal_range(to));
    00796   for (Iterator it(range.first); it != range.second && it.GetKey() == to;) {
    00797     CargoPacket *packet = *it;
    00798     it = this->packets.erase(it);
    00799     StationID next = this->station->goods[this->cargo].GetVia(packet->source, this->station->index);
    00800     assert(next != to);
    00801
    00802     /* legal, as insert doesn't invalidate iterators in the MultiMap, however
    00803      * this might insert the packet between range.first and range.second (which might be end())
    00804      * This is why we check for GetKey above to avoid infinite loops
    00805      */
    00806     this->packets.Insert(next, packet);
    00807   }
    00808 }
    00809
    00817 void StationCargoList::CountAndTruncate(uint max_remaining, StationCargoAmountMap &cargo_per_source)
    00818 {
    00819   uint prev_count = this->count;
    00820   uint loop = 0;
    00821   while (this->count > max_remaining) {
    00822     for (Iterator it(this->packets.begin()); it != this->packets.end();) {
    00823       CargoPacket *packet = *it;
    00824       if (loop == 0) cargo_per_source[packet->source] += packet->count;
    00825
    00826       if (RandomRange(prev_count) < max_remaining) {
    00827         ++it;
    00828         continue;
    00829       }
    00830
    00831       uint diff = this->count - max_remaining;
    00832       if (packet->count > diff) {
    00833         packet->count -= diff;
    00834         this->count = max_remaining;
    00835         this->cargo_days_in_transit -= packet->days_in_transit * diff;
    00836         if (loop > 0) {
    00837           return;
    00838         } else {
    00839           ++it;
    00840         }
    00841       } else {
    00842         it = this->packets.erase(it);
    00843         this->RemoveFromCache(packet);
    00844         delete packet;
    00845       }
    00846     }
    00847     loop++;
    00848   }
    00849 }
    00850
    00854 void VehicleCargoList::InvalidateCache()
    00855 {
    00856   this->feeder_share = 0;
    00857   this->reserved_count = 0;
    00858   this->Parent::InvalidateCache();
    00859   for (ConstIterator it(this->reserved.begin()); it != this->reserved.end(); it++) {
    00860     this->AddToCache(*it);
    00861     this->reserved_count += (*it)->count;
    00862   }
    00863 }
    00864
    00870 void StationCargoList::AssignTo(Station *station, CargoID cargo)
    00871 {
    00872   assert(this->station == NULL);
    00873   assert(station != NULL && cargo != INVALID_CARGO);
    00874   this->station = station;
    00875   this->cargo = cargo;
    00876 }
    00877
    00878
    00879 /*
    00880  * We have to instantiate everything we want to be usable.
    00881  */
    00882 template class CargoList<VehicleCargoList, CargoPacketList>;
    00883 template class CargoList<StationCargoList, StationCargoPacketMap>;