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 FORCEINLINE 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 FORCEINLINE 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 
00199   if (update_cache) this->AddToCache(cp);
00200   for (CargoPacketList::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
00201     CargoPacket *icp = *it;
00202     if (VehicleCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
00203       icp->Merge(cp);
00204       return;
00205     }
00206   }
00207 
00208   /* The packet could not be merged with another one. */
00209   this->packets.push_back(cp);
00210 }
00211 
00217 template <class Tinst, class Tcont>
00218 void CargoList<Tinst, Tcont>::Truncate(uint max_remaining)
00219 {
00220   for (Iterator it(packets.begin()); it != packets.end(); /* Done during loop. */) {
00221     CargoPacket *cp = *it;
00222     if (max_remaining == 0) {
00223       /* Nothing should remain, just remove the packets. */
00224       this->packets.erase(it++);
00225       static_cast<Tinst *>(this)->RemoveFromCache(cp);
00226       delete cp;
00227       continue;
00228     }
00229 
00230     uint local_count = cp->count;
00231     if (local_count > max_remaining) {
00232       uint diff = local_count - max_remaining;
00233       this->count -= diff;
00234       this->cargo_days_in_transit -= cp->days_in_transit * diff;
00235       cp->count = max_remaining;
00236       max_remaining = 0;
00237     } else {
00238       max_remaining -= local_count;
00239     }
00240     ++it;
00241   }
00242 }
00243 
00248 void VehicleCargoList::Reserve(CargoPacket *cp)
00249 {
00250   assert(cp != NULL);
00251   this->AddToCache(cp);
00252   this->reserved_count += cp->count;
00253   this->reserved.push_back(cp);
00254 }
00255 
00261 void VehicleCargoList::Unreserve(StationID next, StationCargoList *dest)
00262 {
00263   Iterator it(this->reserved.begin());
00264   while (it != this->reserved.end()) {
00265     CargoPacket *cp = *it;
00266     this->RemoveFromCache(cp);
00267     this->reserved_count -= cp->count;
00268     dest->Append(next, cp);
00269     this->reserved.erase(it++);
00270   }
00271 }
00272 
00278 uint VehicleCargoList::LoadReserved(uint max_move)
00279 {
00280   uint orig_max = max_move;
00281   Iterator it(this->reserved.begin());
00282   while (it != this->reserved.end() && max_move > 0) {
00283     CargoPacket *cp = *it;
00284     if (cp->count <= max_move) {
00285       /* Can move the complete packet. */
00286       max_move -= cp->count;
00287       this->reserved.erase(it++);
00288       this->reserved_count -= cp->count;
00289       this->Append(cp, false);
00290     } else if (CargoPacket::CanAllocateItem()) {
00291       cp->count -= max_move;
00292       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);
00293       this->Append(cp_new, false);
00294       this->reserved_count -= max_move;
00295       max_move = 0;
00296     }
00297   }
00298   return orig_max - max_move;
00299 }
00300 
00311 template<class Tinst, class Tcont>
00312 uint CargoList<Tinst, Tcont>::MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place, bool reserve)
00313 {
00314   CargoPacket *packet = this->RemovePacket(it, cap, load_place);
00315   uint ret = packet->count;
00316   if (reserve) {
00317     dest->Reserve(packet);
00318   } else {
00319     dest->Append(packet);
00320   }
00321   return ret;
00322 }
00323 
00333 template<class Tinst, class Tcont>
00334 uint CargoList<Tinst, Tcont>::MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap)
00335 {
00336   CargoPacket *packet = this->RemovePacket(it, cap);
00337   uint ret = packet->count;
00338   dest->Append(next, packet);
00339   return ret;
00340 }
00341 
00351 template<class Tinst, class Tcont>
00352 CargoPacket *CargoList<Tinst, Tcont>::RemovePacket(Iterator &it, uint cap, TileIndex load_place)
00353 {
00354   CargoPacket *packet = *it;
00355   /* Load the packet if possible. */
00356   if (packet->count > cap) {
00357     /* Packet needs to be split. */
00358     packet = packet->Split(cap);
00359 
00360     /* We could not allocate a CargoPacket? Is the map that full?
00361      * Just remove the whole packet and drop some cargo then.
00362      */
00363     if (packet == NULL) {
00364       packet = *it;
00365       uint dropped = packet->count - cap;
00366       this->count -= dropped;
00367       this->cargo_days_in_transit -= dropped * packet->days_in_transit; 
00368       packet->count = cap;
00369       this->packets.erase(it++);
00370     } else {
00371       assert(packet->count == cap);
00372       ++it;
00373     }
00374   } else {
00375     this->packets.erase(it++);
00376   }
00377   static_cast<Tinst *>(this)->RemoveFromCache(packet);
00378   if (load_place != INVALID_TILE) {
00379     packet->loaded_at_xy = load_place;
00380   }
00381   return packet;
00382 }
00383 
00387 template <class Tinst, class Tcont>
00388 void CargoList<Tinst, Tcont>::InvalidateCache()
00389 {
00390   this->count = 0;
00391   this->cargo_days_in_transit = 0;
00392 
00393   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00394     static_cast<Tinst *>(this)->AddToCache(*it);
00395   }
00396 }
00397 
00401 VehicleCargoList::~VehicleCargoList()
00402 {
00403   for (Iterator it(this->reserved.begin()); it != this->reserved.end(); ++it) {
00404     delete *it;
00405   }
00406 }
00407 
00416 uint VehicleCargoList::DeliverPacket(Iterator &it, uint cap, CargoPayment *payment)
00417 {
00418   CargoPacket *p = *it;
00419   uint unloaded = 0;
00420   if (p->count <= cap) {
00421     payment->PayFinalDelivery(p, p->count);
00422     this->packets.erase(it++);
00423     this->RemoveFromCache(p);
00424     unloaded = p->count;
00425     delete p;
00426   } else {
00427     payment->PayFinalDelivery(p, cap);
00428     this->count -= cap;
00429     this->cargo_days_in_transit -= cap * p->days_in_transit;
00430     this->feeder_share -= p->feeder_share;
00431     p->feeder_share = 0;
00432     p->count -= cap;
00433     unloaded = cap;
00434     ++it;
00435   }
00436   return unloaded;
00437 }
00438 
00445 uint VehicleCargoList::KeepPacket(Iterator &it)
00446 {
00447   CargoPacket *cp = *it;
00448   this->reserved.push_back(cp);
00449   this->reserved_count += cp->count;
00450   this->packets.erase(it++);
00451   return cp->count;
00452 }
00453 
00465 uint VehicleCargoList::TransferPacket(Iterator &it, uint cap, StationCargoList *dest, CargoPayment *payment, StationID next)
00466 {
00467   CargoPacket *cp = this->RemovePacket(it, cap);
00468   cp->feeder_share += payment->PayTransfer(cp, cp->count);
00469   uint ret = cp->count;
00470   dest->Append(next, cp);
00471   return ret;
00472 }
00473 
00483 UnloadType StationCargoList::WillUnloadOld(byte flags, StationID source)
00484 {
00485   /* Try to unload cargo. */
00486   bool move = (flags & (UL_DELIVER | UL_ACCEPTED | UL_TRANSFER)) != 0;
00487   /* Try to deliver cargo if unloading. */
00488   bool deliver = (flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && (source != this->station->index);
00489   /* Transfer cargo if delivery was unsuccessful. */
00490   bool transfer = (flags & (UL_TRANSFER | UL_DELIVER)) != 0;
00491   if (move) {
00492     if(deliver) {
00493       return UL_DELIVER;
00494     } else if (transfer) {
00495       return UL_TRANSFER;
00496     } else {
00497       /* This case is for (non-)delivery to the source station without special flags.
00498        * Like the code in MoveTo did, we keep the packet in this case.
00499        */
00500       return UL_KEEP;
00501     }
00502   } else {
00503     return UL_KEEP;
00504   }
00505 }
00506 
00520 UnloadType StationCargoList::WillUnloadCargoDist(byte flags, StationID next, StationID via, StationID source)
00521 {
00522   if (via == this->station->index) {
00523     /* This is the final destination, deliver ... */
00524     if (flags & UL_TRANSFER) {
00525       /* .. except if explicitly told not to do so ... */
00526       return UL_TRANSFER;
00527     } else if (flags & UL_ACCEPTED) {
00528       return UL_DELIVER;
00529     } else if (flags & UL_DELIVER) {
00530       /* .. or if the station suddenly doesn't accept our cargo, but we have an explicit deliver order... */
00531       return UL_TRANSFER;
00532     } else {
00533       /* .. or else if it doesn't accept. */
00534       return UL_KEEP;
00535     }
00536   } else {
00537     /* Packet has to travel on, find out if it can stay on board. */
00538     if (flags & UL_DELIVER) {
00539       /* Order overrides cargodist:
00540        * play by the old loading rules here as player is interfering with cargodist.
00541        * Try to deliver, as move has been forced upon us. */
00542       if ((flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && source != this->station->index) {
00543         return UL_DELIVER;
00544       } else {
00545         /* Transfer cargo, as delivering didn't work. */
00546         return UL_TRANSFER;
00547       }
00548     } else if (flags & UL_TRANSFER) {
00549       /* Transfer forced. */
00550       return UL_TRANSFER;
00551     } else if (next == via && next != INVALID_STATION) {
00552       /* Vehicle goes to the packet's next hop or has nondeterministic order: keep the packet. */
00553       return UL_KEEP;
00554     } else {
00555       /* Vehicle goes somewhere else, transfer the packet. */
00556       return UL_TRANSFER;
00557     }
00558   }
00559 }
00560 
00567 void VehicleCargoList::SwapReserved()
00568 {
00569   assert(this->packets.empty());
00570   this->packets.swap(this->reserved);
00571   this->reserved_count = 0;
00572 }
00573 
00595 uint StationCargoList::TakeFrom(VehicleCargoList *source, uint max_unload, OrderUnloadFlags order_flags, StationID next, bool has_stopped, CargoPayment *payment)
00596 {
00597   uint remaining_unload = max_unload;
00598   uint unloaded;
00599   byte flags = this->GetUnloadFlags(order_flags);
00600   GoodsEntry *dest = &this->station->goods[this->cargo];
00601   UnloadType action;
00602 
00603   for (VehicleCargoList::Iterator c = source->packets.begin(); c != source->packets.end() && remaining_unload > 0;) {
00604     StationID cargo_source = (*c)->source;
00605     FlowStatSet &flows = dest->flows[cargo_source];
00606     FlowStatSet::iterator begin = flows.begin();
00607     StationID via = (begin != flows.end() ? begin->Via() : INVALID_STATION);
00608     if (via != INVALID_STATION) {
00609       /* Use cargodist unloading. */
00610       action = this->WillUnloadCargoDist(flags, next, via, cargo_source);
00611     } else {
00612       /* There is no plan: use normal unloading. */
00613       action = WillUnloadOld(flags, cargo_source);
00614     }
00615 
00616     switch(action) {
00617       case UL_DELIVER:
00618         unloaded = source->DeliverPacket(c, remaining_unload, payment);
00619         if (via != INVALID_STATION) {
00620           if (via == this->station->index) {
00621             dest->UpdateFlowStats(flows, begin, unloaded);
00622           } else {
00623             dest->UpdateFlowStats(flows, unloaded, this->station->index);
00624           }
00625         }
00626         remaining_unload -= unloaded;
00627         break;
00628       case UL_TRANSFER:
00629         /* TransferPacket may split the packet and return the transferred part. */
00630         if (via == this->station->index) {
00631           via = (++begin != flows.end()) ? begin->Via() : INVALID_STATION;
00632         }
00633         unloaded = source->TransferPacket(c, remaining_unload, this, payment, via);
00634         if (via != INVALID_STATION) {
00635           dest->UpdateFlowStats(flows, begin, unloaded);
00636         }
00637         remaining_unload -= unloaded;
00638         break;
00639       case UL_KEEP:
00640         unloaded = source->KeepPacket(c);
00641         if (via != INVALID_STATION && next != INVALID_STATION && !has_stopped) {
00642           if (via == next) {
00643             dest->UpdateFlowStats(flows, begin, unloaded);
00644           } else {
00645             dest->UpdateFlowStats(flows, unloaded, next);
00646           }
00647         }
00648         break;
00649       default:
00650         NOT_REACHED();
00651     }
00652   }
00653   return max_unload - remaining_unload;
00654 }
00655 
00659 void VehicleCargoList::OnCleanPool()
00660 {
00661   this->reserved.clear();
00662   this->Parent::OnCleanPool();
00663 }
00664 
00670 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
00671 {
00672   this->feeder_share -= cp->feeder_share;
00673   this->Parent::RemoveFromCache(cp);
00674 }
00675 
00681 void VehicleCargoList::AddToCache(const CargoPacket *cp)
00682 {
00683   this->feeder_share += cp->feeder_share;
00684   this->Parent::AddToCache(cp);
00685 }
00686 
00693 uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
00694 {
00695   uint orig_cap = cap;
00696   Iterator it = packets.begin();
00697   while (it != packets.end() && cap > 0) {
00698     cap -= MovePacket(dest, it, cap);
00699   }
00700   return orig_cap - cap;
00701 }
00702 
00706 void VehicleCargoList::AgeCargo()
00707 {
00708   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00709     CargoPacket *cp = *it;
00710     /* If we're at the maximum, then we can't increase no more. */
00711     if (cp->days_in_transit == 0xFF) continue;
00712 
00713     cp->days_in_transit++;
00714     this->cargo_days_in_transit += cp->count;
00715   }
00716 }
00717 
00718 /*
00719  *
00720  * Station cargo list implementation.
00721  *
00722  */
00723 
00729 FORCEINLINE byte StationCargoList::GetUnloadFlags(OrderUnloadFlags order_flags)
00730 {
00731   byte flags = 0;
00732   if (HasBit(this->station->goods[this->cargo].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) {
00733     flags |= UL_ACCEPTED;
00734   }
00735   if (order_flags & OUFB_UNLOAD) {
00736     flags |= UL_DELIVER;
00737   }
00738   if (order_flags & OUFB_TRANSFER) {
00739     flags |= UL_TRANSFER;
00740   }
00741   return flags;
00742 }
00743 
00752 void StationCargoList::Append(StationID next, CargoPacket *cp)
00753 {
00754   assert(cp != NULL);
00755   this->AddToCache(cp);
00756 
00757   StationCargoPacketMap::List &list = this->packets[next];
00758 
00759   for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
00760     CargoPacket *icp = *it;
00761     if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
00762       icp->Merge(cp);
00763       return;
00764     }
00765   }
00766 
00767   /* The packet could not be merged with another one. */
00768   list.push_back(cp);
00769 }
00770 
00780 uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, bool reserve)
00781 {
00782   uint orig_cap = cap;
00783   while (begin != end && cap > 0) {
00784     cap -= this->MovePacket(dest, begin, cap, this->station->xy, reserve);
00785   }
00786   return orig_cap - cap;
00787 }
00788 
00797 uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID next, bool reserve)
00798 {
00799   uint orig_cap = cap;
00800   std::pair<Iterator, Iterator> bounds(this->packets.equal_range(next));
00801   cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
00802   if (next != INVALID_STATION && cap > 0) {
00803     bounds = this->packets.equal_range(INVALID_STATION);
00804     cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
00805   }
00806   return orig_cap - cap;
00807 }
00808 
00813 void StationCargoList::RerouteStalePackets(StationID to)
00814 {
00815   std::pair<Iterator, Iterator> range(this->packets.equal_range(to));
00816   for (Iterator it(range.first); it != range.second && it.GetKey() == to;) {
00817     CargoPacket *packet = *it;
00818     this->packets.erase(it++);
00819     StationID next = this->station->goods[this->cargo].UpdateFlowStatsTransfer(packet->source, packet->count, this->station->index);
00820     assert(next != to);
00821 
00822     /* Legal, as insert doesn't invalidate iterators in the MultiMap, however
00823      * this might insert the packet between range.first and range.second (which might be end())
00824      * This is why we check for GetKey above to avoid infinite loops.
00825      */
00826     this->packets.Insert(next, packet);
00827   }
00828 }
00829 
00837 void StationCargoList::CountAndTruncate(uint max_remaining, StationCargoAmountMap &cargo_per_source)
00838 {
00839   uint prev_count = this->count;
00840   uint loop = 0;
00841   while (this->count > max_remaining) {
00842     for (Iterator it(this->packets.begin()); it != this->packets.end();) {
00843       CargoPacket *packet = *it;
00844       if (loop == 0) cargo_per_source[packet->source] += packet->count;
00845 
00846       if (RandomRange(prev_count) < max_remaining) {
00847         ++it;
00848         continue;
00849       }
00850 
00851       uint diff = this->count - max_remaining;
00852       if (packet->count > diff) {
00853         packet->count -= diff;
00854         this->count = max_remaining;
00855         this->cargo_days_in_transit -= packet->days_in_transit * diff;
00856         if (loop > 0) {
00857           return;
00858         } else {
00859           ++it;
00860         }
00861       } else {
00862         this->packets.erase(it++);
00863         this->RemoveFromCache(packet);
00864         delete packet;
00865       }
00866     }
00867     loop++;
00868   }
00869 }
00870 
00872 void VehicleCargoList::InvalidateCache()
00873 {
00874   this->feeder_share = 0;
00875   this->reserved_count = 0;
00876   this->Parent::InvalidateCache();
00877   for (ConstIterator it(this->reserved.begin()); it != this->reserved.end(); it++) {
00878     this->AddToCache(*it);
00879     this->reserved_count += (*it)->count;
00880   }
00881 }
00882 
00888 void StationCargoList::AssignTo(Station *station, CargoID cargo)
00889 {
00890   assert(this->station == NULL);
00891   assert(station != NULL && cargo != INVALID_CARGO);
00892   this->station = station;
00893   this->cargo = cargo;
00894 }
00895 
00897 template class CargoList<VehicleCargoList, CargoPacketList>;
00898 template class CargoList<StationCargoList, StationCargoPacketMap>;