1
0
Fork 0

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.
pull/12338/head
Peter Nelson 2023-07-14 11:49:11 +01:00 committed by Peter Nelson
parent 00e0021e3a
commit 3de8853e29
5 changed files with 99 additions and 58 deletions

View File

@ -90,14 +90,14 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
TimerGameEconomy::Date last_accepted; ///< Last day cargo was accepted by this industry TimerGameEconomy::Date last_accepted; ///< Last day cargo was accepted by this industry
}; };
using ProducedCargoArray = std::array<ProducedCargo, INDUSTRY_NUM_OUTPUTS>; using ProducedCargoes = std::vector<ProducedCargo>;
using AcceptedCargoArray = std::array<AcceptedCargo, INDUSTRY_NUM_INPUTS>; using AcceptedCargoes = std::vector<AcceptedCargo>;
TileArea location; ///< Location of the industry TileArea location; ///< Location of the industry
Town *town; ///< Nearest town Town *town; ///< Nearest town
Station *neutral_station; ///< Associated neutral station Station *neutral_station; ///< Associated neutral station
ProducedCargoArray produced; ///< INDUSTRY_NUM_OUTPUTS production cargo slots ProducedCargoes produced; ///< produced cargo slots
AcceptedCargoArray accepted; ///< INDUSTRY_NUM_INPUTS input cargo slots AcceptedCargoes accepted; ///< accepted cargo slots
uint8_t prod_level; ///< general production level uint8_t prod_level; ///< general production level
uint16_t counter; ///< used for animation and/or production (if available cargo) 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. * @param cargo CargoID to find.
* @return Iterator pointing to produced cargo slot if it exists, or the end iterator. * @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); 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; }); 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. * @param cargo CargoID to find.
* @return Iterator pointing to produced cargo slot if it exists, or the end iterator. * @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); 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; }); 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. * @param cargo CargoID to find.
* @return Iterator pointing to accepted cargo slot if it exists, or the end iterator. * @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); 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; }); 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, IDIWD_FORCE_RESORT,
}; };
void TrimIndustryAcceptedProduced(Industry *ind);
#endif /* INDUSTRY_H */ #endif /* INDUSTRY_H */

View File

@ -1788,15 +1788,19 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
i->type = type; i->type = type;
Industry::IncIndustryTypeCount(type); Industry::IncIndustryTypeCount(type);
for (auto it = std::begin(i->produced); it != std::end(i->produced); ++it) { for (size_t index = 0; index < lengthof(indspec->produced_cargo); ++index) {
size_t index = it - std::begin(i->produced); if (!IsValidCargoID(indspec->produced_cargo[index])) break;
it->cargo = indspec->produced_cargo[index];
it->rate = indspec->production_rate[index]; 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) { for (size_t index = 0; index < lengthof(indspec->accepts_cargo); ++index) {
size_t index = it - std::begin(i->accepted); if (!IsValidCargoID(indspec->accepts_cargo[index])) break;
it->cargo = indspec->accepts_cargo[index];
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. */ /* 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)) { if (HasBit(indspec->callback_mask, CBM_IND_INPUT_CARGO_TYPES)) {
/* Clear all input cargo types */ /* Clear all input cargo types */
for (auto &a : i->accepted) a.cargo = INVALID_CARGO; i->accepted.clear();
/* Query actual types */ /* Query actual types */
uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->accepted.size()) : 3; uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->accepted.size()) : 3;
for (uint j = 0; j < maxcargoes; j++) { 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. /* 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, * 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. */ * 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 */ /* Verify valid cargo */
if (std::find(indspec->accepts_cargo, endof(indspec->accepts_cargo), cargo) == endof(indspec->accepts_cargo)) { if (std::find(indspec->accepts_cargo, endof(indspec->accepts_cargo), cargo) == endof(indspec->accepts_cargo)) {
/* Cargo not in spec, error in NewGRF */ /* 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); ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
break; 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)) { if (HasBit(indspec->callback_mask, CBM_IND_OUTPUT_CARGO_TYPES)) {
/* Clear all output cargo types */ /* Clear all output cargo types */
for (auto &p : i->produced) p.cargo = INVALID_CARGO; i->produced.clear();
/* Query actual types */ /* Query actual types */
uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->produced.size()) : 2; uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->produced.size()) : 2;
for (uint j = 0; j < maxcargoes; j++) { 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); CargoID cargo = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
/* Allow older GRFs to skip slots. */ /* 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 */ /* Verify valid cargo */
if (std::find(indspec->produced_cargo, endof(indspec->produced_cargo), cargo) == endof(indspec->produced_cargo)) { if (std::find(indspec->produced_cargo, endof(indspec->produced_cargo), cargo) == endof(indspec->produced_cargo)) {
/* Cargo not in spec, error in NewGRF */ /* 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); ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
break; 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. */ /* 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); 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();
}

View File

@ -3044,16 +3044,6 @@ bool AfterLoadGame()
if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS)) { if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS)) {
/* Make sure added industry cargo slots are cleared */ /* Make sure added industry cargo slots are cleared */
for (Industry *i : Industry::Iterate()) { 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. /* 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. */ * The loading routine should put the original singular value into the first array element. */
for (auto &a : i->accepted) { for (auto &a : i->accepted) {

View File

@ -39,11 +39,12 @@ public:
void Load(Industry *i) const override 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) { i->accepted.reserve(len);
if (--len > i->accepted.size()) break; // unsigned so wraps after hitting zero. for (size_t index = 0; index < len; ++index) {
SlObject(&a, this->GetDescription()); auto &a = i->accepted.emplace_back();
SlObject(&a, this->GetLoadDescription());
} }
} }
@ -115,11 +116,12 @@ public:
void Load(Industry *i) const override 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) { i->produced.reserve(len);
if (--len > i->produced.size()) break; // unsigned so wraps after hitting zero. for (size_t index = 0; index < len; ++index) {
SlObject(&p, this->GetDescription()); 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) { i->accepted.reserve(inputs);
auto &a = i->accepted[j]; for (uint j = 0; j != inputs; ++j) {
auto &a = i->accepted.emplace_back();
a.cargo = SlIndustryAccepted::old_cargo[j]; a.cargo = SlIndustryAccepted::old_cargo[j];
a.waiting = SlIndustryAccepted::old_waiting[j]; a.waiting = SlIndustryAccepted::old_waiting[j];
a.last_accepted = SlIndustryAccepted::old_last_accepted[j]; a.last_accepted = SlIndustryAccepted::old_last_accepted[j];
} }
for (uint j = 0; j != INDUSTRY_NUM_OUTPUTS; ++j) { i->produced.reserve(outputs);
auto &p = i->produced[j]; for (uint j = 0; j != outputs; ++j) {
auto &p = i->produced.emplace_back();
p.cargo = SlIndustryProduced::old_cargo[j]; p.cargo = SlIndustryProduced::old_cargo[j];
p.waiting = SlIndustryProduced::old_waiting[j]; p.waiting = SlIndustryProduced::old_waiting[j];
p.rate = SlIndustryProduced::old_rate[j]; p.rate = SlIndustryProduced::old_rate[j];
@ -256,8 +260,13 @@ struct INDYChunkHandler : ChunkHandler {
i->psa = new PersistentStorage(0, 0, 0); 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)); 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); Industry::IncIndustryTypeCount(i->type);
TrimIndustryAcceptedProduced(i);
} }
} }

