# 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     if (packet == NULL) {
    00362       packet = *it;
    00363       uint dropped = packet->count - cap;
    00364       this->count -= dropped;
    00365       this->cargo_days_in_transit -= dropped * packet->days_in_transit;
    00366       packet->count = cap;
    00367       it = this->packets.erase(it);
    00368     } else {
    00369       assert(packet->count == cap);
    00370       ++it;
    00371     }
    00372   } else {
    00373     it = this->packets.erase(it);
    00374   }
    00375   static_cast<Tinst *>(this)->RemoveFromCache(packet);
    00376   if (load_place != INVALID_TILE) {
    00377     packet->loaded_at_xy = load_place;
    00378   }
    00379   return packet;
    00380 }
    00381
    00385 template <class Tinst, class Tcont>
    00386 void CargoList<Tinst, Tcont>::InvalidateCache()
    00387 {
    00388   this->count = 0;
    00389   this->cargo_days_in_transit = 0;
    00390
    00391   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
    00392     static_cast<Tinst *>(this)->AddToCache(*it);
    00393   }
    00394 }
    00395
    00399 VehicleCargoList::~VehicleCargoList()
    00400 {
    00401   for (Iterator it(this->reserved.begin()); it != this->reserved.end(); ++it) {
    00402     delete *it;
    00403   }
    00404 }
    00405
    00414 uint VehicleCargoList::DeliverPacket(Iterator &it, uint cap, CargoPayment *payment)
    00415 {
    00416   CargoPacket *p = *it;
    00417   uint unloaded = 0;
    00418   if (p->count <= cap) {
    00419     payment->PayFinalDelivery(p, p->count);
    00420     it = this->packets.erase(it);
    00421     this->RemoveFromCache(p);
    00422     unloaded = p->count;
    00423     delete p;
    00424   } else {
    00425     payment->PayFinalDelivery(p, cap);
    00426     this->count -= cap;
    00427     this->cargo_days_in_transit -= cap * p->days_in_transit;
    00428     this->feeder_share -= p->feeder_share;
    00429     p->feeder_share = 0;
    00430     p->count -= cap;
    00431     unloaded = cap;
    00432     ++it;
    00433   }
    00434   return unloaded;
    00435 }
    00436
    00443 uint VehicleCargoList::KeepPacket(Iterator &it)
    00444 {
    00445   CargoPacket *cp = *it;
    00446   this->reserved.push_back(cp);
    00447   this->reserved_count += cp->count;
    00448   it = this->packets.erase(it);
    00449   return cp->count;
    00450 }
    00451
    00463 uint VehicleCargoList::TransferPacket(Iterator &it, uint cap, StationCargoList *dest, CargoPayment *payment, StationID next)
    00464 {
    00465   CargoPacket *cp = this->RemovePacket(it, cap);
    00466   cp->feeder_share += payment->PayTransfer(cp, cp->count);
    00467   uint ret = cp->count;
    00468   dest->Append(next, cp);
    00469   return ret;
    00470 }
    00471
    00481 UnloadType StationCargoList::WillUnloadOld(byte flags, StationID source)
    00482 {
    00483   /* Try to unload cargo. */
    00484   bool move = (flags & (UL_DELIVER | UL_ACCEPTED | UL_TRANSFER)) != 0;
    00485   /* Try to deliver cargo if unloading. */
    00486   bool deliver = (flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && (source != this->station->index);
    00487   /* Transfer cargo if delivery was unsuccessful. */
    00488   bool transfer = (flags & (UL_TRANSFER | UL_DELIVER)) != 0;
    00489   if (move) {
    00490     if (deliver) {
    00491       return UL_DELIVER;
    00492     } else if (transfer) {
    00493       return UL_TRANSFER;
    00494     } else {
    00495       /* This case is for (non-)delivery to the source station without special flags.
    00496        * Like the code in MoveTo did, we keep the packet in this case. */
    00497       return UL_KEEP;
    00498     }
    00499   } else {
    00500     return UL_KEEP;
    00501   }
    00502 }
    00503
    00517 UnloadType StationCargoList::WillUnloadCargoDist(byte flags, StationID next, StationID via, StationID source)
    00518 {
    00519   if (via == this->station->index) {
    00520     /* This is the final destination, deliver ... */
    00521     if (flags & UL_TRANSFER) {
    00522       /* .. except if explicitly told not to do so ... */
    00523       return UL_TRANSFER;
    00524     } else if (flags & UL_ACCEPTED) {
    00525       return UL_DELIVER;
    00526     } else if (flags & UL_DELIVER) {
    00527       /* .. or if the station suddenly doesn't accept our cargo, but we have an explicit deliver order... */
    00528       return UL_TRANSFER;
    00529     } else {
    00530       /* .. or else if it doesn't accept. */
    00531       return UL_KEEP;
    00532     }
    00533   } else {
    00534     /* Packet has to travel on, find out if it can stay on board. */
    00535     if (flags & UL_DELIVER) {
    00536       /* Order overrides cargodist:
    00537        * Play by the old loading rules here as player is interfering with cargodist.
    00538        * Try to deliver, as move has been forced upon us. */
    00539       if ((flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && source != this->station->index) {
    00540         return UL_DELIVER;
    00541       } else {
    00542         /* Transfer cargo, as delivering didn't work. */
    00543         return UL_TRANSFER;
    00544       }
    00545     } else if (flags & UL_TRANSFER) {
    00546       /* Transfer forced. */
    00547       return UL_TRANSFER;
    00548     } else if (next == via && next != INVALID_STATION) {
    00549       /* Vehicle goes to the packet's next hop or has nondeterministic order: keep the packet. */
    00550       return UL_KEEP;
    00551     } else {
    00552       /* Vehicle goes somewhere else, transfer the packet. */
    00553       return UL_TRANSFER;
    00554     }
    00555   }
    00556 }
    00557
    00564 void VehicleCargoList::SwapReserved()
    00565 {
    00566   assert(this->packets.empty());
    00567   this->packets.swap(this->reserved);
    00568   this->reserved_count = 0;
    00569 }
    00570
    00592 uint StationCargoList::TakeFrom(VehicleCargoList *source, uint max_unload, OrderUnloadFlags order_flags, StationID next, bool has_stopped, CargoPayment *payment)
    00593 {
    00594   uint remaining_unload = max_unload;
    00595   uint unloaded;
    00596   byte flags = this->GetUnloadFlags(order_flags);
    00597   GoodsEntry *dest = &this->station->goods[this->cargo];
    00598   UnloadType action;
    00599
    00600   for (VehicleCargoList::Iterator c = source->packets.begin(); c != source->packets.end() && remaining_unload > 0;) {
    00601     StationID cargo_source = (*c)->source;
    00602     FlowStatMap::const_iterator flows_it = dest->flows.find(cargo_source);
    00603     StationID via;
    00604     if (flows_it != dest->flows.end()) {
    00605       via = flows_it->second.GetVia();
    00606       /* Use cargodist unloading. */
    00607       action = this->WillUnloadCargoDist(flags, next, via, cargo_source);
    00608     } else {
    00609       via = INVALID_STATION;
    00610       /* There is no plan: use normal unloading. */
    00611       action = this->WillUnloadOld(flags, cargo_source);
    00612     }
    00613
    00614     switch (action) {
    00615       case UL_DELIVER:
    00616         unloaded = source->DeliverPacket(c, remaining_unload, payment);
    00617         remaining_unload -= unloaded;
    00618         break;
    00619       case UL_TRANSFER:
    00620         /* TransferPacket may split the packet and return the transferred part. */
    00621         if (via == this->station->index) via = flows_it->second.GetVia(via);
    00622         unloaded = source->TransferPacket(c, remaining_unload, this, payment, via);
    00623         remaining_unload -= unloaded;
    00624         break;
    00625       case UL_KEEP:
    00626         unloaded = source->KeepPacket(c);
    00627         break;
    00628       default:
    00629         NOT_REACHED();
    00630     }
    00631   }
    00632   return max_unload - remaining_unload;
    00633 }
    00634
    00638 void VehicleCargoList::OnCleanPool()
    00639 {
    00640   this->reserved.clear();
    00641   this->Parent::OnCleanPool();
    00642 }
    00643
    00649 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
    00650 {
    00651   this->feeder_share -= cp->feeder_share;
    00652   this->Parent::RemoveFromCache(cp);
    00653 }
    00654
    00660 void VehicleCargoList::AddToCache(const CargoPacket *cp)
    00661 {
    00662   this->feeder_share += cp->feeder_share;
    00663   this->Parent::AddToCache(cp);
    00664 }
    00665
    00672 uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
    00673 {
    00674   uint orig_cap = cap;
    00675   Iterator it = packets.begin();
    00676   while (it != packets.end() && cap > 0) {
    00677     cap -= MovePacket(dest, it, cap);
    00678   }
    00679   return orig_cap - cap;
    00680 }
    00681
    00685 void VehicleCargoList::AgeCargo()
    00686 {
    00687   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
    00688     CargoPacket *cp = *it;
    00689     /* If we're at the maximum, then we can't increase no more. */
    00690     if (cp->days_in_transit == 0xFF) continue;
    00691
    00692     cp->days_in_transit++;
    00693     this->cargo_days_in_transit += cp->count;
    00694   }
    00695 }
    00696
    00697 /*
    00698  *
    00699  * Station cargo list implementation
    00700  *
    00701  */
    00702
    00708 inline byte StationCargoList::GetUnloadFlags(OrderUnloadFlags order_flags)
    00709 {
    00710   byte flags = 0;
    00711   if (HasBit(this->station->goods[this->cargo].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) {
    00712     flags |= UL_ACCEPTED;
    00713   }
    00714   if (order_flags & OUFB_UNLOAD) {
    00715     flags |= UL_DELIVER;
    00716   }
    00717   if (order_flags & OUFB_TRANSFER) {
    00718     flags |= UL_TRANSFER;
    00719   }
    00720   return flags;
    00721 }
    00722
    00731 void StationCargoList::Append(StationID next, CargoPacket *cp)
    00732 {
    00733   assert(cp != NULL);
    00734   this->AddToCache(cp);
    00735
    00736   StationCargoPacketMap::List &list = this->packets[next];
    00737   for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
    00738     CargoPacket *icp = *it;
    00739     if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
    00740       icp->Merge(cp);
    00741       return;
    00742     }
    00743   }
    00744
    00745   /* The packet could not be merged with another one */
    00746   list.push_back(cp);
    00747 }
    00748
    00758 uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, bool reserve)
    00759 {
    00760   uint orig_cap = cap;
    00761   while (begin != end && cap > 0) {
    00762     cap -= this->MovePacket(dest, begin, cap, this->station->xy, reserve);
    00763   }
    00764   return orig_cap - cap;
    00765 }
    00766
    00775 uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID next, bool reserve)
    00776 {
    00777   uint orig_cap = cap;
    00778   std::pair<Iterator, Iterator> bounds(this->packets.equal_range(next));
    00779   cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
    00780   if (next != INVALID_STATION && cap > 0) {
    00781     bounds = this->packets.equal_range(INVALID_STATION);
    00782     cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
    00783   }
    00784   return orig_cap - cap;
    00785 }
    00786
    00791 void StationCargoList::RerouteStalePackets(StationID to)
    00792 {
    00793   std::pair<Iterator, Iterator> range(this->packets.equal_range(to));
    00794   for (Iterator it(range.first); it != range.second && it.GetKey() == to;) {
    00795     CargoPacket *packet = *it;
    00796     it = this->packets.erase(it);
    00797     StationID next = this->station->goods[this->cargo].GetVia(packet->source, this->station->index);
    00798     assert(next != to);
    00799
    00800     /* Legal, as insert doesn't invalidate iterators in the MultiMap, however
    00801      * this might insert the packet between range.first and range.second (which might be end())
    00802      * This is why we check for GetKey above to avoid infinite loops. */
    00803     this->packets.Insert(next, packet);
    00804   }
    00805 }
    00806
    00814 void StationCargoList::CountAndTruncate(uint max_remaining, StationCargoAmountMap &cargo_per_source)
    00815 {
    00816   uint prev_count = this->count;
    00817   uint loop = 0;
    00818   while (this->count > max_remaining) {
    00819     for (Iterator it(this->packets.begin()); it != this->packets.end();) {
    00820       CargoPacket *packet = *it;
    00821       if (loop == 0) cargo_per_source[packet->source] += packet->count;
    00822
    00823       if (RandomRange(prev_count) < max_remaining) {
    00824         ++it;
    00825         continue;
    00826       }
    00827
    00828       uint diff = this->count - max_remaining;
    00829       if (packet->count > diff) {
    00830         packet->count -= diff;
    00831         this->count = max_remaining;
    00832         this->cargo_days_in_transit -= packet->days_in_transit * diff;
    00833         if (loop > 0) {
    00834           return;
    00835         } else {
    00836           ++it;
    00837         }
    00838       } else {
    00839         it = this->packets.erase(it);
    00840         this->RemoveFromCache(packet);
    00841         delete packet;
    00842       }
    00843     }
    00844     loop++;
    00845   }
    00846 }
    00847
    00851 void VehicleCargoList::InvalidateCache()
    00852 {
    00853   this->feeder_share = 0;
    00854   this->reserved_count = 0;
    00855   this->Parent::InvalidateCache();
    00856   for (ConstIterator it(this->reserved.begin()); it != this->reserved.end(); it++) {
    00857     this->AddToCache(*it);
    00858     this->reserved_count += (*it)->count;
    00859   }
    00860 }
    00861
    00867 void StationCargoList::AssignTo(Station *station, CargoID cargo)
    00868 {
    00869   assert(this->station == NULL);
    00870   assert(station != NULL && cargo != INVALID_CARGO);
    00871   this->station = station;
    00872   this->cargo = cargo;
    00873 }
    00874
    00875
    00876 /*
    00877  * We have to instantiate everything we want to be usable.
    00878  */
    00879 template class CargoList<VehicleCargoList, CargoPacketList>;
    00880 template class CargoList<StationCargoList, StationCargoPacketMap>;