# cargopacket.cpp

Go to the documentation of this file.

    00001 /* $Id: cargopacket.cpp 26333 2014-02-11 20:34:48Z frosch $ */
    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 #include "cargoaction.h"
    00018 #include "order_type.h"
    00019
    00020 /* Initialize the cargopacket-pool */
    00021 CargoPacketPool _cargopacket_pool("CargoPacket");
    00022 INSTANTIATE_POOL_METHODS(CargoPacket)
    00023
    00024
    00027 CargoPacket::CargoPacket()
    00028 {
    00029   this->source_type = ST_INDUSTRY;
    00030   this->source_id   = INVALID_SOURCE;
    00031 }
    00032
    00044 CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) :
    00045   feeder_share(0),
    00046   count(count),
    00047   days_in_transit(0),
    00048   source_id(source_id),
    00049   source(source),
    00050   source_xy(source_xy),
    00051   loaded_at_xy(0)
    00052 {
    00053   assert(count != 0);
    00054   this->source_type  = source_type;
    00055 }
    00056
    00071 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) :
    00072     feeder_share(feeder_share),
    00073     count(count),
    00074     days_in_transit(days_in_transit),
    00075     source_id(source_id),
    00076     source(source),
    00077     source_xy(source_xy),
    00078     loaded_at_xy(loaded_at_xy)
    00079 {
    00080   assert(count != 0);
    00081   this->source_type = source_type;
    00082 }
    00083
    00089 CargoPacket *CargoPacket::Split(uint new_size)
    00090 {
    00091   if (!CargoPacket::CanAllocateItem()) return NULL;
    00092
    00093   Money fs = this->FeederShare(new_size);
    00094   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);
    00095   this->feeder_share -= fs;
    00096   this->count -= new_size;
    00097   return cp_new;
    00098 }
    00099
    00104 void CargoPacket::Merge(CargoPacket *cp)
    00105 {
    00106   this->count += cp->count;
    00107   this->feeder_share += cp->feeder_share;
    00108   delete cp;
    00109 }
    00110
    00115 void CargoPacket::Reduce(uint count)
    00116 {
    00117   assert(count < this->count);
    00118   this->feeder_share -= this->FeederShare(count);
    00119   this->count -= count;
    00120 }
    00121
    00127 /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
    00128 {
    00129   CargoPacket *cp;
    00130   FOR_ALL_CARGOPACKETS(cp) {
    00131     if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
    00132   }
    00133 }
    00134
    00139 /* static */ void CargoPacket::InvalidateAllFrom(StationID sid)
    00140 {
    00141   CargoPacket *cp;
    00142   FOR_ALL_CARGOPACKETS(cp) {
    00143     if (cp->source == sid) cp->source = INVALID_STATION;
    00144   }
    00145 }
    00146
    00147 /*
    00148  *
    00149  * Cargo list implementation
    00150  *
    00151  */
    00152
    00156 template <class Tinst, class Tcont>
    00157 CargoList<Tinst, Tcont>::~CargoList()
    00158 {
    00159   for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
    00160     delete *it;
    00161   }
    00162 }
    00163
    00168 template <class Tinst, class Tcont>
    00169 void CargoList<Tinst, Tcont>::OnCleanPool()
    00170 {
    00171   this->packets.clear();
    00172 }
    00173
    00180 template <class Tinst, class Tcont>
    00181 void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp, uint count)
    00182 {
    00183   assert(count <= cp->count);
    00184   this->count                 -= count;
    00185   this->cargo_days_in_transit -= cp->days_in_transit * count;
    00186 }
    00187
    00193 template <class Tinst, class Tcont>
    00194 void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
    00195 {
    00196   this->count                 += cp->count;
    00197   this->cargo_days_in_transit += cp->days_in_transit * cp->count;
    00198 }
    00199
    00201 template <class Tinst, class Tcont>
    00202 void CargoList<Tinst, Tcont>::InvalidateCache()
    00203 {
    00204   this->count = 0;
    00205   this->cargo_days_in_transit = 0;
    00206
    00207   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
    00208     static_cast<Tinst *>(this)->AddToCache(*it);
    00209   }
    00210 }
    00211
    00219 template <class Tinst, class Tcont>
    00220 /* static */ bool CargoList<Tinst, Tcont>::TryMerge(CargoPacket *icp, CargoPacket *cp)
    00221 {
    00222   if (Tinst::AreMergable(icp, cp) &&
    00223       icp->count + cp->count <= CargoPacket::MAX_COUNT) {
    00224     icp->Merge(cp);
    00225     return true;
    00226   } else {
    00227     return false;
    00228   }
    00229 }
    00230
    00231 /*
    00232  *
    00233  * Vehicle cargo list implementation.
    00234  *
    00235  */
    00236
    00252 void VehicleCargoList::Append(CargoPacket *cp, MoveToAction action)
    00253 {
    00254   assert(cp != NULL);
    00255   assert(action == MTA_LOAD ||
    00256       (action == MTA_KEEP && this->action_counts[MTA_LOAD] == 0));
    00257   this->AddToMeta(cp, action);
    00258
    00259   if (this->count == cp->count) {
    00260     this->packets.push_back(cp);
    00261     return;
    00262   }
    00263
    00264   uint sum = cp->count;
    00265   for (ReverseIterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
    00266     CargoPacket *icp = *it;
    00267     if (VehicleCargoList::TryMerge(icp, cp)) return;
    00268     sum += icp->count;
    00269     if (sum >= this->action_counts[action]) {
    00270       this->packets.push_back(cp);
    00271       return;
    00272     }
    00273   }
    00274
    00275   NOT_REACHED();
    00276 }
    00277
    00286 template<class Taction>
    00287 void VehicleCargoList::ShiftCargo(Taction action)
    00288 {
    00289   Iterator it(this->packets.begin());
    00290   while (it != this->packets.end() && action.MaxMove() > 0) {
    00291     CargoPacket *cp = *it;
    00292     if (action(cp)) {
    00293       it = this->packets.erase(it);
    00294     } else {
    00295       break;
    00296     }
    00297   }
    00298 }
    00299
    00308 template<class Taction>
    00309 void VehicleCargoList::PopCargo(Taction action)
    00310 {
    00311   if (this->packets.empty()) return;
    00312   Iterator it(--(this->packets.end()));
    00313   Iterator begin(this->packets.begin());
    00314   while (action.MaxMove() > 0) {
    00315     CargoPacket *cp = *it;
    00316     if (action(cp)) {
    00317       if (it != begin) {
    00318         this->packets.erase(it--);
    00319       } else {
    00320         this->packets.erase(it);
    00321         break;
    00322       }
    00323     } else {
    00324       break;
    00325     }
    00326   }
    00327 }
    00328
    00335 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp, uint count)
    00336 {
    00337   this->feeder_share -= cp->FeederShare(count);
    00338   this->Parent::RemoveFromCache(cp, count);
    00339 }
    00340
    00346 void VehicleCargoList::AddToCache(const CargoPacket *cp)
    00347 {
    00348   this->feeder_share += cp->feeder_share;
    00349   this->Parent::AddToCache(cp);
    00350 }
    00351
    00358 void VehicleCargoList::RemoveFromMeta(const CargoPacket *cp, MoveToAction action, uint count)
    00359 {
    00360   this->AssertCountConsistency();
    00361   this->RemoveFromCache(cp, count);
    00362   this->action_counts[action] -= count;
    00363   this->AssertCountConsistency();
    00364 }
    00365
    00371 void VehicleCargoList::AddToMeta(const CargoPacket *cp, MoveToAction action)
    00372 {
    00373   this->AssertCountConsistency();
    00374   this->AddToCache(cp);
    00375   this->action_counts[action] += cp->count;
    00376   this->AssertCountConsistency();
    00377 }
    00378
    00382 void VehicleCargoList::AgeCargo()
    00383 {
    00384   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
    00385     CargoPacket *cp = *it;
    00386     /* If we're at the maximum, then we can't increase no more. */
    00387     if (cp->days_in_transit == 0xFF) continue;
    00388
    00389     cp->days_in_transit++;
    00390     this->cargo_days_in_transit += cp->count;
    00391   }
    00392 }
    00393
    00401 void VehicleCargoList::SetTransferLoadPlace(TileIndex xy)
    00402 {
    00403   uint sum = 0;
    00404   for (Iterator it = this->packets.begin(); sum < this->action_counts[MTA_TRANSFER]; ++it) {
    00405     CargoPacket *cp = *it;
    00406     cp->loaded_at_xy = xy;
    00407     sum += cp->count;
    00408   }
    00409 }
    00410
    00420 /* static */ VehicleCargoList::MoveToAction VehicleCargoList::ChooseAction(const CargoPacket *cp, StationID cargo_next,
    00421     StationID current_station, bool accepted, StationIDStack next_station)
    00422 {
    00423   if (cargo_next == INVALID_STATION) {
    00424     return (accepted && cp->source != current_station) ? MTA_DELIVER : MTA_KEEP;
    00425   } else if (cargo_next == current_station) {
    00426     return MTA_DELIVER;
    00427   } else if (next_station.Contains(cargo_next)) {
    00428     return MTA_KEEP;
    00429   } else {
    00430     return MTA_TRANSFER;
    00431   }
    00432 }
    00433
    00447 bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment)
    00448 {
    00449   this->AssertCountConsistency();
    00450   assert(this->action_counts[MTA_LOAD] == 0);
    00451   this->action_counts[MTA_TRANSFER] = this->action_counts[MTA_DELIVER] = this->action_counts[MTA_KEEP] = 0;
    00452   Iterator deliver = this->packets.end();
    00453   Iterator it = this->packets.begin();
    00454   uint sum = 0;
    00455
    00456   bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0;
    00457   bool force_unload = (order_flags & OUFB_UNLOAD) != 0;
    00458   bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0;
    00459   assert(this->count > 0 || it == this->packets.end());
    00460   while (sum < this->count) {
    00461     CargoPacket *cp = *it;
    00462
    00463     this->packets.erase(it++);
    00464     StationID cargo_next = INVALID_STATION;
    00465     MoveToAction action = MTA_LOAD;
    00466     if (force_keep) {
    00467       action = MTA_KEEP;
    00468     } else if (force_unload && accepted && cp->source != current_station) {
    00469       action = MTA_DELIVER;
    00470     } else if (force_transfer) {
    00471       action = MTA_TRANSFER;
    00472       /* We cannot send the cargo to any of the possible next hops and
    00473        * also not to the current station. */
    00474       FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
    00475       if (flow_it == ge->flows.end()) {
    00476         cargo_next = INVALID_STATION;
    00477       } else {
    00478         FlowStat new_shares = flow_it->second;
    00479         new_shares.ChangeShare(current_station, INT_MIN);
    00480         StationIDStack excluded = next_station;
    00481         while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
    00482           new_shares.ChangeShare(excluded.Pop(), INT_MIN);
    00483         }
    00484         if (new_shares.GetShares()->empty()) {
    00485           cargo_next = INVALID_STATION;
    00486         } else {
    00487           cargo_next = new_shares.GetVia();
    00488         }
    00489       }
    00490     } else {
    00491       /* Rewrite an invalid source station to some random other one to
    00492        * avoid keeping the cargo in the vehicle forever. */
    00493       if (cp->source == INVALID_STATION && !ge->flows.empty()) {
    00494         cp->source = ge->flows.begin()->first;
    00495       }
    00496       bool restricted = false;
    00497       FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
    00498       if (flow_it == ge->flows.end()) {
    00499         cargo_next = INVALID_STATION;
    00500       } else {
    00501         cargo_next = flow_it->second.GetViaWithRestricted(restricted);
    00502       }
    00503       action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
    00504       if (restricted && action == MTA_TRANSFER) {
    00505         /* If the flow is restricted we can't transfer to it. Choose an
    00506          * unrestricted one instead. */
    00507         cargo_next = flow_it->second.GetVia();
    00508         action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
    00509       }
    00510     }
    00511     Money share;
    00512     switch (action) {
    00513       case MTA_KEEP:
    00514         this->packets.push_back(cp);
    00515         if (deliver == this->packets.end()) --deliver;
    00516         break;
    00517       case MTA_DELIVER:
    00518         this->packets.insert(deliver, cp);
    00519         break;
    00520       case MTA_TRANSFER:
    00521         this->packets.push_front(cp);
    00522         /* Add feeder share here to allow reusing field for next station. */
    00523         share = payment->PayTransfer(cp, cp->count);
    00524         cp->AddFeederShare(share);
    00525         this->feeder_share += share;
    00526         cp->next_station = cargo_next;
    00527         break;
    00528       default:
    00529         NOT_REACHED();
    00530     }
    00531     this->action_counts[action] += cp->count;
    00532     sum += cp->count;
    00533   }
    00534   this->AssertCountConsistency();
    00535   return this->action_counts[MTA_DELIVER] > 0 || this->action_counts[MTA_TRANSFER] > 0;
    00536 }
    00537
    00539 void VehicleCargoList::InvalidateCache()
    00540 {
    00541   this->feeder_share = 0;
    00542   this->Parent::InvalidateCache();
    00543 }
    00544
    00557 template<VehicleCargoList::MoveToAction Tfrom, VehicleCargoList::MoveToAction Tto>
    00558 uint VehicleCargoList::Reassign(uint max_move, TileOrStationID)
    00559 {
    00560   assert_tcompile(Tfrom != MTA_TRANSFER && Tto != MTA_TRANSFER);
    00561   assert_tcompile(Tfrom - Tto == 1 || Tto - Tfrom == 1);
    00562   max_move = min(this->action_counts[Tfrom], max_move);
    00563   this->action_counts[Tfrom] -= max_move;
    00564   this->action_counts[Tto] += max_move;
    00565   return max_move;
    00566 }
    00567
    00575 template<>
    00576 uint VehicleCargoList::Reassign<VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_TRANSFER>(uint max_move, TileOrStationID next_station)
    00577 {
    00578   max_move = min(this->action_counts[MTA_DELIVER], max_move);
    00579
    00580   uint sum = 0;
    00581   for (Iterator it(this->packets.begin()); sum < this->action_counts[MTA_TRANSFER] + max_move;) {
    00582     CargoPacket *cp = *it++;
    00583     sum += cp->Count();
    00584     if (sum <= this->action_counts[MTA_TRANSFER]) continue;
    00585     if (sum > this->action_counts[MTA_TRANSFER] + max_move) {
    00586       CargoPacket *cp_split = cp->Split(sum - this->action_counts[MTA_TRANSFER] + max_move);
    00587       sum -= cp_split->Count();
    00588       this->packets.insert(it, cp_split);
    00589     }
    00590     cp->next_station = next_station;
    00591   }
    00592
    00593   this->action_counts[MTA_DELIVER] -= max_move;
    00594   this->action_counts[MTA_TRANSFER] += max_move;
    00595   return max_move;
    00596 }
    00597
    00605 uint VehicleCargoList::Return(uint max_move, StationCargoList *dest, StationID next)
    00606 {
    00607   max_move = min(this->action_counts[MTA_LOAD], max_move);
    00608   this->PopCargo(CargoReturn(this, dest, max_move, next));
    00609   return max_move;
    00610 }
    00611
    00618 uint VehicleCargoList::Shift(uint max_move, VehicleCargoList *dest)
    00619 {
    00620   max_move = min(this->count, max_move);
    00621   this->PopCargo(CargoShift(this, dest, max_move));
    00622   return max_move;
    00623 }
    00624
    00633 uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPayment *payment)
    00634 {
    00635   uint moved = 0;
    00636   if (this->action_counts[MTA_TRANSFER] > 0) {
    00637     uint move = min(this->action_counts[MTA_TRANSFER], max_move);
    00638     this->ShiftCargo(CargoTransfer(this, dest, move));
    00639     moved += move;
    00640   }
    00641   if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) {
    00642     uint move = min(this->action_counts[MTA_DELIVER], max_move - moved);
    00643     this->ShiftCargo(CargoDelivery(this, move, payment));
    00644     moved += move;
    00645   }
    00646   return moved;
    00647 }
    00648
    00655 uint VehicleCargoList::Truncate(uint max_move)
    00656 {
    00657   max_move = min(this->count, max_move);
    00658   this->PopCargo(CargoRemoval<VehicleCargoList>(this, max_move));
    00659   return max_move;
    00660 }
    00661
    00670 uint VehicleCargoList::Reroute(uint max_move, VehicleCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
    00671 {
    00672   max_move = min(this->action_counts[MTA_TRANSFER], max_move);
    00673   this->ShiftCargo(VehicleCargoReroute(this, dest, max_move, avoid, avoid2, ge));
    00674   return max_move;
    00675 }
    00676
    00677 /*
    00678  *
    00679  * Station cargo list implementation.
    00680  *
    00681  */
    00682
    00691 void StationCargoList::Append(CargoPacket *cp, StationID next)
    00692 {
    00693   assert(cp != NULL);
    00694   this->AddToCache(cp);
    00695
    00696   StationCargoPacketMap::List &list = this->packets[next];
    00697   for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin());
    00698       it != list.rend(); it++) {
    00699     if (StationCargoList::TryMerge(*it, cp)) return;
    00700   }
    00701
    00702   /* The packet could not be merged with another one */
    00703   list.push_back(cp);
    00704 }
    00705
    00718 template <class Taction>
    00719 bool StationCargoList::ShiftCargo(Taction &action, StationID next)
    00720 {
    00721   std::pair<Iterator, Iterator> range(this->packets.equal_range(next));
    00722   for (Iterator it(range.first); it != range.second && it.GetKey() == next;) {
    00723     if (action.MaxMove() == 0) return false;
    00724     CargoPacket *cp = *it;
    00725     if (action(cp)) {
    00726       it = this->packets.erase(it);
    00727     } else {
    00728       return false;
    00729     }
    00730   }
    00731   return true;
    00732 }
    00733
    00748 template <class Taction>
    00749 uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool include_invalid)
    00750 {
    00751   uint max_move = action.MaxMove();
    00752   while (!next.IsEmpty()) {
    00753     this->ShiftCargo(action, next.Pop());
    00754     if (action.MaxMove() == 0) break;
    00755   }
    00756   if (include_invalid && action.MaxMove() > 0) {
    00757     this->ShiftCargo(action, INVALID_STATION);
    00758   }
    00759   return max_move - action.MaxMove();
    00760 }
    00761
    00770 uint StationCargoList::Truncate(uint max_move, StationCargoAmountMap *cargo_per_source)
    00771 {
    00772   max_move = min(max_move, this->count);
    00773   uint prev_count = this->count;
    00774   uint moved = 0;
    00775   uint loop = 0;
    00776   bool do_count = cargo_per_source != NULL;
    00777   while (max_move > moved) {
    00778     for (Iterator it(this->packets.begin()); it != this->packets.end();) {
    00779       CargoPacket *cp = *it;
    00780       if (prev_count > max_move && RandomRange(prev_count) < prev_count - max_move) {
    00781         if (do_count && loop == 0) {
    00782           (*cargo_per_source)[cp->source] += cp->count;
    00783         }
    00784         ++it;
    00785         continue;
    00786       }
    00787       uint diff = max_move - moved;
    00788       if (cp->count > diff) {
    00789         if (diff > 0) {
    00790           this->RemoveFromCache(cp, diff);
    00791           cp->Reduce(diff);
    00792           moved += diff;
    00793         }
    00794         if (loop > 0) {
    00795           if (do_count) (*cargo_per_source)[cp->source] -= diff;
    00796           return moved;
    00797         } else {
    00798           if (do_count) (*cargo_per_source)[cp->source] += cp->count;
    00799           ++it;
    00800         }
    00801       } else {
    00802         it = this->packets.erase(it);
    00803         if (do_count && loop > 0) {
    00804           (*cargo_per_source)[cp->source] -= cp->count;
    00805         }
    00806         moved += cp->count;
    00807         this->RemoveFromCache(cp, cp->count);
    00808         delete cp;
    00809       }
    00810     }
    00811     loop++;
    00812   }
    00813   return moved;
    00814 }
    00815
    00824 uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
    00825 {
    00826   return this->ShiftCargo(CargoReservation(this, dest, max_move, load_place), next_station, true);
    00827 }
    00828
    00841 uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
    00842 {
    00843   uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move);
    00844   if (move > 0) {
    00845     this->reserved_count -= move;
    00846     dest->Reassign<VehicleCargoList::MTA_LOAD, VehicleCargoList::MTA_KEEP>(move);
    00847     return move;
    00848   } else {
    00849     return this->ShiftCargo(CargoLoad(this, dest, max_move, load_place), next_station, true);
    00850   }
    00851 }
    00852
    00861 uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
    00862 {
    00863   return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
    00864 }
    00865
    00866 /*
    00867  * We have to instantiate everything we want to be usable.
    00868  */
    00869 template class CargoList<VehicleCargoList, CargoPacketList>;
    00870 template class CargoList<StationCargoList, StationCargoPacketMap>;
    00871 template uint VehicleCargoList::Reassign<VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_KEEP>(uint, TileOrStationID);