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   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       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     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       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       this->packets.erase(it++);
00369     } else {
00370       assert(packet->count == cap);
00371       ++it;
00372     }
00373   } else {
00374     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     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   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     FlowStatSet &flows = dest->flows[cargo_source];
00605     FlowStatSet::iterator begin = flows.begin();
00606     StationID via = (begin != flows.end() ? begin->Via() : INVALID_STATION);
00607     if (via != INVALID_STATION) {
00608       /* use cargodist unloading*/
00609       action = this->WillUnloadCargoDist(flags, next, via, cargo_source);
00610     } else {
00611       /* there is no plan: use normal unloading */
00612       action = this->WillUnloadOld(flags, cargo_source);
00613     }
00614 
00615     switch (action) {
00616       case UL_DELIVER:
00617         unloaded = source->DeliverPacket(c, remaining_unload, payment);
00618         if (via != INVALID_STATION) {
00619           if (via == this->station->index) {
00620             dest->UpdateFlowStats(flows, begin, unloaded);
00621           } else {
00622             dest->UpdateFlowStats(flows, unloaded, this->station->index);
00623           }
00624         }
00625         remaining_unload -= unloaded;
00626         break;
00627       case UL_TRANSFER:
00628         /* TransferPacket may split the packet and return the transferred part */
00629         if (via == this->station->index) {
00630           via = (++begin != flows.end()) ? begin->Via() : INVALID_STATION;
00631         }
00632         unloaded = source->TransferPacket(c, remaining_unload, this, payment, via);
00633         if (via != INVALID_STATION) {
00634           dest->UpdateFlowStats(flows, begin, unloaded);
00635         }
00636         remaining_unload -= unloaded;
00637         break;
00638       case UL_KEEP:
00639         unloaded = source->KeepPacket(c);
00640         if (via != INVALID_STATION && next != INVALID_STATION && !has_stopped) {
00641           if (via == next) {
00642             dest->UpdateFlowStats(flows, begin, unloaded);
00643           } else {
00644             dest->UpdateFlowStats(flows, unloaded, next);
00645           }
00646         }
00647         break;
00648       default:
00649         NOT_REACHED();
00650     }
00651   }
00652   return max_unload - remaining_unload;
00653 }
00654 
00658 void VehicleCargoList::OnCleanPool()
00659 {
00660   this->reserved.clear();
00661   this->Parent::OnCleanPool();
00662 }
00663 
00669 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
00670 {
00671   this->feeder_share -= cp->feeder_share;
00672   this->Parent::RemoveFromCache(cp);
00673 }
00674 
00680 void VehicleCargoList::AddToCache(const CargoPacket *cp)
00681 {
00682   this->feeder_share += cp->feeder_share;
00683   this->Parent::AddToCache(cp);
00684 }
00685 
00692 uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
00693 {
00694   uint orig_cap = cap;
00695   Iterator it = packets.begin();
00696   while (it != packets.end() && cap > 0) {
00697     cap -= MovePacket(dest, it, cap);
00698   }
00699   return orig_cap - cap;
00700 }
00701 
00705 void VehicleCargoList::AgeCargo()
00706 {
00707   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00708     CargoPacket *cp = *it;
00709     /* If we're at the maximum, then we can't increase no more. */
00710     if (cp->days_in_transit == 0xFF) continue;
00711 
00712     cp->days_in_transit++;
00713     this->cargo_days_in_transit += cp->count;
00714   }
00715 }
00716 
00717 /*
00718  *
00719  * Station cargo list implementation
00720  *
00721  */
00722 
00728 FORCEINLINE byte StationCargoList::GetUnloadFlags(OrderUnloadFlags order_flags)
00729 {
00730   byte flags = 0;
00731   if (HasBit(this->station->goods[this->cargo].acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
00732     flags |= UL_ACCEPTED;
00733   }
00734   if (order_flags & OUFB_UNLOAD) {
00735     flags |= UL_DELIVER;
00736   }
00737   if (order_flags & OUFB_TRANSFER) {
00738     flags |= UL_TRANSFER;
00739   }
00740   return flags;
00741 }
00742 
00751 void StationCargoList::Append(StationID next, CargoPacket *cp)
00752 {
00753   assert(cp != NULL);
00754   this->AddToCache(cp);
00755 
00756   StationCargoPacketMap::List &list = this->packets[next];
00757   for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
00758     CargoPacket *icp = *it;
00759     if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
00760       icp->Merge(cp);
00761       return;
00762     }
00763   }
00764 
00765   /* The packet could not be merged with another one */
00766   list.push_back(cp);
00767 }
00768 
00778 uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, bool reserve)
00779 {
00780   uint orig_cap = cap;
00781   while (begin != end && cap > 0) {
00782     cap -= this->MovePacket(dest, begin, cap, this->station->xy, reserve);
00783   }
00784   return orig_cap - cap;
00785 }
00786 
00795 uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID next, bool reserve)
00796 {
00797   uint orig_cap = cap;
00798   std::pair<Iterator, Iterator> bounds(this->packets.equal_range(next));
00799   cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
00800   if (next != INVALID_STATION && cap > 0) {
00801     bounds = this->packets.equal_range(INVALID_STATION);
00802     cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
00803   }
00804   return orig_cap - cap;
00805 }
00806 
00811 void StationCargoList::RerouteStalePackets(StationID to)
00812 {
00813   std::pair<Iterator, Iterator> range(this->packets.equal_range(to));
00814   for (Iterator it(range.first); it != range.second && it.GetKey() == to;) {
00815     CargoPacket *packet = *it;
00816     this->packets.erase(it++);
00817     StationID next = this->station->goods[this->cargo].UpdateFlowStatsTransfer(packet->source, packet->count, this->station->index);
00818     assert(next != to);
00819 
00820     /* legal, as insert doesn't invalidate iterators in the MultiMap, however
00821      * this might insert the packet between range.first and range.second (which might be end())
00822      * This is why we check for GetKey above to avoid infinite loops
00823      */
00824     this->packets.Insert(next, packet);
00825   }
00826 }
00827 
00835 void StationCargoList::CountAndTruncate(uint max_remaining, StationCargoAmountMap &cargo_per_source)
00836 {
00837   uint prev_count = this->count;
00838   uint loop = 0;
00839   while (this->count > max_remaining) {
00840     for (Iterator it(this->packets.begin()); it != this->packets.end();) {
00841       CargoPacket *packet = *it;
00842       if (loop == 0) cargo_per_source[packet->source] += packet->count;
00843 
00844       if (RandomRange(prev_count) < max_remaining) {
00845         ++it;
00846         continue;
00847       }
00848 
00849       uint diff = this->count - max_remaining;
00850       if (packet->count > diff) {
00851         packet->count -= diff;
00852         this->count = max_remaining;
00853         this->cargo_days_in_transit -= packet->days_in_transit * diff;
00854         if (loop > 0) {
00855           return;
00856         } else {
00857           ++it;
00858         }
00859       } else {
00860         this->packets.erase(it++);
00861         this->RemoveFromCache(packet);
00862         delete packet;
00863       }
00864     }
00865     loop++;
00866   }
00867 }
00868 
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 
00896 
00897 /*
00898  * We have to instantiate everything we want to be usable.
00899  */
00900 template class CargoList<VehicleCargoList, CargoPacketList>;
00901 template class CargoList<StationCargoList, StationCargoPacketMap>;

Generated on Sun Jun 5 04:19:54 2011 for OpenTTD by  doxygen 1.6.1