diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index fba82f6be4..91f7cb83ef 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -1476,7 +1476,7 @@ static void MaybeCrashAirplane(Aircraft *v) /* Crash the airplane. Remove all goods stored at the station. */ for (CargoID i = 0; i < NUM_CARGO; i++) { st->goods[i].rating = 1; - st->goods[i].cargo.Truncate(); + if (st->goods[i].data != nullptr) st->goods[i].data->cargo.Truncate(); } CrashAirplane(v); diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index 927f0df4e9..1be073d3a6 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -638,6 +638,8 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID CargoPacketList transfer_deliver; std::vector keep; + const FlowStatMap &flows = ge->CreateData().flows; + bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0; bool force_unload = (order_flags & OUFB_UNLOAD) != 0; bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0; @@ -656,8 +658,8 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID action = MTA_TRANSFER; /* We cannot send the cargo to any of the possible next hops and * also not to the current station. */ - FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source)); - if (flow_it == ge->flows.end()) { + FlowStatMap::const_iterator flow_it(flows.find(cp->source)); + if (flow_it == flows.end()) { cargo_next = INVALID_STATION; } else { FlowStat new_shares = *flow_it; @@ -675,12 +677,12 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID } else { /* Rewrite an invalid source station to some random other one to * avoid keeping the cargo in the vehicle forever. */ - if (cp->source == INVALID_STATION && !ge->flows.empty()) { - cp->source = ge->flows.FirstStationID(); + if (cp->source == INVALID_STATION && !flows.empty()) { + cp->source = flows.FirstStationID(); } bool restricted = false; - FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source)); - if (flow_it == ge->flows.end()) { + FlowStatMap::const_iterator flow_it(flows.find(cp->source)); + if (flow_it == flows.end()) { cargo_next = INVALID_STATION; } else { cargo_next = flow_it->GetViaWithRestricted(restricted); diff --git a/src/cargopacket.h b/src/cargopacket.h index 6d179e8db1..3c8c6b1c93 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -605,6 +605,11 @@ public: this->reserved_count += count; } + void LoadSetReservedCount(uint count) + { + this->reserved_count = count; + } + /** * Are the two CargoPackets mergeable in the context of * a list of CargoPackets for a Station? diff --git a/src/economy.cpp b/src/economy.cpp index d8fe33c06b..cfc1d75a7d 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -1668,7 +1668,7 @@ struct ReturnCargoAction */ bool operator()(Vehicle *v) { - v->cargo.Return(UINT_MAX, &this->st->goods[v->cargo_type].cargo, this->next_hop); + v->cargo.Return(UINT_MAX, &this->st->goods[v->cargo_type].CreateData().cargo, this->next_hop); return true; } }; @@ -1704,7 +1704,7 @@ struct FinalizeRefitAction bool operator()(Vehicle *v) { if (this->do_reserve || (cargo_type_loading == nullptr || (cargo_type_loading->current_order.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD))) { - this->st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), + this->st->goods[v->cargo_type].CreateData().cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), &v->cargo, st->xy, this->next_station.Get(v->cargo_type)); } this->consist_capleft[v->cargo_type] += v->cargo_cap - v->cargo.RemainingCount(); @@ -1740,7 +1740,7 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station new_cid = v_start->cargo_type; for (CargoID cid : SetCargoBitIterator(refit_mask)) { if (check_order && v->First()->current_order.GetCargoLoadType(cid) == OLFB_NO_LOAD) continue; - if (st->goods[cid].cargo.HasCargoFor(next_station.Get(cid))) { + if (st->goods[cid].data != nullptr && st->goods[cid].data->cargo.HasCargoFor(next_station.Get(cid))) { /* Try to find out if auto-refitting would succeed. In case the refit is allowed, * the returned refit capacity will be greater than zero. */ DoCommand(v_start->tile, v_start->index, cid | 1U << 24 | 0xFF << 8 | 1U << 16, DC_QUERY_COST, GetCmdRefitVeh(v_start)); // Auto-refit and only this vehicle including artic parts. @@ -1752,7 +1752,7 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station * of 0 for all cargoes. */ if (_returned_refit_capacity > 0 && (consist_capleft[cid] < consist_capleft[new_cid] || (consist_capleft[cid] == consist_capleft[new_cid] && - st->goods[cid].cargo.AvailableCount() > st->goods[new_cid].cargo.AvailableCount()))) { + st->goods[cid].data->cargo.AvailableCount() > st->goods[new_cid].CargoAvailableCount()))) { new_cid = cid; } } @@ -1809,7 +1809,7 @@ struct ReserveCargoAction { if (!(flags & OLFB_FULL_LOAD) && !through_load) return true; } if (v->cargo_cap > v->cargo.RemainingCount() && MayLoadUnderExclusiveRights(st, v)) { - st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), + st->goods[v->cargo_type].CreateData().cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), &v->cargo, st->xy, next_station.Get(v->cargo_type)); } @@ -2022,6 +2022,7 @@ static void LoadUnloadVehicle(Vehicle *front) artic_part++; GoodsEntry *ge = &st->goods[v->cargo_type]; + GoodsEntryData &ged = ge->CreateData(); if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && payment == nullptr) { /* Once the payment has been made, never attempt to unload again */ @@ -2046,7 +2047,7 @@ static void LoadUnloadVehicle(Vehicle *front) uint new_remaining = v->cargo.RemainingCount() + v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER); if (v->cargo_cap < new_remaining) { /* Return some of the reserved cargo to not overload the vehicle. */ - v->cargo.Return(new_remaining - v->cargo_cap, &ge->cargo, INVALID_STATION); + v->cargo.Return(new_remaining - v->cargo_cap, &ged.cargo, INVALID_STATION); } /* Keep instead of delivering. This may lead to no cargo being unloaded, so ...*/ @@ -2073,7 +2074,7 @@ static void LoadUnloadVehicle(Vehicle *front) } } - amount_unloaded = v->cargo.Unload(amount_unloaded, &ge->cargo, payment); + amount_unloaded = v->cargo.Unload(amount_unloaded, &ged.cargo, payment); remaining = v->cargo.UnloadCount() > 0; if (amount_unloaded > 0) { dirty_vehicle = true; @@ -2143,11 +2144,11 @@ static void LoadUnloadVehicle(Vehicle *front) /* If there's goods waiting at the station, and the vehicle * has capacity for it, load it on the vehicle. */ - if ((v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0 || ge->cargo.AvailableCount() > 0) && MayLoadUnderExclusiveRights(st, v)) { + if ((v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0 || ged.cargo.AvailableCount() > 0) && MayLoadUnderExclusiveRights(st, v)) { if (v->cargo.StoredCount() == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO); if (_settings_game.order.gradual_loading) cap_left = std::min(cap_left, GetLoadAmount(v)); - uint loaded = ge->cargo.Load(cap_left, &v->cargo, st->xy, next_station.Get(v->cargo_type)); + uint loaded = ged.cargo.Load(cap_left, &v->cargo, st->xy, next_station.Get(v->cargo_type)); if (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) { /* Remember if there are reservations left so that we don't stop * loading before they're loaded. */ @@ -2175,7 +2176,7 @@ static void LoadUnloadVehicle(Vehicle *front) st->time_since_load = 0; ge->last_vehicle_type = v->type; - if (ge->cargo.TotalCount() == 0) { + if (ged.cargo.TotalCount() == 0) { TriggerStationRandomisation(st, st->xy, SRT_CARGO_TAKEN, v->cargo_type); TriggerStationAnimation(st, st->xy, SAT_CARGO_TAKEN, v->cargo_type); AirportAnimationTrigger(st, AAT_STATION_CARGO_TAKEN, v->cargo_type); diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index dbf8a8ef35..cfccf7214f 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -172,7 +172,7 @@ void LinkGraphOverlay::RebuildCache(bool incremental) item->to_pt = to_pt; } this->AddStats(c, lg.Monthly(edge.Capacity()), lg.Monthly(edge.Usage()), - ge.flows.GetFlowVia(to->index), + ge.data != nullptr ? ge.data->flows.GetFlowVia(to->index) : 0, edge.TravelTime(), from->owner == OWNER_NONE || to->owner == OWNER_NONE, item->prop); diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 7f798459f6..2233b9124f 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -121,6 +121,7 @@ void LinkGraphJob::FinaliseJob() LinkGraph *lg = LinkGraph::Get(ge.link_graph); FlowStatMap &flows = from.Flows(); + FlowStatMap &geflows = ge.CreateData().flows; for (Edge &edge : from.GetEdges()) { if (edge.Flow() == 0) continue; @@ -135,7 +136,7 @@ void LinkGraphJob::FinaliseJob() /* Delete old flows for source stations which have been deleted * from the new flows. This avoids flow cycles between old and * new flows. */ - while (!erased.IsEmpty()) ge.flows.erase(erased.Pop()); + while (!erased.IsEmpty()) geflows.erase(erased.Pop()); } else if (lg_edge.LastUnrestrictedUpdate() == INVALID_DATE) { /* Edge is fully restricted. */ flows.RestrictFlows(to); @@ -146,7 +147,7 @@ void LinkGraphJob::FinaliseJob() * really delete them as we could then end up with unroutable cargo * somewhere. Do delete them and also reroute relevant cargo if * automatic distribution has been turned off for that cargo. */ - for (FlowStatMap::iterator it(ge.flows.begin()); it != ge.flows.end();) { + for (FlowStatMap::iterator it(geflows.begin()); it != geflows.end();) { FlowStatMap::iterator new_it = flows.find(it->GetOrigin()); if (new_it == flows.end()) { if (_settings_game.linkgraph.GetDistributionType(this->Cargo()) != DT_MANUAL) { @@ -154,7 +155,7 @@ void LinkGraphJob::FinaliseJob() NodeID origin = it->GetOrigin(); FlowStat shares(INVALID_STATION, INVALID_STATION, 1); it->SwapShares(shares); - it = ge.flows.erase(it); + it = geflows.erase(it); for (FlowStat::const_iterator shares_it(shares.begin()); shares_it != shares.end(); ++shares_it) { RerouteCargoFromSource(st, this->Cargo(), origin, shares_it->second, st->index); @@ -165,7 +166,7 @@ void LinkGraphJob::FinaliseJob() } else { FlowStat shares(INVALID_STATION, INVALID_STATION, 1); it->SwapShares(shares); - it = ge.flows.erase(it); + it = geflows.erase(it); for (FlowStat::const_iterator shares_it(shares.begin()); shares_it != shares.end(); ++shares_it) { RerouteCargo(st, this->Cargo(), shares_it->second, st->index); @@ -178,9 +179,9 @@ void LinkGraphJob::FinaliseJob() } } for (FlowStatMap::iterator it(flows.begin()); it != flows.end(); ++it) { - ge.flows.insert(std::move(*it)); + geflows.insert(std::move(*it)); } - ge.flows.SortStorage(); + geflows.SortStorage(); InvalidateWindowData(WC_STATION_VIEW, st->index, this->Cargo()); } } diff --git a/src/newgrf_roadstop.cpp b/src/newgrf_roadstop.cpp index 00c93e0099..2faf2bce6d 100644 --- a/src/newgrf_roadstop.cpp +++ b/src/newgrf_roadstop.cpp @@ -271,7 +271,7 @@ RoadStopResolverObject::RoadStopResolverObject(const RoadStopSpec *roadstopspec, /* Pick the first cargo that we have waiting */ for (const CargoSpec *cs : CargoSpec::Iterate()) { if (roadstopspec->grf_prop.spritegroup[cs->Index()] != nullptr && - station->goods[cs->Index()].cargo.TotalCount() > 0) { + station->goods[cs->Index()].CargoTotalCount() > 0) { ctype = cs->Index(); break; } @@ -474,7 +474,7 @@ void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTri if (trigger == RSRT_CARGO_TAKEN) { /* Create a bitmask of completely empty cargo types to be matched */ for (CargoID i = 0; i < NUM_CARGO; i++) { - if (st->goods[i].cargo.TotalCount() == 0) { + if (st->goods[i].CargoTotalCount() == 0) { SetBit(empty_mask, i); } } @@ -699,7 +699,7 @@ void DumpRoadStopSpriteGroup(const BaseStation *st, const RoadStopSpec *spec, Du /* Pick the first cargo that we have waiting */ for (const CargoSpec *cs : CargoSpec::Iterate()) { if (spec->grf_prop.spritegroup[cs->Index()] != nullptr && - station->goods[cs->Index()].cargo.TotalCount() > 0) { + station->goods[cs->Index()].CargoTotalCount() > 0) { ctype = cs->Index(); break; } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index fa1b6ea23f..4489e13326 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -447,10 +447,10 @@ uint32 Station::GetNewGRFVariable(const ResolverObject &object, uint16 variable, const GoodsEntry *ge = &this->goods[c]; switch (variable) { - case 0x60: return std::min(ge->cargo.TotalCount(), 4095); + case 0x60: return std::min(ge->CargoTotalCount(), 4095); case 0x61: return ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->time_since_pickup : 0; case 0x62: return ge->HasRating() ? ge->rating : 0xFFFFFFFF; - case 0x63: return ge->cargo.DaysInTransit(); + case 0x63: return ge->data != nullptr ? ge->data->cargo.DaysInTransit() : 0; case 0x64: return ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->last_speed | (ge->last_age << 8) : 0xFF00; case 0x65: return GB(ge->status, GoodsEntry::GES_ACCEPTANCE, 1) << 3; case 0x69: { @@ -466,12 +466,12 @@ uint32 Station::GetNewGRFVariable(const ResolverObject &object, uint16 variable, if (variable >= 0x8C && variable <= 0xEC) { const GoodsEntry *g = &this->goods[GB(variable - 0x8C, 3, 4)]; switch (GB(variable - 0x8C, 0, 3)) { - case 0: return g->cargo.TotalCount(); - case 1: return GB(std::min(g->cargo.TotalCount(), 4095u), 0, 4) | (GB(g->status, GoodsEntry::GES_ACCEPTANCE, 1) << 7); + case 0: return g->CargoTotalCount(); + case 1: return GB(std::min(g->CargoTotalCount(), 4095u), 0, 4) | (GB(g->status, GoodsEntry::GES_ACCEPTANCE, 1) << 7); case 2: return g->time_since_pickup; case 3: return g->rating; - case 4: return g->cargo.Source(); - case 5: return g->cargo.DaysInTransit(); + case 4: return g->data != nullptr ? g->data->cargo.Source() : INVALID_STATION; + case 5: return g->data != nullptr ? g->data->cargo.DaysInTransit() : 0; case 6: return g->last_speed; case 7: return g->last_age; } @@ -533,12 +533,12 @@ uint32 Waypoint::GetNewGRFVariable(const ResolverObject &object, uint16 variable case CT_DEFAULT: for (CargoID cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) { - cargo += st->goods[cargo_type].cargo.TotalCount(); + cargo += st->goods[cargo_type].CargoTotalCount(); } break; default: - cargo = st->goods[this->station_scope.cargo_type].cargo.TotalCount(); + cargo = st->goods[this->station_scope.cargo_type].CargoTotalCount(); break; } @@ -598,7 +598,7 @@ StationResolverObject::StationResolverObject(const StationSpec *statspec, BaseSt /* Pick the first cargo that we have waiting */ for (const CargoSpec *cs : CargoSpec::Iterate()) { if (this->station_scope.statspec->grf_prop.spritegroup[cs->Index()] != nullptr && - st->goods[cs->Index()].cargo.TotalCount() > 0) { + st->goods[cs->Index()].CargoTotalCount() > 0) { ctype = cs->Index(); break; } @@ -1006,7 +1006,7 @@ void TriggerStationRandomisation(Station *st, TileIndex trigger_tile, StationRan if (trigger == SRT_CARGO_TAKEN) { /* Create a bitmask of completely empty cargo types to be matched */ for (CargoID i = 0; i < NUM_CARGO; i++) { - if (st->goods[i].cargo.TotalCount() == 0) { + if (st->goods[i].CargoTotalCount() == 0) { SetBit(empty_mask, i); } } diff --git a/src/openttd.cpp b/src/openttd.cpp index 314e49f812..c29678f2dc 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1916,14 +1916,16 @@ void CheckCaches(bool force_check, std::function log, CheckC for (Station *st : Station::Iterate()) { for (CargoID c = 0; c < NUM_CARGO; c++) { - uint old_count = st->goods[c].cargo.TotalCount(); - uint64 old_cargo_days_in_transit = st->goods[c].cargo.CargoDaysInTransit(); + if (st->goods[c].data == nullptr) continue; - st->goods[c].cargo.InvalidateCache(); + uint old_count = st->goods[c].data->cargo.TotalCount(); + uint64 old_cargo_days_in_transit = st->goods[c].data->cargo.CargoDaysInTransit(); + + st->goods[c].data->cargo.InvalidateCache(); uint changed = 0; - if (st->goods[c].cargo.TotalCount() != old_count) SetBit(changed, 0); - if (st->goods[c].cargo.CargoDaysInTransit() != old_cargo_days_in_transit) SetBit(changed, 1); + if (st->goods[c].data->cargo.TotalCount() != old_count) SetBit(changed, 0); + if (st->goods[c].data->cargo.CargoDaysInTransit() != old_cargo_days_in_transit) SetBit(changed, 1); if (changed != 0) { CCLOG("station cargo cache mismatch: station %i, company %i, cargo %u: %c%c", st->index, (int)st->owner, c, diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 9e6d60f44c..4217947e24 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -3097,16 +3097,16 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v, Pro case OCV_UNCONDITIONALLY: skip_order = true; break; case OCV_CARGO_WAITING: { StationID next_station = GetNextRealStation(v, order); - if (Station::IsValidID(next_station)) skip_order = OrderConditionCompare(occ, (Station::Get(next_station)->goods[value].cargo.AvailableCount() > 0), value); + if (Station::IsValidID(next_station)) skip_order = OrderConditionCompare(occ, (Station::Get(next_station)->goods[value].CargoAvailableCount() > 0), value); break; } case OCV_CARGO_WAITING_AMOUNT: { StationID next_station = GetNextRealStation(v, order); if (Station::IsValidID(next_station)) { if (GB(order->GetXData(), 16, 16) == 0) { - skip_order = OrderConditionCompare(occ, Station::Get(next_station)->goods[value].cargo.AvailableCount(), GB(order->GetXData(), 0, 16)); + skip_order = OrderConditionCompare(occ, Station::Get(next_station)->goods[value].CargoAvailableCount(), GB(order->GetXData(), 0, 16)); } else { - skip_order = OrderConditionCompare(occ, Station::Get(next_station)->goods[value].cargo.AvailableViaCount(GB(order->GetXData(), 16, 16) - 2), GB(order->GetXData(), 0, 16)); + skip_order = OrderConditionCompare(occ, Station::Get(next_station)->goods[value].CargoAvailableViaCount(GB(order->GetXData(), 16, 16) - 2), GB(order->GetXData(), 0, 16)); } } break; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index c8ffdc2696..4ba66a6f42 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2051,7 +2051,7 @@ bool AfterLoadGame() for (Station *st : Station::Iterate()) { for (CargoID c = 0; c < NUM_CARGO; c++) { st->goods[c].last_speed = 0; - if (st->goods[c].cargo.AvailableCount() != 0) SetBit(st->goods[c].status, GoodsEntry::GES_RATING); + if (st->goods[c].CargoAvailableCount() != 0) SetBit(st->goods[c].status, GoodsEntry::GES_RATING); } } } diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 5e91a863ad..2c62782921 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -40,6 +40,7 @@ static Money _cargo_feeder_share; CargoPacketList _packets; uint32 _old_num_dests; +uint _cargo_reserved_count; struct FlowSaveLoad { FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {} @@ -61,7 +62,7 @@ static byte _old_last_vehicle_type; */ static void SwapPackets(GoodsEntry *ge) { - StationCargoPacketMap &ge_packets = const_cast(*ge->cargo.Packets()); + StationCargoPacketMap &ge_packets = const_cast(*ge->data->cargo.Packets()); if (_packets.empty()) { std::map::iterator it(ge_packets.find(INVALID_STATION)); @@ -153,14 +154,15 @@ public: StationCargoPair pair; for (uint j = 0; j < num_dests; ++j) { SlObject(&pair, this->GetLoadDescription()); - const_cast(*(ge->cargo.Packets()))[pair.first].swap(pair.second); + const_cast(*(ge->data->cargo.Packets()))[pair.first].swap(pair.second); assert(pair.second.empty()); } } void FixPointers(GoodsEntry *ge) const override { - for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) { + if (ge->data == nullptr) return; + for (StationCargoPacketMap::ConstMapIterator it = ge->data->cargo.Packets()->begin(); it != ge->data->cargo.Packets()->end(); ++it) { SlObject(const_cast(&(*it)), this->GetDescription()); } } @@ -192,7 +194,7 @@ public: for (uint32 j = 0; j < num_flows; ++j) { SlObject(&flow, this->GetLoadDescription()); if (fs == nullptr || prev_source != flow.source) { - fs = &(*(ge->flows.insert(ge->flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); + fs = &(*(ge->data->flows.insert(ge->data->flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); } else { fs->AppendShare(flow.via, flow.share, flow.restricted); } @@ -229,7 +231,7 @@ public: SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), SLEG_CONDREFRING("packets", _packets, REF_CARGO_PACKET, SLV_68, SLV_183), SLEG_CONDVAR("old_num_dests", _old_num_dests, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), - SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), + SLEG_CONDVAR("cargo.reserved_count", _cargo_reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION), SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION), SLEG_CONDVAR("old_num_flows", _old_num_flows, SLE_UINT32, SLV_183, SLV_SAVELOAD_LIST_LENGTH), @@ -258,19 +260,15 @@ public: void Save(BaseStation *bst) const override { - Station *st = Station::From(bst); - - SlSetStructListLength(NUM_CARGO); - - for (CargoID i = 0; i < NUM_CARGO; i++) { - SlObject(&st->goods[i], this->GetDescription()); - } + NOT_REACHED(); } void Load(BaseStation *bst) const override { Station *st = Station::From(bst); + std::unique_ptr spare_ged; + /* Before savegame version 161, persistent storages were not stored in a pool. */ if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_145) && st->facilities & FACIL_AIRPORT) { /* Store the old persistent storage. The GRFID will be added later. */ @@ -282,7 +280,15 @@ public: size_t num_cargo = this->GetNumCargo(); for (size_t i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; + if (ge->data == nullptr) { + if (spare_ged != nullptr) { + ge->data = std::move(spare_ged); + } else { + ge->data.reset(new GoodsEntryData()); + } + } SlObject(ge, this->GetLoadDescription()); + if (!IsSavegameVersionBefore(SLV_181)) ge->data->cargo.LoadSetReservedCount(_cargo_reserved_count); if (IsSavegameVersionBefore(SLV_183)) { SwapPackets(ge); } @@ -300,10 +306,13 @@ public: /* Don't construct the packet with station here, because that'll fail with old savegames */ CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share); - ge->cargo.Append(cp, INVALID_STATION); + ge->data->cargo.Append(cp, INVALID_STATION); SB(ge->status, GoodsEntry::GES_RATING, 1, 1); } } + if (ge->data->MayBeRemoved()) { + spare_ged = std::move(ge->data); + } } } diff --git a/src/script/api/script_station.cpp b/src/script/api/script_station.cpp index b2955fb814..cce7ada021 100644 --- a/src/script/api/script_station.cpp +++ b/src/script/api/script_station.cpp @@ -58,7 +58,10 @@ template return -1; } - const StationCargoList &cargo_list = ::Station::Get(station_id)->goods[cargo_id].cargo; + const GoodsEntry &ge = ::Station::Get(station_id)->goods[cargo_id]; + if (ge.data == nullptr) return 0; + + const StationCargoList &cargo_list = ge.data->cargo; if (!Tfrom && !Tvia) return cargo_list.TotalCount(); uint16 cargo_count = 0; @@ -106,7 +109,10 @@ template return -1; } - const FlowStatMap &flows = ::Station::Get(station_id)->goods[cargo_id].flows; + const GoodsEntry &ge = ::Station::Get(station_id)->goods[cargo_id]; + if (ge.data == nullptr) return 0; + + const FlowStatMap &flows = ge.data->flows; if (Tfrom) { return Tvia ? flows.GetFlowFromVia(from_station_id, via_station_id) : flows.GetFlowFrom(from_station_id); diff --git a/src/script/api/script_stationlist.cpp b/src/script/api/script_stationlist.cpp index a1de49ae05..7151e7c117 100644 --- a/src/script/api/script_stationlist.cpp +++ b/src/script/api/script_stationlist.cpp @@ -171,8 +171,11 @@ void ScriptStationList_CargoWaiting::Add(StationID station_id, CargoID cargo, St CargoCollector collector(this, station_id, cargo, other_station); if (collector.GE() == nullptr) return; - StationCargoList::ConstIterator iter = collector.GE()->cargo.Packets()->begin(); - StationCargoList::ConstIterator end = collector.GE()->cargo.Packets()->end(); + const GoodsEntry *ge = collector.GE(); + if (ge->data == nullptr) return; + + StationCargoList::ConstIterator iter = ge->data->cargo.Packets()->begin(); + StationCargoList::ConstIterator end = ge->data->cargo.Packets()->end(); for (; iter != end; ++iter) { collector.Update((*iter)->SourceStation(), iter.GetKey(), (*iter)->Count()); } @@ -185,8 +188,11 @@ void ScriptStationList_CargoPlanned::Add(StationID station_id, CargoID cargo, St CargoCollector collector(this, station_id, cargo, other_station); if (collector.GE() == nullptr) return; - FlowStatMap::const_iterator iter = collector.GE()->flows.begin(); - FlowStatMap::const_iterator end = collector.GE()->flows.end(); + const GoodsEntry *ge = collector.GE(); + if (ge->data == nullptr) return; + + FlowStatMap::const_iterator iter = ge->data->flows.begin(); + FlowStatMap::const_iterator end = ge->data->flows.end(); for (; iter != end; ++iter) { uint prev = 0; for (FlowStat::const_iterator flow_iter = iter->begin(); @@ -209,8 +215,11 @@ ScriptStationList_CargoWaitingViaByFrom::ScriptStationList_CargoWaitingViaByFrom CargoCollector collector(this, station_id, cargo, via); if (collector.GE() == nullptr) return; + const GoodsEntry *ge = collector.GE(); + if (ge->data == nullptr) return; + std::pair range = - collector.GE()->cargo.Packets()->equal_range(via); + ge->data->cargo.Packets()->equal_range(via); for (StationCargoList::ConstIterator iter = range.first; iter != range.second; ++iter) { collector.Update((*iter)->SourceStation(), iter.GetKey(), (*iter)->Count()); } @@ -255,8 +264,11 @@ ScriptStationList_CargoPlannedFromByVia::ScriptStationList_CargoPlannedFromByVia CargoCollector collector(this, station_id, cargo, from); if (collector.GE() == nullptr) return; - FlowStatMap::const_iterator iter = collector.GE()->flows.find(from); - if (iter == collector.GE()->flows.end()) return; + const GoodsEntry *ge = collector.GE(); + if (ge->data == nullptr) return; + + FlowStatMap::const_iterator iter = ge->data->flows.find(from); + if (iter == ge->data->flows.end()) return; uint prev = 0; for (FlowStat::const_iterator flow_iter = iter->begin(); flow_iter != iter->end(); ++flow_iter) { diff --git a/src/sl/cargopacket_sl.cpp b/src/sl/cargopacket_sl.cpp index 69ad66fec4..01267eff28 100644 --- a/src/sl/cargopacket_sl.cpp +++ b/src/sl/cargopacket_sl.cpp @@ -49,7 +49,8 @@ extern btree::btree_map _cargo_packet_deferred_payments; for (CargoID c = 0; c < NUM_CARGO; c++) { GoodsEntry *ge = &st->goods[c]; - const StationCargoPacketMap *packets = ge->cargo.Packets(); + if (ge->data == nullptr) continue; + const StationCargoPacketMap *packets = ge->data->cargo.Packets(); for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) { CargoPacket *cp = *it; cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : st->xy; @@ -73,7 +74,9 @@ extern btree::btree_map _cargo_packet_deferred_payments; for (Vehicle *v : Vehicle::Iterate()) v->cargo.InvalidateCache(); for (Station *st : Station::Iterate()) { - for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache(); + for (CargoID c = 0; c < NUM_CARGO; c++) { + if (st->goods[c].data != nullptr) st->goods[c].data->cargo.InvalidateCache(); + } } } @@ -95,7 +98,7 @@ extern btree::btree_map _cargo_packet_deferred_payments; Station *st = Station::Get(v->First()->last_station_visited); assert_msg(st != nullptr, "%s", scope_dumper().VehicleInfo(v)); for (CargoPacket *cp : iter.second) { - st->goods[v->cargo_type].cargo.AfterLoadIncreaseReservationCount(cp->count); + st->goods[v->cargo_type].CreateData().cargo.AfterLoadIncreaseReservationCount(cp->count); v->cargo.Append(cp, VehicleCargoList::MTA_LOAD); } } diff --git a/src/sl/oldloader_sl.cpp b/src/sl/oldloader_sl.cpp index 1d581316fd..cb072811fb 100644 --- a/src/sl/oldloader_sl.cpp +++ b/src/sl/oldloader_sl.cpp @@ -706,7 +706,7 @@ static bool LoadOldGood(LoadgameState *ls, int num) SB(ge->status, GoodsEntry::GES_ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); SB(ge->status, GoodsEntry::GES_RATING, 1, _cargo_source != 0xFF); if (GB(_waiting_acceptance, 0, 12) != 0 && CargoPacket::CanAllocateItem()) { - ge->cargo.Append(new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, 0, 0), + ge->CreateData().cargo.Append(new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, 0, 0), INVALID_STATION); } diff --git a/src/sl/station_sl.cpp b/src/sl/station_sl.cpp index 4c9ed072a9..5816191f23 100644 --- a/src/sl/station_sl.cpp +++ b/src/sl/station_sl.cpp @@ -237,6 +237,7 @@ static uint16 _cargo_source; static uint32 _cargo_source_xy; static uint8 _cargo_days; static Money _cargo_feeder_share; +static uint _cargo_reserved_count; static const SaveLoad _station_speclist_desc[] = { SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION), @@ -295,7 +296,7 @@ SaveLoadTable GetGoodsDesc() SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION), SLEG_CONDPTRRING_X( _packets, REF_CARGO_PACKET, SLV_68, SLV_183, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, 0, 0)), SLEG_CONDVAR_X( _num_dests, SLE_UINT32, SLV_183, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)), - SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION), + SLEG_CONDVAR( _cargo_reserved_count,SLE_UINT, SLV_181, SL_MAX_VERSION), SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION), SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION), SLEG_CONDVAR( _num_flows, SLE_UINT32, SLV_183, SL_MAX_VERSION), @@ -321,7 +322,9 @@ static const SaveLoad _cargo_list_desc[] = { */ static void SwapPackets(GoodsEntry *ge) { - StationCargoPacketMap &ge_packets = const_cast(*ge->cargo.Packets()); + if (_packets.empty() && ge->data == nullptr) return; + + StationCargoPacketMap &ge_packets = const_cast(*ge->CreateData().cargo.Packets()); if (_packets.empty()) { std::map::iterator it(ge_packets.find(INVALID_STATION)); @@ -342,6 +345,7 @@ static void Load_STNS() _cargo_days = 0; _cargo_feeder_share = 0; _num_specs = 0; + _cargo_reserved_count = 0; uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; int index; @@ -355,6 +359,7 @@ static void Load_STNS() for (CargoID i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; SlObject(ge, GetGoodsDesc()); + if (_cargo_reserved_count) ge->CreateData().cargo.LoadSetReservedCount(_cargo_reserved_count); SwapPackets(ge); if (IsSavegameVersionBefore(SLV_68)) { SB(ge->status, GoodsEntry::GES_ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15)); @@ -370,7 +375,7 @@ static void Load_STNS() /* Don't construct the packet with station here, because that'll fail with old savegames */ CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share); - ge->cargo.Append(cp, INVALID_STATION); + ge->CreateData().cargo.Append(cp, INVALID_STATION); SB(ge->status, GoodsEntry::GES_RATING, 1, 1); } } @@ -544,10 +549,19 @@ static void RealSave_STNN(BaseStation *bst) if (!waypoint) { Station *st = Station::From(bst); for (CargoID i = 0; i < NUM_CARGO; i++) { - _num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize(); - _num_flows = (uint32)st->goods[i].flows.size(); + const GoodsEntryData *ged = st->goods[i].data.get(); + if (ged != nullptr) { + _cargo_reserved_count = ged->cargo.ReservedCount(); + _num_dests = (uint32)ged->cargo.Packets()->MapSize(); + _num_flows = (uint32)ged->flows.size(); + } else { + _cargo_reserved_count = 0; + _num_dests = 0; + _num_flows = 0; + } SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc); - for (FlowStatMap::const_iterator outer_it(st->goods[i].flows.begin()); outer_it != st->goods[i].flows.end(); ++outer_it) { + if (ged == nullptr) continue; + for (FlowStatMap::const_iterator outer_it(ged->flows.begin()); outer_it != ged->flows.end(); ++outer_it) { uint32 sum_shares = 0; FlowSaveLoad flow; flow.source = outer_it->GetOrigin(); @@ -571,7 +585,7 @@ static void RealSave_STNN(BaseStation *bst) } SlWriteUint16(outer_it->GetRawFlags()); } - for (StationCargoPacketMap::ConstMapIterator it(st->goods[i].cargo.Packets()->begin()); it != st->goods[i].cargo.Packets()->end(); ++it) { + for (StationCargoPacketMap::ConstMapIterator it(ged->cargo.Packets()->begin()); it != ged->cargo.Packets()->end(); ++it) { SlObjectSaveFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals } } @@ -620,6 +634,9 @@ static void Load_STNN() _num_specs = 0; _num_roadstop_specs = 0; _num_roadstop_custom_tiles = 0; + _cargo_reserved_count = 0; + + std::unique_ptr spare_ged; const uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO; ReadBuffer *buffer = ReadBuffer::GetCurrent(); @@ -643,10 +660,19 @@ static void Load_STNN() } for (CargoID i = 0; i < num_cargo; i++) { - SlObjectLoadFiltered(&st->goods[i], _filtered_goods_desc); + GoodsEntry &ge = st->goods[i]; + if (ge.data == nullptr) { + if (spare_ged != nullptr) { + ge.data = std::move(spare_ged); + } else { + ge.data.reset(new GoodsEntryData()); + } + } + SlObjectLoadFiltered(&ge, _filtered_goods_desc); + ge.data->cargo.LoadSetReservedCount(_cargo_reserved_count); StationID prev_source = INVALID_STATION; if (SlXvIsFeaturePresent(XSLFI_FLOW_STAT_FLAGS)) { - st->goods[i].flows.reserve(_num_flows); + ge.data->flows.reserve(_num_flows); for (uint32 j = 0; j < _num_flows; ++j) { FlowSaveLoad flow; buffer->CheckBytes(2 + 4); @@ -657,7 +683,7 @@ static void Load_STNN() flow.via = buffer->RawReadUint16(); flow.share = buffer->RawReadUint32(); flow.restricted = (buffer->RawReadByte() != 0); - FlowStat *fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); + FlowStat *fs = &(*(ge.data->flows.insert(ge.data->flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); for (uint32 k = 1; k < flow_count; ++k) { buffer->CheckBytes(2 + 4 + 1); flow.via = buffer->RawReadUint16(); @@ -679,7 +705,7 @@ static void Load_STNN() if (!IsSavegameVersionBefore(SLV_187)) flow.restricted = (buffer->ReadByte() != 0); if (fs == nullptr || prev_source != flow.source) { - fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); + fs = &(*(ge.data->flows.insert(ge.data->flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); } else { fs->AppendShare(flow.via, flow.share, flow.restricted); } @@ -687,7 +713,7 @@ static void Load_STNN() } } if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) { - SwapPackets(&st->goods[i]); + SwapPackets(&ge); } else { if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) { SlSkipBytes(8); @@ -701,11 +727,14 @@ static void Load_STNN() StationCargoPair pair; for (uint j = 0; j < _num_dests; ++j) { SlObjectLoadFiltered(&pair, _cargo_list_desc); // _cargo_list_desc has no conditionals - const_cast(*(st->goods[i].cargo.Packets()))[pair.first].swap(pair.second); + const_cast(*(ge.data->cargo.Packets()))[pair.first].swap(pair.second); assert(pair.second.empty()); } } - if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) st->goods[i].last_vehicle_type = _old_last_vehicle_type; + if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) ge.last_vehicle_type = _old_last_vehicle_type; + if (ge.data->MayBeRemoved()) { + spare_ged = std::move(ge.data); + } } st->station_cargo_history.resize(CountBits(st->station_cargo_history_cargoes)); @@ -780,8 +809,10 @@ static void Ptrs_STNN() SwapPackets(ge); } else { //SlObject(ge, GetGoodsDesc()); - for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) { - SlObjectPtrOrNullFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals + if (ge->data != nullptr) { + for (StationCargoPacketMap::ConstMapIterator it = ge->data->cargo.Packets()->begin(); it != ge->data->cargo.Packets()->end(); ++it) { + SlObjectPtrOrNullFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals + } } } } diff --git a/src/station.cpp b/src/station.cpp index 96a713a52b..a2b45fe51c 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -42,6 +42,8 @@ std::array _extra_station_names; uint _extra_station_names_used; uint8 _extra_station_names_probability; +const StationCargoList _empty_cargo_list{}; +const FlowStatMap _empty_flows{}; StationKdtree _station_kdtree(Kdtree_StationXYFunc); @@ -93,7 +95,7 @@ Station::~Station() { if (CleaningPool()) { for (CargoID c = 0; c < NUM_CARGO; c++) { - this->goods[c].cargo.OnCleanPool(); + if (this->goods[c].data != nullptr) this->goods[c].data->cargo.OnCleanPool(); } return; } @@ -113,9 +115,10 @@ Station::~Station() for (NodeID node = 0; node < lg->Size(); ++node) { Station *st = Station::Get((*lg)[node].Station()); - st->goods[c].flows.erase(this->index); + GoodsEntryData *ged = st->goods[c].data.get(); + if (ged != nullptr) ged->flows.erase(this->index); if (lg->GetConstEdge(node, this->goods[c].node).LastUpdate() != INVALID_DATE) { - st->goods[c].flows.DeleteFlows(this->index); + if (ged != nullptr) ged->flows.DeleteFlows(this->index); RerouteCargo(st, c, this->index, st->index); } } @@ -161,7 +164,7 @@ Station::~Station() DeleteStationNews(this->index); for (CargoID c = 0; c < NUM_CARGO; c++) { - this->goods[c].cargo.Truncate(); + if (this->goods[c].data != nullptr) this->goods[c].data->cargo.Truncate(); } CargoPacket::InvalidateAllFrom(this->index); diff --git a/src/station_base.h b/src/station_base.h index 50dbf15f19..f9a8c3df9a 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -501,6 +501,16 @@ public: void SortStorage(); }; +struct GoodsEntryData { + StationCargoList cargo; ///< The cargo packets of cargo waiting in this station + FlowStatMap flows; ///< Planned flows through this station. + + bool MayBeRemoved() const + { + return this->cargo.Packets()->MapSize() == 0 && this->cargo.ReservedCount() == 0 && this->flows.empty(); + } +}; + /** * Stores station stats for a single cargo. */ @@ -597,11 +607,12 @@ struct GoodsEntry { byte last_age; byte amount_fract; ///< Fractional part of the amount in the cargo list - StationCargoList cargo; ///< The cargo packets of cargo waiting in this station + + std::unique_ptr data; LinkGraphID link_graph; ///< Link graph this station belongs to. NodeID node; ///< ID of node in link graph referring to this goods entry. - FlowStatMap flows; ///< Planned flows through this station. + uint max_waiting_cargo; ///< Max cargo from this station waiting at any station. bool IsSupplyAllowed() const @@ -632,8 +643,10 @@ struct GoodsEntry { */ inline StationID GetVia(StationID source) const { - FlowStatMap::const_iterator flow_it(this->flows.find(source)); - return flow_it != this->flows.end() ? flow_it->GetVia() : INVALID_STATION; + if (this->data == nullptr) return INVALID_STATION; + + FlowStatMap::const_iterator flow_it(this->data->flows.find(source)); + return flow_it != this->data->flows.end() ? flow_it->GetVia() : INVALID_STATION; } /** @@ -646,8 +659,54 @@ struct GoodsEntry { */ inline StationID GetVia(StationID source, StationID excluded, StationID excluded2 = INVALID_STATION) const { - FlowStatMap::const_iterator flow_it(this->flows.find(source)); - return flow_it != this->flows.end() ? flow_it->GetVia(excluded, excluded2) : INVALID_STATION; + if (this->data == nullptr) return INVALID_STATION; + + FlowStatMap::const_iterator flow_it(this->data->flows.find(source)); + return flow_it != this->data->flows.end() ? flow_it->GetVia(excluded, excluded2) : INVALID_STATION; + } + + GoodsEntryData &CreateData() + { + if (this->data == nullptr) this->data.reset(new GoodsEntryData()); + return *this->data; + } + + const GoodsEntryData &CreateData() const + { + if (this->data == nullptr) const_cast(this)->data.reset(new GoodsEntryData()); + return *this->data; + } + + inline uint CargoAvailableCount() const + { + return this->data != nullptr ? this->data->cargo.AvailableCount() : 0; + } + + inline uint CargoReservedCount() const + { + return this->data != nullptr ? this->data->cargo.ReservedCount() : 0; + } + + inline uint CargoTotalCount() const + { + return this->data != nullptr ? this->data->cargo.TotalCount() : 0; + } + + inline uint CargoAvailableViaCount(StationID next) const + { + return this->data != nullptr ? this->data->cargo.AvailableViaCount(next) : 0; + } + + const StationCargoList &ConstCargoList() const + { + extern const StationCargoList _empty_cargo_list; + return this->data != nullptr ? this->data->cargo : _empty_cargo_list; + } + + const FlowStatMap &ConstFlows() const + { + extern const FlowStatMap _empty_flows; + return this->data != nullptr ? this->data->flows : _empty_flows; } }; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index de25fb0d1e..7b89aac15d 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -469,7 +469,7 @@ void Station::UpdateCargoHistory() uint storage_offset = 0; bool update_window = false; for (const CargoSpec *cs : CargoSpec::Iterate()) { - uint amount = this->goods[cs->Index()].cargo.TotalCount(); + uint amount = this->goods[cs->Index()].CargoTotalCount(); if (!HasBit(this->station_cargo_history_cargoes, cs->Index())) { if (amount == 0) { /* No cargo present, and no history stored for this cargo, no work to do */ @@ -4115,8 +4115,10 @@ static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UIN /* If truncating also punish the source stations' ratings to * decrease the flow of incoming cargo. */ + if (ge->data == nullptr) return; + StationCargoAmountMap waiting_per_source; - ge->cargo.Truncate(amount, &waiting_per_source); + ge->data->cargo.Truncate(amount, &waiting_per_source); for (StationCargoAmountMap::iterator i(waiting_per_source.begin()); i != waiting_per_source.end(); ++i) { Station *source_station = Station::GetIfValid(i->first); if (source_station == nullptr) continue; @@ -4290,12 +4292,12 @@ static void UpdateStationRating(Station *st) { int rating = GetTargetRating(st, cs, ge); - uint waiting = ge->cargo.AvailableCount(); + uint waiting = ge->CargoAvailableCount(); /* num_dests is at least 1 if there is any cargo as * INVALID_STATION is also a destination. */ - const uint num_dests = (uint)ge->cargo.Packets()->MapSize(); + const uint num_dests = ge->data != nullptr ? (uint)ge->data->cargo.Packets()->MapSize() : 0; /* Average amount of cargo per next hop, but prefer solitary stations * with only one or two next hops. They are allowed to have more @@ -4352,12 +4354,12 @@ static void UpdateStationRating(Station *st) /* We can't truncate cargo that's already reserved for loading. * Thus StoredCount() here. */ - if (waiting_changed && waiting < ge->cargo.AvailableCount()) { + if (waiting_changed && waiting < ge->CargoAvailableCount()) { /* Feed back the exact own waiting cargo at this station for the * next rating calculation. */ ge->max_waiting_cargo = 0; - TruncateCargo(cs, ge, ge->cargo.AvailableCount() - waiting); + TruncateCargo(cs, ge, ge->CargoAvailableCount() - waiting); } else { /* If the average number per next hop is low, be more forgiving. */ ge->max_waiting_cargo = waiting_avg; @@ -4388,7 +4390,7 @@ void RerouteCargo(Station *st, CargoID c, StationID avoid, StationID avoid2) GoodsEntry &ge = st->goods[c]; /* Reroute cargo in station. */ - ge.cargo.Reroute(UINT_MAX, &ge.cargo, avoid, avoid2, &ge); + if (ge.data != nullptr) ge.data->cargo.Reroute(UINT_MAX, &ge.data->cargo, avoid, avoid2, &ge); /* Reroute cargo staged to be transferred. */ for (Vehicle *v : st->loading_vehicles) { @@ -4413,7 +4415,7 @@ void RerouteCargoFromSource(Station *st, CargoID c, StationID source, StationID GoodsEntry &ge = st->goods[c]; /* Reroute cargo in station. */ - ge.cargo.RerouteFromSource(UINT_MAX, &ge.cargo, source, avoid, avoid2, &ge); + if (ge.data != nullptr) ge.data->cargo.RerouteFromSource(UINT_MAX, &ge.data->cargo, source, avoid, avoid2, &ge); /* Reroute cargo staged to be transferred. */ for (Vehicle *v : st->loading_vehicles) { @@ -4520,12 +4522,12 @@ void DeleteStaleLinks(Station *from) if (!updated) { /* If it's still considered dead remove it. */ result = LinkGraph::EdgeIterationResult::EraseEdge; - ge.flows.DeleteFlows(to->index); + if (ge.data != nullptr) ge.data->flows.DeleteFlows(to->index); RerouteCargo(from, c, to->index, from->index); } } else if (edge.LastUnrestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastUnrestrictedUpdate()) > timeout) { edge.Restrict(); - ge.flows.RestrictFlows(to->index); + if (ge.data != nullptr) ge.data->flows.RestrictFlows(to->index); RerouteCargo(from, c, to->index, from->index); } else if (edge.LastRestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastRestrictedUpdate()) > timeout) { edge.Release(); @@ -4697,7 +4699,7 @@ static uint UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceT if (amount == 0) return 0; StationID next = ge.GetVia(st->index); - ge.cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id), next); + ge.CreateData().cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id), next); LinkGraph *lg = nullptr; if (ge.link_graph == INVALID_LINK_GRAPH) { if (LinkGraph::CanAllocateItem()) { @@ -5686,7 +5688,8 @@ void DumpStationFlowStats(char *b, const char *last) for (const Station *st : Station::Iterate()) { for (CargoID i = 0; i < NUM_CARGO; i++) { const GoodsEntry &ge = st->goods[i]; - for (FlowStatMap::const_iterator it(ge.flows.begin()); it != ge.flows.end(); ++it) { + if (ge.data == nullptr) continue; + for (FlowStatMap::const_iterator it(ge.data->flows.begin()); it != ge.data->flows.end(); ++it) { count_map[(uint32)it->size()]++; invalid_map[it->GetRawFlags() & 0x1F]++; } diff --git a/src/station_gui.cpp b/src/station_gui.cpp index e8d3b8046c..5d4801c570 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -334,7 +334,7 @@ protected: int diff = 0; for (CargoID j : SetCargoBitIterator(cargo_filter)) { - diff += a->goods[j].cargo.TotalCount() - b->goods[j].cargo.TotalCount(); + diff += a->goods[j].CargoTotalCount() - b->goods[j].CargoTotalCount(); } return diff < 0; @@ -346,7 +346,7 @@ protected: int diff = 0; for (CargoID j : SetCargoBitIterator(cargo_filter)) { - diff += a->goods[j].cargo.AvailableCount() - b->goods[j].cargo.AvailableCount(); + diff += a->goods[j].CargoAvailableCount() - b->goods[j].CargoAvailableCount(); } return diff < 0; @@ -573,7 +573,7 @@ public: /* show cargo waiting and station ratings */ for (const CargoSpec *cs : _sorted_standard_cargo_specs) { CargoID cid = cs->Index(); - if (st->goods[cid].cargo.TotalCount() > 0) { + if (st->goods[cid].CargoTotalCount() > 0) { /* For RTL we work in exactly the opposite direction. So * decrement the space needed first, then draw to the left * instead of drawing to the left and then incrementing @@ -582,7 +582,7 @@ public: x -= rating_width + rating_spacing; if (x < tr.left) break; } - StationsWndShowStationRating(x, x + rating_width, tr.top, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating); + StationsWndShowStationRating(x, x + rating_width, tr.top, cid, st->goods[cid].CargoTotalCount(), st->goods[cid].rating); if (!rtl) { x += rating_width + rating_spacing; if (x > tr.right) break; @@ -1649,7 +1649,9 @@ struct StationViewWindow : public Window { CargoDataEntry *cargo_entry = cached_destinations.InsertOrRetrieve(i); cargo_entry->Clear(); - const FlowStatMap &flows = st->goods[i].flows; + if (st->goods[i].data == nullptr) return; + + const FlowStatMap &flows = st->goods[i].data->flows; for (const auto &it : flows) { StationID from = it.GetOrigin(); CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from); @@ -1680,13 +1682,17 @@ struct StationViewWindow : public Window { { if (depth <= 128 && Station::IsValidID(next) && Station::IsValidID(source)) { CargoDataEntry tmp; - const FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows; - FlowStatMap::const_iterator map_it = flowmap.find(source); - if (map_it != flowmap.end()) { - uint32 prev_count = 0; - for (FlowStat::const_iterator i = map_it->begin(); i != map_it->end(); ++i) { - tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count); - prev_count = i->first; + const GoodsEntry &ge = Station::Get(next)->goods[cargo]; + + if (ge.data != nullptr) { + const FlowStatMap &flowmap = ge.data->flows; + FlowStatMap::const_iterator map_it = flowmap.find(source); + if (map_it != flowmap.end()) { + uint32 prev_count = 0; + for (FlowStat::const_iterator i = map_it->begin(); i != map_it->end(); ++i) { + tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count); + prev_count = i->first; + } } } @@ -1807,9 +1813,9 @@ struct StationViewWindow : public Window { } if (this->current_mode == MODE_WAITING) { - this->BuildCargoList(i, st->goods[i].cargo, cargo); + this->BuildCargoList(i, st->goods[i].ConstCargoList(), cargo); } else { - this->BuildFlowList(i, st->goods[i].flows, cargo); + this->BuildFlowList(i, st->goods[i].ConstFlows(), cargo); } } } @@ -1971,8 +1977,8 @@ struct StationViewWindow : public Window { sym = "+"; } else { /* Only draw '+' if there is something to be shown. */ - const StationCargoList &list = Station::Get(this->window_number)->goods[cargo].cargo; - if (grouping == GR_CARGO && (list.ReservedCount() > 0 || cd->HasTransfers())) { + const GoodsEntry &ge = Station::Get(this->window_number)->goods[cargo]; + if (grouping == GR_CARGO && (ge.CargoReservedCount() > 0 || cd->HasTransfers())) { sym = "+"; } } diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index ae189accbc..b381c89bc8 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -1909,15 +1909,16 @@ class NIHStationStruct : public NIHelper { for (const CargoSpec *cs : CargoSpec::Iterate()) { const GoodsEntry *ge = &st->goods[cs->Index()]; - const StationCargoPacketMap *pkts = ge->cargo.Packets(); - if (pkts->empty() && ge->status == 0) { + if (ge->data == nullptr && ge->status == 0) { /* Nothing of note to show */ continue; } + const StationCargoPacketMap *pkts = ge->data != nullptr ? ge->data->cargo.Packets() : nullptr; + seprintf(buffer, lastof(buffer), " Goods entry: %u: %s", cs->Index(), GetStringPtr(cs->name)); output.print(buffer); - seprintf(buffer, lastof(buffer), " Status: %c%c%c%c%c%c%c", + char *b = buffer + seprintf(buffer, lastof(buffer), " Status: %c%c%c%c%c%c%c", HasBit(ge->status, GoodsEntry::GES_ACCEPTANCE) ? 'a' : '-', HasBit(ge->status, GoodsEntry::GES_RATING) ? 'r' : '-', HasBit(ge->status, GoodsEntry::GES_EVER_ACCEPTED) ? 'e' : '-', @@ -1925,15 +1926,17 @@ class NIHStationStruct : public NIHelper { HasBit(ge->status, GoodsEntry::GES_CURRENT_MONTH) ? 'c' : '-', HasBit(ge->status, GoodsEntry::GES_ACCEPTED_BIGTICK) ? 'b' : '-', HasBit(ge->status, GoodsEntry::GES_NO_CARGO_SUPPLY) ? 'n' : '-'); + if (ge->data != nullptr && ge->data->MayBeRemoved()) b += seprintf(b, lastof(buffer), ", (removable)"); + if (ge->data == nullptr) b += seprintf(b, lastof(buffer), ", (no data)"); output.print(buffer); if (ge->amount_fract > 0) { seprintf(buffer, lastof(buffer), " Amount fract: %u", ge->amount_fract); output.print(buffer); } - if (!pkts->empty()) { - seprintf(buffer, lastof(buffer), " Cargo packets: %u, available: %u, reserved: %u", - (uint)pkts->size(), ge->cargo.AvailableCount(), ge->cargo.ReservedCount()); + if (pkts != nullptr && pkts->MapSize() > 0) { + seprintf(buffer, lastof(buffer), " Cargo packets: %u, cargo packet keys: %u, available: %u, reserved: %u", + (uint)pkts->size(), (uint)pkts->MapSize(), ge->CargoAvailableCount(), ge->CargoReservedCount()); output.print(buffer); } if (ge->link_graph != INVALID_LINK_GRAPH) { @@ -1944,12 +1947,12 @@ class NIHStationStruct : public NIHelper { seprintf(buffer, lastof(buffer), " Max waiting cargo: %u", ge->max_waiting_cargo); output.print(buffer); } - if (ge->flows.size() > 0) { + if (ge->data != nullptr && ge->data->flows.size() > 0) { size_t total_shares = 0; - for (const FlowStat &fs : ge->flows) { + for (const FlowStat &fs : ge->data->flows) { total_shares += fs.size(); } - seprintf(buffer, lastof(buffer), " Flows: %u, total shares: %u", (uint)ge->flows.size(), (uint)total_shares); + seprintf(buffer, lastof(buffer), " Flows: %u, total shares: %u", (uint)ge->data->flows.size(), (uint)total_shares); output.print(buffer); } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 4af7dd1611..02907d9f46 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -3318,7 +3318,7 @@ void Vehicle::CancelReservation(StationID next, Station *st) VehicleCargoList &cargo = v->cargo; if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) { DEBUG(misc, 1, "cancelling cargo reservation"); - cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next); + cargo.Return(UINT_MAX, &st->goods[v->cargo_type].CreateData().cargo, next); cargo.SetTransferLoadPlace(st->xy); } cargo.KeepAll(); diff --git a/src/viewport_gui.cpp b/src/viewport_gui.cpp index 59099cdf28..28a6312532 100644 --- a/src/viewport_gui.cpp +++ b/src/viewport_gui.cpp @@ -307,7 +307,7 @@ void ShowStationViewportTooltip(Window *w, const TileIndex tile) SetDParam(0, cs->name); SetDParam(1, ToPercent8(goods_entry->rating)); SetDParam(2, cs->Index()); - SetDParam(3, goods_entry->cargo.TotalCount()); + SetDParam(3, goods_entry->CargoTotalCount()); msg += GetString(STR_STATION_VIEW_CARGO_LINE_TOOLTIP); } }