OpenTTD
cargopacket.cpp
Go to the documentation of this file.
1 /* $Id: cargopacket.cpp 26575 2014-05-11 12:49:51Z fonsinchen $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "stdafx.h"
13 #include "station_base.h"
14 #include "core/pool_func.hpp"
15 #include "core/random_func.hpp"
16 #include "economy_base.h"
17 #include "cargoaction.h"
18 #include "order_type.h"
19 
20 #include "safeguards.h"
21 
22 /* Initialize the cargopacket-pool */
23 CargoPacketPool _cargopacket_pool("CargoPacket");
25 
26 
30 {
31  this->source_type = ST_INDUSTRY;
32  this->source_id = INVALID_SOURCE;
33 }
34 
46 CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) :
47  feeder_share(0),
48  count(count),
49  days_in_transit(0),
50  source_id(source_id),
51  source(source),
52  source_xy(source_xy),
53  loaded_at_xy(0)
54 {
55  assert(count != 0);
56  this->source_type = source_type;
57 }
58 
73 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) :
74  feeder_share(feeder_share),
75  count(count),
76  days_in_transit(days_in_transit),
77  source_id(source_id),
78  source(source),
79  source_xy(source_xy),
80  loaded_at_xy(loaded_at_xy)
81 {
82  assert(count != 0);
83  this->source_type = source_type;
84 }
85 
92 {
93  if (!CargoPacket::CanAllocateItem()) return NULL;
94 
95  Money fs = this->FeederShare(new_size);
96  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);
97  this->feeder_share -= fs;
98  this->count -= new_size;
99  return cp_new;
100 }
101 
107 {
108  this->count += cp->count;
109  this->feeder_share += cp->feeder_share;
110  delete cp;
111 }
112 
117 void CargoPacket::Reduce(uint count)
118 {
119  assert(count < this->count);
120  this->feeder_share -= this->FeederShare(count);
121  this->count -= count;
122 }
123 
129 /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
130 {
131  CargoPacket *cp;
133  if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
134  }
135 }
136 
141 /* static */ void CargoPacket::InvalidateAllFrom(StationID sid)
142 {
143  CargoPacket *cp;
145  if (cp->source == sid) cp->source = INVALID_STATION;
146  }
147 }
148 
149 /*
150  *
151  * Cargo list implementation
152  *
153  */
154 
158 template <class Tinst, class Tcont>
160 {
161  for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
162  delete *it;
163  }
164 }
165 
170 template <class Tinst, class Tcont>
172 {
173  this->packets.clear();
174 }
175 
182 template <class Tinst, class Tcont>
184 {
185  assert(count <= cp->count);
186  this->count -= count;
187  this->cargo_days_in_transit -= cp->days_in_transit * count;
188 }
189 
195 template <class Tinst, class Tcont>
197 {
198  this->count += cp->count;
199  this->cargo_days_in_transit += cp->days_in_transit * cp->count;
200 }
201 
203 template <class Tinst, class Tcont>
205 {
206  this->count = 0;
207  this->cargo_days_in_transit = 0;
208 
209  for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
210  static_cast<Tinst *>(this)->AddToCache(*it);
211  }
212 }
213 
221 template <class Tinst, class Tcont>
223 {
224  if (Tinst::AreMergable(icp, cp) &&
225  icp->count + cp->count <= CargoPacket::MAX_COUNT) {
226  icp->Merge(cp);
227  return true;
228  } else {
229  return false;
230  }
231 }
232 
233 /*
234  *
235  * Vehicle cargo list implementation.
236  *
237  */
238 
255 {
256  assert(cp != NULL);
257  assert(action == MTA_LOAD ||
258  (action == MTA_KEEP && this->action_counts[MTA_LOAD] == 0));
259  this->AddToMeta(cp, action);
260 
261  if (this->count == cp->count) {
262  this->packets.push_back(cp);
263  return;
264  }
265 
266  uint sum = cp->count;
267  for (ReverseIterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
268  CargoPacket *icp = *it;
269  if (VehicleCargoList::TryMerge(icp, cp)) return;
270  sum += icp->count;
271  if (sum >= this->action_counts[action]) {
272  this->packets.push_back(cp);
273  return;
274  }
275  }
276 
277  NOT_REACHED();
278 }
279 
288 template<class Taction>
289 void VehicleCargoList::ShiftCargo(Taction action)
290 {
291  Iterator it(this->packets.begin());
292  while (it != this->packets.end() && action.MaxMove() > 0) {
293  CargoPacket *cp = *it;
294  if (action(cp)) {
295  it = this->packets.erase(it);
296  } else {
297  break;
298  }
299  }
300 }
301 
310 template<class Taction>
311 void VehicleCargoList::PopCargo(Taction action)
312 {
313  if (this->packets.empty()) return;
314  Iterator it(--(this->packets.end()));
315  Iterator begin(this->packets.begin());
316  while (action.MaxMove() > 0) {
317  CargoPacket *cp = *it;
318  if (action(cp)) {
319  if (it != begin) {
320  this->packets.erase(it--);
321  } else {
322  this->packets.erase(it);
323  break;
324  }
325  } else {
326  break;
327  }
328  }
329 }
330 
338 {
339  this->feeder_share -= cp->FeederShare(count);
340  this->Parent::RemoveFromCache(cp, count);
341 }
342 
349 {
350  this->feeder_share += cp->feeder_share;
351  this->Parent::AddToCache(cp);
352 }
353 
360 void VehicleCargoList::RemoveFromMeta(const CargoPacket *cp, MoveToAction action, uint count)
361 {
362  assert(count <= this->action_counts[action]);
363  this->AssertCountConsistency();
364  this->RemoveFromCache(cp, count);
365  this->action_counts[action] -= count;
366  this->AssertCountConsistency();
367 }
368 
375 {
376  this->AssertCountConsistency();
377  this->AddToCache(cp);
378  this->action_counts[action] += cp->count;
379  this->AssertCountConsistency();
380 }
381 
386 {
387  for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
388  CargoPacket *cp = *it;
389  /* If we're at the maximum, then we can't increase no more. */
390  if (cp->days_in_transit == 0xFF) continue;
391 
392  cp->days_in_transit++;
393  this->cargo_days_in_transit += cp->count;
394  }
395 }
396 
405 {
406  uint sum = 0;
407  for (Iterator it = this->packets.begin(); sum < this->action_counts[MTA_TRANSFER]; ++it) {
408  CargoPacket *cp = *it;
409  cp->loaded_at_xy = xy;
410  sum += cp->count;
411  }
412 }
413 
423 /* static */ VehicleCargoList::MoveToAction VehicleCargoList::ChooseAction(const CargoPacket *cp, StationID cargo_next,
424  StationID current_station, bool accepted, StationIDStack next_station)
425 {
426  if (cargo_next == INVALID_STATION) {
427  return (accepted && cp->source != current_station) ? MTA_DELIVER : MTA_KEEP;
428  } else if (cargo_next == current_station) {
429  return MTA_DELIVER;
430  } else if (next_station.Contains(cargo_next)) {
431  return MTA_KEEP;
432  } else {
433  return MTA_TRANSFER;
434  }
435 }
436 
450 bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment)
451 {
452  this->AssertCountConsistency();
453  assert(this->action_counts[MTA_LOAD] == 0);
455  Iterator deliver = this->packets.end();
456  Iterator it = this->packets.begin();
457  uint sum = 0;
458 
459  bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0;
460  bool force_unload = (order_flags & OUFB_UNLOAD) != 0;
461  bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0;
462  assert(this->count > 0 || it == this->packets.end());
463  while (sum < this->count) {
464  CargoPacket *cp = *it;
465 
466  this->packets.erase(it++);
467  StationID cargo_next = INVALID_STATION;
468  MoveToAction action = MTA_LOAD;
469  if (force_keep) {
470  action = MTA_KEEP;
471  } else if (force_unload && accepted && cp->source != current_station) {
472  action = MTA_DELIVER;
473  } else if (force_transfer) {
474  action = MTA_TRANSFER;
475  /* We cannot send the cargo to any of the possible next hops and
476  * also not to the current station. */
477  FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
478  if (flow_it == ge->flows.end()) {
479  cargo_next = INVALID_STATION;
480  } else {
481  FlowStat new_shares = flow_it->second;
482  new_shares.ChangeShare(current_station, INT_MIN);
483  StationIDStack excluded = next_station;
484  while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
485  new_shares.ChangeShare(excluded.Pop(), INT_MIN);
486  }
487  if (new_shares.GetShares()->empty()) {
488  cargo_next = INVALID_STATION;
489  } else {
490  cargo_next = new_shares.GetVia();
491  }
492  }
493  } else {
494  /* Rewrite an invalid source station to some random other one to
495  * avoid keeping the cargo in the vehicle forever. */
496  if (cp->source == INVALID_STATION && !ge->flows.empty()) {
497  cp->source = ge->flows.begin()->first;
498  }
499  bool restricted = false;
500  FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
501  if (flow_it == ge->flows.end()) {
502  cargo_next = INVALID_STATION;
503  } else {
504  cargo_next = flow_it->second.GetViaWithRestricted(restricted);
505  }
506  action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
507  if (restricted && action == MTA_TRANSFER) {
508  /* If the flow is restricted we can't transfer to it. Choose an
509  * unrestricted one instead. */
510  cargo_next = flow_it->second.GetVia();
511  action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
512  }
513  }
514  Money share;
515  switch (action) {
516  case MTA_KEEP:
517  this->packets.push_back(cp);
518  if (deliver == this->packets.end()) --deliver;
519  break;
520  case MTA_DELIVER:
521  this->packets.insert(deliver, cp);
522  break;
523  case MTA_TRANSFER:
524  this->packets.push_front(cp);
525  /* Add feeder share here to allow reusing field for next station. */
526  share = payment->PayTransfer(cp, cp->count);
527  cp->AddFeederShare(share);
528  this->feeder_share += share;
529  cp->next_station = cargo_next;
530  break;
531  default:
532  NOT_REACHED();
533  }
534  this->action_counts[action] += cp->count;
535  sum += cp->count;
536  }
537  this->AssertCountConsistency();
538  return this->action_counts[MTA_DELIVER] > 0 || this->action_counts[MTA_TRANSFER] > 0;
539 }
540 
543 {
544  this->feeder_share = 0;
545  this->Parent::InvalidateCache();
546 }
547 
560 template<VehicleCargoList::MoveToAction Tfrom, VehicleCargoList::MoveToAction Tto>
561 uint VehicleCargoList::Reassign(uint max_move, TileOrStationID)
562 {
563  assert_tcompile(Tfrom != MTA_TRANSFER && Tto != MTA_TRANSFER);
564  assert_tcompile(Tfrom - Tto == 1 || Tto - Tfrom == 1);
565  max_move = min(this->action_counts[Tfrom], max_move);
566  this->action_counts[Tfrom] -= max_move;
567  this->action_counts[Tto] += max_move;
568  return max_move;
569 }
570 
578 template<>
579 uint VehicleCargoList::Reassign<VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_TRANSFER>(uint max_move, TileOrStationID next_station)
580 {
581  max_move = min(this->action_counts[MTA_DELIVER], max_move);
582 
583  uint sum = 0;
584  for (Iterator it(this->packets.begin()); sum < this->action_counts[MTA_TRANSFER] + max_move;) {
585  CargoPacket *cp = *it++;
586  sum += cp->Count();
587  if (sum <= this->action_counts[MTA_TRANSFER]) continue;
588  if (sum > this->action_counts[MTA_TRANSFER] + max_move) {
589  CargoPacket *cp_split = cp->Split(sum - this->action_counts[MTA_TRANSFER] + max_move);
590  sum -= cp_split->Count();
591  this->packets.insert(it, cp_split);
592  }
593  cp->next_station = next_station;
594  }
595 
596  this->action_counts[MTA_DELIVER] -= max_move;
597  this->action_counts[MTA_TRANSFER] += max_move;
598  return max_move;
599 }
600 
608 uint VehicleCargoList::Return(uint max_move, StationCargoList *dest, StationID next)
609 {
610  max_move = min(this->action_counts[MTA_LOAD], max_move);
611  this->PopCargo(CargoReturn(this, dest, max_move, next));
612  return max_move;
613 }
614 
621 uint VehicleCargoList::Shift(uint max_move, VehicleCargoList *dest)
622 {
623  max_move = min(this->count, max_move);
624  this->PopCargo(CargoShift(this, dest, max_move));
625  return max_move;
626 }
627 
636 uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPayment *payment)
637 {
638  uint moved = 0;
639  if (this->action_counts[MTA_TRANSFER] > 0) {
640  uint move = min(this->action_counts[MTA_TRANSFER], max_move);
641  this->ShiftCargo(CargoTransfer(this, dest, move));
642  moved += move;
643  }
644  if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) {
645  uint move = min(this->action_counts[MTA_DELIVER], max_move - moved);
646  this->ShiftCargo(CargoDelivery(this, move, payment));
647  moved += move;
648  }
649  return moved;
650 }
651 
658 uint VehicleCargoList::Truncate(uint max_move)
659 {
660  max_move = min(this->count, max_move);
661  this->PopCargo(CargoRemoval<VehicleCargoList>(this, max_move));
662  return max_move;
663 }
664 
673 uint VehicleCargoList::Reroute(uint max_move, VehicleCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
674 {
675  max_move = min(this->action_counts[MTA_TRANSFER], max_move);
676  this->ShiftCargo(VehicleCargoReroute(this, dest, max_move, avoid, avoid2, ge));
677  return max_move;
678 }
679 
680 /*
681  *
682  * Station cargo list implementation.
683  *
684  */
685 
694 void StationCargoList::Append(CargoPacket *cp, StationID next)
695 {
696  assert(cp != NULL);
697  this->AddToCache(cp);
698 
699  StationCargoPacketMap::List &list = this->packets[next];
700  for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin());
701  it != list.rend(); it++) {
702  if (StationCargoList::TryMerge(*it, cp)) return;
703  }
704 
705  /* The packet could not be merged with another one */
706  list.push_back(cp);
707 }
708 
721 template <class Taction>
722 bool StationCargoList::ShiftCargo(Taction &action, StationID next)
723 {
724  std::pair<Iterator, Iterator> range(this->packets.equal_range(next));
725  for (Iterator it(range.first); it != range.second && it.GetKey() == next;) {
726  if (action.MaxMove() == 0) return false;
727  CargoPacket *cp = *it;
728  if (action(cp)) {
729  it = this->packets.erase(it);
730  } else {
731  return false;
732  }
733  }
734  return true;
735 }
736 
751 template <class Taction>
752 uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool include_invalid)
753 {
754  uint max_move = action.MaxMove();
755  while (!next.IsEmpty()) {
756  this->ShiftCargo(action, next.Pop());
757  if (action.MaxMove() == 0) break;
758  }
759  if (include_invalid && action.MaxMove() > 0) {
760  this->ShiftCargo(action, INVALID_STATION);
761  }
762  return max_move - action.MaxMove();
763 }
764 
773 uint StationCargoList::Truncate(uint max_move, StationCargoAmountMap *cargo_per_source)
774 {
775  max_move = min(max_move, this->count);
776  uint prev_count = this->count;
777  uint moved = 0;
778  uint loop = 0;
779  bool do_count = cargo_per_source != NULL;
780  while (max_move > moved) {
781  for (Iterator it(this->packets.begin()); it != this->packets.end();) {
782  CargoPacket *cp = *it;
783  if (prev_count > max_move && RandomRange(prev_count) < prev_count - max_move) {
784  if (do_count && loop == 0) {
785  (*cargo_per_source)[cp->source] += cp->count;
786  }
787  ++it;
788  continue;
789  }
790  uint diff = max_move - moved;
791  if (cp->count > diff) {
792  if (diff > 0) {
793  this->RemoveFromCache(cp, diff);
794  cp->Reduce(diff);
795  moved += diff;
796  }
797  if (loop > 0) {
798  if (do_count) (*cargo_per_source)[cp->source] -= diff;
799  return moved;
800  } else {
801  if (do_count) (*cargo_per_source)[cp->source] += cp->count;
802  ++it;
803  }
804  } else {
805  it = this->packets.erase(it);
806  if (do_count && loop > 0) {
807  (*cargo_per_source)[cp->source] -= cp->count;
808  }
809  moved += cp->count;
810  this->RemoveFromCache(cp, cp->count);
811  delete cp;
812  }
813  }
814  loop++;
815  }
816  return moved;
817 }
818 
827 uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
828 {
829  return this->ShiftCargo(CargoReservation(this, dest, max_move, load_place), next_station, true);
830 }
831 
844 uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
845 {
846  uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move);
847  if (move > 0) {
848  this->reserved_count -= move;
850  return move;
851  } else {
852  return this->ShiftCargo(CargoLoad(this, dest, max_move, load_place), next_station, true);
853  }
854 }
855 
864 uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
865 {
866  return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
867 }
868 
869 /*
870  * We have to instantiate everything we want to be usable.
871  */
874 template uint VehicleCargoList::Reassign<VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_KEEP>(uint, TileOrStationID);