From 3de8853e2953b086e22c458c6ff625bb526e154c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 14 Jul 2023 11:49:11 +0100 Subject: [PATCH] Codechange: Store accepted and produced cargo in vector instead of array. Most industries do not use the full 16 slots, so this can save a little memory and iteration time. --- src/industry.h | 16 +++++----- src/industry_cmd.cpp | 57 +++++++++++++++++++++++++++-------- src/saveload/afterload.cpp | 10 ------ src/saveload/industry_sl.cpp | 37 ++++++++++++++--------- src/saveload/oldloader_sl.cpp | 37 ++++++++++++++--------- 5 files changed, 99 insertions(+), 58 deletions(-) diff --git a/src/industry.h b/src/industry.h index 2747001aea..24fe0cef8f 100644 --- a/src/industry.h +++ b/src/industry.h @@ -90,14 +90,14 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> { TimerGameEconomy::Date last_accepted; ///< Last day cargo was accepted by this industry }; - using ProducedCargoArray = std::array; - using AcceptedCargoArray = std::array; + using ProducedCargoes = std::vector; + using AcceptedCargoes = std::vector; TileArea location; ///< Location of the industry Town *town; ///< Nearest town Station *neutral_station; ///< Associated neutral station - ProducedCargoArray produced; ///< INDUSTRY_NUM_OUTPUTS production cargo slots - AcceptedCargoArray accepted; ///< INDUSTRY_NUM_INPUTS input cargo slots + ProducedCargoes produced; ///< produced cargo slots + AcceptedCargoes accepted; ///< accepted cargo slots uint8_t prod_level; ///< general production level uint16_t counter; ///< used for animation and/or production (if available cargo) @@ -166,7 +166,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> { * @param cargo CargoID to find. * @return Iterator pointing to produced cargo slot if it exists, or the end iterator. */ - inline ProducedCargoArray::iterator GetCargoProduced(CargoID cargo) + inline ProducedCargoes::iterator GetCargoProduced(CargoID cargo) { if (!IsValidCargoID(cargo)) return std::end(this->produced); return std::find_if(std::begin(this->produced), std::end(this->produced), [&cargo](const auto &p) { return p.cargo == cargo; }); @@ -177,7 +177,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> { * @param cargo CargoID to find. * @return Iterator pointing to produced cargo slot if it exists, or the end iterator. */ - inline ProducedCargoArray::const_iterator GetCargoProduced(CargoID cargo) const + inline ProducedCargoes::const_iterator GetCargoProduced(CargoID cargo) const { if (!IsValidCargoID(cargo)) return std::end(this->produced); return std::find_if(std::begin(this->produced), std::end(this->produced), [&cargo](const auto &p) { return p.cargo == cargo; }); @@ -188,7 +188,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> { * @param cargo CargoID to find. * @return Iterator pointing to accepted cargo slot if it exists, or the end iterator. */ - inline AcceptedCargoArray::iterator GetCargoAccepted(CargoID cargo) + inline AcceptedCargoes::iterator GetCargoAccepted(CargoID cargo) { if (!IsValidCargoID(cargo)) return std::end(this->accepted); return std::find_if(std::begin(this->accepted), std::end(this->accepted), [&cargo](const auto &a) { return a.cargo == cargo; }); @@ -332,4 +332,6 @@ enum IndustryDirectoryInvalidateWindowData { IDIWD_FORCE_RESORT, }; +void TrimIndustryAcceptedProduced(Industry *ind); + #endif /* INDUSTRY_H */ diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 4f1f448ca1..816e36a5e5 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -1788,15 +1788,19 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, i->type = type; Industry::IncIndustryTypeCount(type); - for (auto it = std::begin(i->produced); it != std::end(i->produced); ++it) { - size_t index = it - std::begin(i->produced); - it->cargo = indspec->produced_cargo[index]; - it->rate = indspec->production_rate[index]; + for (size_t index = 0; index < lengthof(indspec->produced_cargo); ++index) { + if (!IsValidCargoID(indspec->produced_cargo[index])) break; + + Industry::ProducedCargo &p = i->produced.emplace_back(); + p.cargo = indspec->produced_cargo[index]; + p.rate = indspec->production_rate[index]; } - for (auto it = std::begin(i->accepted); it != std::end(i->accepted); ++it) { - size_t index = it - std::begin(i->accepted); - it->cargo = indspec->accepts_cargo[index]; + for (size_t index = 0; index < lengthof(indspec->accepts_cargo); ++index) { + if (!IsValidCargoID(indspec->accepts_cargo[index])) break; + + Industry::AcceptedCargo &a = i->accepted.emplace_back(); + a.cargo = indspec->accepts_cargo[index]; } /* Randomize inital production if non-original economy is used and there are no production related callbacks. */ @@ -1870,7 +1874,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, if (HasBit(indspec->callback_mask, CBM_IND_INPUT_CARGO_TYPES)) { /* Clear all input cargo types */ - for (auto &a : i->accepted) a.cargo = INVALID_CARGO; + i->accepted.clear(); /* Query actual types */ uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast(i->accepted.size()) : 3; for (uint j = 0; j < maxcargoes; j++) { @@ -1884,7 +1888,12 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, /* Industries without "unlimited" cargo types support depend on the specific order/slots of cargo types. * They need to be able to blank out specific slots without aborting the callback sequence, * and solve this by returning undefined cargo indexes. Skip these. */ - if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue; + if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) { + /* As slots are allocated as needed now, this means we do need to add a slot for the invalid cargo. */ + Industry::AcceptedCargo &a = i->accepted.emplace_back(); + a.cargo = INVALID_CARGO; + continue; + } /* Verify valid cargo */ if (std::find(indspec->accepts_cargo, endof(indspec->accepts_cargo), cargo) == endof(indspec->accepts_cargo)) { /* Cargo not in spec, error in NewGRF */ @@ -1896,13 +1905,14 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res); break; } - i->accepted[j].cargo = cargo; + Industry::AcceptedCargo &a = i->accepted.emplace_back(); + a.cargo = cargo; } } if (HasBit(indspec->callback_mask, CBM_IND_OUTPUT_CARGO_TYPES)) { /* Clear all output cargo types */ - for (auto &p : i->produced) p.cargo = INVALID_CARGO; + i->produced.clear(); /* Query actual types */ uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast(i->produced.size()) : 2; for (uint j = 0; j < maxcargoes; j++) { @@ -1914,7 +1924,12 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, } CargoID cargo = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile); /* Allow older GRFs to skip slots. */ - if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue; + if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) { + /* As slots are allocated as needed now, this means we do need to add a slot for the invalid cargo. */ + Industry::ProducedCargo &p = i->produced.emplace_back(); + p.cargo = INVALID_CARGO; + continue; + } /* Verify valid cargo */ if (std::find(indspec->produced_cargo, endof(indspec->produced_cargo), cargo) == endof(indspec->produced_cargo)) { /* Cargo not in spec, error in NewGRF */ @@ -1926,7 +1941,8 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res); break; } - i->produced[j].cargo = cargo; + Industry::ProducedCargo &p = i->produced.emplace_back(); + p.cargo = cargo; } } @@ -3212,3 +3228,18 @@ bool IndustryCompare::operator() (const IndustryListEntry &lhs, const IndustryLi /* Compare by distance first and use index as a tiebreaker. */ return std::tie(lhs.distance, lhs.industry->index) < std::tie(rhs.distance, rhs.industry->index); } + +/** + * Remove unused industry accepted/produced slots -- entries after the last slot with valid cargo. + * @param ind Industry to trim slots. + */ +void TrimIndustryAcceptedProduced(Industry *ind) +{ + auto ita = std::find_if(std::rbegin(ind->accepted), std::rend(ind->accepted), [](const auto &a) { return IsValidCargoID(a.cargo); }); + ind->accepted.erase(ita.base(), std::end(ind->accepted)); + ind->accepted.shrink_to_fit(); + + auto itp = std::find_if(std::rbegin(ind->produced), std::rend(ind->produced), [](const auto &p) { return IsValidCargoID(p.cargo); }); + ind->produced.erase(itp.base(), std::end(ind->produced)); + ind->produced.shrink_to_fit(); +} diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 34a9f36d27..bc203e16dc 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3044,16 +3044,6 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS)) { /* Make sure added industry cargo slots are cleared */ for (Industry *i : Industry::Iterate()) { - for (auto it = std::begin(i->produced) + 2; it != std::end(i->produced); ++it) { - it->cargo = INVALID_CARGO; - it->waiting = 0; - it->rate = 0; - it->history = {}; - } - for (auto it = std::begin(i->accepted) + 3; it != std::end(i->accepted); ++it) { - it->cargo = INVALID_CARGO; - it->waiting = 0; - } /* Make sure last_cargo_accepted_at is copied to elements for every valid input cargo. * The loading routine should put the original singular value into the first array element. */ for (auto &a : i->accepted) { diff --git a/src/saveload/industry_sl.cpp b/src/saveload/industry_sl.cpp index 693fe7160f..342f03d807 100644 --- a/src/saveload/industry_sl.cpp +++ b/src/saveload/industry_sl.cpp @@ -39,11 +39,12 @@ public: void Load(Industry *i) const override { - size_t len = SlGetStructListLength(i->accepted.size()); + size_t len = SlGetStructListLength(INDUSTRY_NUM_INPUTS); - for (auto &a : i->accepted) { - if (--len > i->accepted.size()) break; // unsigned so wraps after hitting zero. - SlObject(&a, this->GetDescription()); + i->accepted.reserve(len); + for (size_t index = 0; index < len; ++index) { + auto &a = i->accepted.emplace_back(); + SlObject(&a, this->GetLoadDescription()); } } @@ -115,11 +116,12 @@ public: void Load(Industry *i) const override { - size_t len = SlGetStructListLength(i->produced.size()); + size_t len = SlGetStructListLength(INDUSTRY_NUM_OUTPUTS); - for (auto &p : i->produced) { - if (--len > i->produced.size()) break; // unsigned so wraps after hitting zero. - SlObject(&p, this->GetDescription()); + i->produced.reserve(len); + for (size_t index = 0; index < len; ++index) { + auto &p = i->produced.emplace_back(); + SlObject(&p, this->GetLoadDescription()); } } @@ -214,17 +216,19 @@ struct INDYChunkHandler : ChunkHandler { } } - void LoadMoveAcceptsProduced(Industry *i) const + void LoadMoveAcceptsProduced(Industry *i, uint inputs, uint outputs) const { - for (uint j = 0; j != INDUSTRY_NUM_INPUTS; ++j) { - auto &a = i->accepted[j]; + i->accepted.reserve(inputs); + for (uint j = 0; j != inputs; ++j) { + auto &a = i->accepted.emplace_back(); a.cargo = SlIndustryAccepted::old_cargo[j]; a.waiting = SlIndustryAccepted::old_waiting[j]; a.last_accepted = SlIndustryAccepted::old_last_accepted[j]; } - for (uint j = 0; j != INDUSTRY_NUM_OUTPUTS; ++j) { - auto &p = i->produced[j]; + i->produced.reserve(outputs); + for (uint j = 0; j != outputs; ++j) { + auto &p = i->produced.emplace_back(); p.cargo = SlIndustryProduced::old_cargo[j]; p.waiting = SlIndustryProduced::old_waiting[j]; p.rate = SlIndustryProduced::old_rate[j]; @@ -256,8 +260,13 @@ struct INDYChunkHandler : ChunkHandler { i->psa = new PersistentStorage(0, 0, 0); std::copy(std::begin(_old_ind_persistent_storage.storage), std::end(_old_ind_persistent_storage.storage), std::begin(i->psa->storage)); } - if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE)) LoadMoveAcceptsProduced(i); + if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS)) { + LoadMoveAcceptsProduced(i, 3, 2); + } else if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE)) { + LoadMoveAcceptsProduced(i, INDUSTRY_NUM_INPUTS, INDUSTRY_NUM_OUTPUTS); + } Industry::IncIndustryTypeCount(i->type); + TrimIndustryAcceptedProduced(i); } } diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index b9d4d632e4..c56b68f7da 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -804,6 +804,10 @@ static bool LoadOldStation(LoadgameState *ls, int num) return true; } +/* Old save games always have 3 input and 2 output slots per industry. */ +static std::array _old_accepted{}; +static std::array _old_produced{}; + static const OldChunks industry_chunk[] = { OCL_SVAR( OC_TILE, Industry, location.tile ), OCL_VAR ( OC_UINT32, 1, &_old_town_index ), @@ -811,29 +815,29 @@ static const OldChunks industry_chunk[] = { OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Industry, location.h ), OCL_NULL( 2 ), ///< used to be industry's produced_cargo - OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[0].waiting ), - OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[1].waiting ), - OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[0].waiting ), - OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[1].waiting ), + OCL_VAR( OC_TTD | OC_UINT16, 1, &_old_produced[0].waiting ), + OCL_VAR( OC_TTD | OC_UINT16, 1, &_old_produced[1].waiting ), + OCL_VAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 1, &_old_produced[0].waiting ), + OCL_VAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 1, &_old_produced[1].waiting ), - OCL_SVAR( OC_UINT8, Industry, produced[0].rate ), - OCL_SVAR( OC_UINT8, Industry, produced[1].rate ), + OCL_VAR( OC_UINT8, 1, &_old_produced[0].rate ), + OCL_VAR( OC_UINT8, 1, &_old_produced[1].rate ), OCL_NULL( 3 ), ///< used to be industry's accepts_cargo OCL_SVAR( OC_UINT8, Industry, prod_level ), - OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].production ), - OCL_SVAR( OC_UINT16, Industry, produced[1].history[THIS_MONTH].production ), - OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].transported ), - OCL_SVAR( OC_UINT16, Industry, produced[1].history[THIS_MONTH].transported ), + OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[THIS_MONTH].production ), + OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[THIS_MONTH].production ), + OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[THIS_MONTH].transported ), + OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[THIS_MONTH].transported ), OCL_NULL( 2 ), ///< last_month_pct_transported, now computed on the fly - OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].production ), - OCL_SVAR( OC_UINT16, Industry, produced[1].history[LAST_MONTH].production ), - OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].transported ), - OCL_SVAR( OC_UINT16, Industry, produced[1].history[LAST_MONTH].transported ), + OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[LAST_MONTH].production ), + OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[LAST_MONTH].production ), + OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[LAST_MONTH].transported ), + OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[LAST_MONTH].transported ), OCL_SVAR( OC_UINT8, Industry, type ), OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, counter ), @@ -854,6 +858,10 @@ static bool LoadOldIndustry(LoadgameState *ls, int num) if (!LoadChunk(ls, i, industry_chunk)) return false; if (i->location.tile != 0) { + /* Copy data from old fixed arrays to industry. */ + std::copy(std::begin(_old_accepted), std::end(_old_accepted), std::back_inserter(i->accepted)); + std::copy(std::begin(_old_produced), std::end(_old_produced), std::back_inserter(i->produced)); + i->town = RemapTown(i->location.tile); if (_savegame_type == SGT_TTO) { @@ -867,6 +875,7 @@ static bool LoadOldIndustry(LoadgameState *ls, int num) } Industry::IncIndustryTypeCount(i->type); + TrimIndustryAcceptedProduced(i); } else { delete i; }