View File

@ -804,6 +804,10 @@ static bool LoadOldStation(LoadgameState *ls, int num)
return true; return true;
} }
/* Old save games always have 3 input and 2 output slots per industry. */
static std::array<Industry::AcceptedCargo, 3> _old_accepted{};
static std::array<Industry::ProducedCargo, 2> _old_produced{};
static const OldChunks industry_chunk[] = { static const OldChunks industry_chunk[] = {
OCL_SVAR( OC_TILE, Industry, location.tile ), OCL_SVAR( OC_TILE, Industry, location.tile ),
OCL_VAR ( OC_UINT32, 1, &_old_town_index ), 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_SVAR( OC_FILE_U8 | OC_VAR_U16, Industry, location.h ),
OCL_NULL( 2 ), ///< used to be industry's produced_cargo OCL_NULL( 2 ), ///< used to be industry's produced_cargo
OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[0].waiting ), OCL_VAR( OC_TTD | OC_UINT16, 1, &_old_produced[0].waiting ),
OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[1].waiting ), OCL_VAR( OC_TTD | OC_UINT16, 1, &_old_produced[1].waiting ),
OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[0].waiting ), OCL_VAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 1, &_old_produced[0].waiting ),
OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[1].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_VAR( OC_UINT8, 1, &_old_produced[0].rate ),
OCL_SVAR( OC_UINT8, Industry, produced[1].rate ), OCL_VAR( OC_UINT8, 1, &_old_produced[1].rate ),
OCL_NULL( 3 ), ///< used to be industry's accepts_cargo OCL_NULL( 3 ), ///< used to be industry's accepts_cargo
OCL_SVAR( OC_UINT8, Industry, prod_level ), OCL_SVAR( OC_UINT8, Industry, prod_level ),
OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].production ), OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[THIS_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[1].history[THIS_MONTH].production ), OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[THIS_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].transported ), OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[THIS_MONTH].transported ),
OCL_SVAR( OC_UINT16, Industry, produced[1].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_NULL( 2 ), ///< last_month_pct_transported, now computed on the fly
OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].production ), OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[LAST_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[1].history[LAST_MONTH].production ), OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[LAST_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].transported ), OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[LAST_MONTH].transported ),
OCL_SVAR( OC_UINT16, Industry, produced[1].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_UINT8, Industry, type ),
OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, counter ), 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 (!LoadChunk(ls, i, industry_chunk)) return false;
if (i->location.tile != 0) { 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); i->town = RemapTown(i->location.tile);
if (_savegame_type == SGT_TTO) { if (_savegame_type == SGT_TTO) {
@ -867,6 +875,7 @@ static bool LoadOldIndustry(LoadgameState *ls, int num)
} }
Industry::IncIndustryTypeCount(i->type); Industry::IncIndustryTypeCount(i->type);
TrimIndustryAcceptedProduced(i);
} else { } else {
delete i; delete i;
} }