From 19835b51eef7dcf24faa3a74367472fd3af2c534 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 19 Aug 2023 21:21:31 +0100 Subject: [PATCH] Move StationCargoList and FlowStatMap out of GoodsEntry struct Move them into a new GoodsEntryData struct referenced using a std::unique_ptr from GoodsEntry. The unique_ptr may be nullptr if the cargo list and flow stat map are both empty (this is the case for unused cargoes). This reduces GoodsEntry from 128 to 24 bytes, and Station from 8680 to 2024 bytes, (on Linux x86_64). --- src/aircraft_cmd.cpp | 2 +- src/cargopacket.cpp | 14 +++--- src/cargopacket.h | 5 ++ src/economy.cpp | 21 ++++---- src/linkgraph/linkgraph_gui.cpp | 2 +- src/linkgraph/linkgraphjob.cpp | 13 ++--- src/newgrf_roadstop.cpp | 6 +-- src/newgrf_station.cpp | 20 ++++---- src/openttd.cpp | 12 +++-- src/order_cmd.cpp | 6 +-- src/saveload/afterload.cpp | 2 +- src/saveload/station_sl.cpp | 35 ++++++++----- src/script/api/script_station.cpp | 10 +++- src/script/api/script_stationlist.cpp | 26 +++++++--- src/sl/cargopacket_sl.cpp | 9 ++-- src/sl/oldloader_sl.cpp | 2 +- src/sl/station_sl.cpp | 63 ++++++++++++++++++------ src/station.cpp | 11 +++-- src/station_base.h | 71 ++++++++++++++++++++++++--- src/station_cmd.cpp | 27 +++++----- src/station_gui.cpp | 38 ++++++++------ src/table/newgrf_debug_data.h | 21 ++++---- src/vehicle.cpp | 2 +- src/viewport_gui.cpp | 2 +- 24 files changed, 283 insertions(+), 137 deletions(-) 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); } }