From a28491357850e5313b2f27c63dbab6da28b4b9c9 Mon Sep 17 00:00:00 2001 From: SamuXarick <43006711+SamuXarick@users.noreply.github.com> Date: Tue, 12 Nov 2024 22:55:26 +0000 Subject: [PATCH] Codechange: Modify industry count management and change two functions related to industry generation to be more performant - Modified `Industry::counts` to use a vector of pairs to store pairs of `TownID` and vectors of `IndustryID`. - Modified `IncIndustryTypeCount` and `DecIndustryTypeCount` to manage industry counts at the town level. - Introduced `ProcessIndustries` as a helper function to process and count industries based on a provided condition. - Introduced `CountTownIndustriesOfTypeMatchingCondition` to count industries for a specific type within a town or across all towns. - Modified `GetIndustryTypeCount` function to utilize the new counting mechanism. - Updated `ResetIndustryCounts` to clear the vector of industry counts for each industry type. - Updated `DoCreateNewIndustry` and save/load functions to use the new `IncIndustryTypeCount` method. - Updated `Industry::~Industry` to use the new `DecIndustryTypeCount` method. - Modified `FindTownForIndustry` to use the new `HasTownIndustryOfType` method for better performance. - Modified `GetCountAndDistanceOfClosestInstance` to use `CountTownIndustriesOfTypeMatchingCondition` for more efficient counting. --- src/industry.h | 193 ++++++++++++++++++++++++++++++---- src/industry_cmd.cpp | 14 ++- src/newgrf_industries.cpp | 9 +- src/saveload/industry_sl.cpp | 2 +- src/saveload/oldloader_sl.cpp | 2 +- 5 files changed, 188 insertions(+), 32 deletions(-) diff --git a/src/industry.h b/src/industry.h index cd29dcff7f..5ba98e8f5b 100644 --- a/src/industry.h +++ b/src/industry.h @@ -18,6 +18,7 @@ #include "station_base.h" #include "timer/timer_game_calendar.h" #include "timer/timer_game_economy.h" +#include "town.h" typedef Pool IndustryPool; @@ -244,42 +245,193 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> { static void PostDestructor(size_t index); /** - * Increment the count of industries for this type. - * @param type IndustryType to increment - * @pre type < NUM_INDUSTRYTYPES + * Increment the count of industries of the specified type in a town. + * + * @param ind Pointer to the Industry to increment its type count in the town. + * @pre ind != nullptr */ - static inline void IncIndustryTypeCount(IndustryType type) + static inline void IncIndustryTypeCount(const Industry *ind) { - assert(type < NUM_INDUSTRYTYPES); - counts[type]++; + assert(ind != nullptr); + + /* Find the correct position to insert or update the town entry using lower_bound. */ + auto &type_vector = counts[ind->type]; + auto pair_it = std::ranges::lower_bound(type_vector, ind->town->index, {}, &std::pair>::first); + + if (pair_it != std::end(type_vector) && pair_it->first == ind->town->index) { + /* Create a reference to the town's industry list. */ + auto &iid_vector = pair_it->second; + + /* Ensure the town's industry list is not empty. */ + assert(!std::empty(iid_vector)); + + /* Ensure the industry is not already in the town's industry list. */ + assert(std::ranges::all_of(iid_vector, [&](auto &iid) { + return iid != ind->index; + })); + + /* Find the correct position to insert the industry ID in sorted order. */ + auto iid_it = std::ranges::lower_bound(iid_vector, ind->index); + + /* Add the industry ID to the town's industry list in the correct position. */ + iid_vector.insert(iid_it, ind->index); + } else { + /* Create a new vector for the industry IDs and add the industry ID. */ + std::vector iid_vector; + iid_vector.emplace_back(ind->index); + + /* Insert the new pair (town index and vector of industry IDs) into the correct position. */ + type_vector.emplace(pair_it, ind->town->index, std::move(iid_vector)); + } } /** - * Decrement the count of industries for this type. - * @param type IndustryType to decrement + * Decrement the count of industries of the specified type in a town. + * + * @param ind Pointer to the Industry to decrement its type count in the town. + * @pre ind != nullptr + */ + static inline void DecIndustryTypeCount(const Industry *ind) + { + assert(ind != nullptr); + + /* Find the correct position of the town entry using lower_bound. */ + auto &type_vector = counts[ind->type]; + auto pair_it = std::ranges::lower_bound(type_vector, ind->town->index, {}, &std::pair>::first); + + /* Ensure the pair was found in the array. */ + assert(pair_it != std::end(type_vector) && pair_it->first == ind->town->index); + + /* Create a reference to the town's industry list. */ + auto &iid_vector = pair_it->second; + + /* Ensure the town's industry list is not empty. */ + assert(!std::empty(iid_vector)); + + /* Find the industry ID within the town's industry list. */ + auto iid_it = std::ranges::lower_bound(iid_vector, ind->index); + + /* Ensure the industry ID was found in the list. */ + assert(iid_it != std::end(iid_vector) && *iid_it == ind->index); + + /* Erase the industry ID from the town's industry list. */ + iid_vector.erase(iid_it); + + /* If the town's industry list is now empty, erase the town from the counts array. */ + if (std::empty(iid_vector)) { + type_vector.erase(pair_it); + } + } + +private: + /** + * Applies the function to each industry and counts those that satisfy the function. + * Also verifies that all industries belong to the specified town. + * + * @param industries Vector of IndustryIDs to be processed. + * @param town Pointer to the Town for validation. + * @param return_early Return as soon as the count is different than zero. + * @param func Function to apply to each industry. + * @return The count of industries that satisfy the function. + */ + template + static inline uint16_t ProcessIndustries(const std::vector &industries, [[maybe_unused]] const Town *town, bool return_early, Func func) + { + uint16_t count = 0; + + for (const auto &iid : industries) { + const Industry *ind = Industry::Get(iid); + + /* Verify the industry belongs to the specified town. */ + assert(ind->town == town); + + if (func(ind)) count++; + if (return_early && count != 0) break; + } + + return count; + } + +public: + /** + * Get the count of industries for a specified type in a town or in all towns. + * + * @param type IndustryType to query. + * @param town Town to query, or nullptr to query all towns. + * @param return_early Return as soon as the count is different than zero. + * @param func Function to apply to each industry. + * @return The count of industries for the specified type. * @pre type < NUM_INDUSTRYTYPES */ - static inline void DecIndustryTypeCount(IndustryType type) + template + static inline uint16_t CountTownIndustriesOfTypeMatchingCondition(IndustryType type, const Town *town, bool return_early, Func func) { assert(type < NUM_INDUSTRYTYPES); - counts[type]--; + + uint16_t count = 0; + if (town != nullptr) { + /* Find the correct position of the town entry using lower_bound. */ + auto pair_it = std::ranges::lower_bound(counts[type], town->index, {}, &std::pair>::first); + + if (pair_it != std::end(counts[type]) && pair_it->first == town->index) { + /* Create a reference to the town's industry list. */ + auto &iid_vector = pair_it->second; + + /* Ensure the town's industry list is not empty. */ + assert(!std::empty(iid_vector)); + count = ProcessIndustries(iid_vector, town, return_early, func); + } + } else { + /* Count industries in all towns. */ + for (auto &pair : counts[type]) { + /* Create a reference to the town's industry list. */ + auto &iid_vector = pair.second; + + /* Ensure the town's industry list is not empty. */ + assert(!std::empty(iid_vector)); + count += ProcessIndustries(iid_vector, Town::Get(pair.first), return_early, func); + if (return_early && count != 0) break; + } + } + + return count; } /** - * Get the count of industries for this type. - * @param type IndustryType to query - * @pre type < NUM_INDUSTRYTYPES + * Get the count of industries for a specified type. + * + * @param type The type of industry to count. + * @return uint16_t The count of industries of the specified type. */ static inline uint16_t GetIndustryTypeCount(IndustryType type) { - assert(type < NUM_INDUSTRYTYPES); - return counts[type]; + return CountTownIndustriesOfTypeMatchingCondition(type, nullptr, false, [](const Industry *) { return true; }); } - /** Resets industry counts. */ + /** + * Check if a town has an industry of the specified type. + * + * @param type The type of industry to check for. + * @param town Pointer to the town to check. + * @return bool True if the town has an industry of the specified type, false otherwise. + */ + static inline bool HasTownIndustryOfType(IndustryType type, const Town *town) + { + assert(town != nullptr); + + return CountTownIndustriesOfTypeMatchingCondition(type, town, true, [](const Industry *) { return true; }) != 0; + } + + /** + * Resets the industry counts for all industry types. + * + * Clears the vector of industry counts for each industry type, + * effectively resetting the count of industries per type in all towns. + */ static inline void ResetIndustryCounts() { - memset(&counts, 0, sizeof(counts)); + /* Clear the vector for each industry type in the counts array. */ + std::ranges::for_each(counts, [](auto &type) { type.clear(); }); } inline const std::string &GetCachedName() const @@ -292,7 +444,12 @@ private: void FillCachedName() const; protected: - static uint16_t counts[NUM_INDUSTRYTYPES]; ///< Number of industries per type ingame + /** + * Array containing vectors of industry types. + * Each vector corresponds to a specific IndustryType and + * contains pairs of TownIDs and their associated lists of IndustryIDs. + */ + static std::array>>, NUM_INDUSTRYTYPES> counts; }; void ClearAllIndustryCachedNames(); diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 0c8d02fda5..62b96ad4ff 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -63,7 +63,7 @@ void BuildOilRig(TileIndex tile); static uint8_t _industry_sound_ctr; static TileIndex _industry_sound_tile; -uint16_t Industry::counts[NUM_INDUSTRYTYPES]; +std::array>>, NUM_INDUSTRYTYPES> Industry::counts; IndustrySpec _industry_specs[NUM_INDUSTRYTYPES]; IndustryTileSpec _industry_tile_specs[NUM_INDUSTRYTILES]; @@ -189,7 +189,7 @@ Industry::~Industry() /* Clear the persistent storage. */ delete this->psa; - DecIndustryTypeCount(this->type); + DecIndustryTypeCount(this); DeleteIndustryNews(this->index); CloseWindowById(WC_INDUSTRY_VIEW, this->index); @@ -1434,11 +1434,9 @@ static CommandCost FindTownForIndustry(TileIndex tile, IndustryType type, Town * if (_settings_game.economy.multiple_industry_per_town) return CommandCost(); - for (const Industry *i : Industry::Iterate()) { - if (i->type == type && i->town == *t) { - *t = nullptr; - return_cmd_error(STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN); - } + if (Industry::HasTownIndustryOfType(type, *t)) { + *t = nullptr; + return_cmd_error(STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN); } return CommandCost(); @@ -1787,7 +1785,6 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, i->location = TileArea(tile, 1, 1); i->type = type; - Industry::IncIndustryTypeCount(type); for (size_t index = 0; index < std::size(indspec->produced_cargo); ++index) { if (!IsValidCargoID(indspec->produced_cargo[index])) break; @@ -1812,6 +1809,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, } i->town = t; + Industry::IncIndustryTypeCount(i); i->owner = OWNER_NONE; uint16_t r = Random(); diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 41ab0218cc..5bc91e9091 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -145,12 +145,13 @@ static uint32_t GetCountAndDistanceOfClosestInstance(uint8_t param_setID, uint8_ } else { /* Count only those who match the same industry type and layout filter * Unfortunately, we have to do it manually */ - for (const Industry *i : Industry::Iterate()) { - if (i->type == ind_index && i != current && (i->selected_layout == layout_filter || layout_filter == 0) && (!town_filter || i->town == current->town)) { + count = Industry::CountTownIndustriesOfTypeMatchingCondition(ind_index, town_filter ? current->town : nullptr, false, [&](const Industry *i) { + if (i != current && (layout_filter == 0 || i->selected_layout == layout_filter)) { closest_dist = std::min(closest_dist, DistanceManhattan(current->location.tile, i->location.tile)); - count++; + return true; } - } + return false; + }); } return count << 16 | GB(closest_dist, 0, 16); diff --git a/src/saveload/industry_sl.cpp b/src/saveload/industry_sl.cpp index bb1682227a..e57033880a 100644 --- a/src/saveload/industry_sl.cpp +++ b/src/saveload/industry_sl.cpp @@ -229,7 +229,6 @@ struct INDYChunkHandler : ChunkHandler { } else if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE)) { LoadMoveAcceptsProduced(i, INDUSTRY_NUM_INPUTS, INDUSTRY_NUM_OUTPUTS); } - Industry::IncIndustryTypeCount(i->type); } } @@ -237,6 +236,7 @@ struct INDYChunkHandler : ChunkHandler { { for (Industry *i : Industry::Iterate()) { SlObject(i, _industry_desc); + Industry::IncIndustryTypeCount(i); } } }; diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index ea9c97c212..76aaa89743 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -874,7 +874,7 @@ static bool LoadOldIndustry(LoadgameState *ls, int num) i->random_colour = RemapTTOColour(i->random_colour); } - Industry::IncIndustryTypeCount(i->type); + Industry::IncIndustryTypeCount(i); } else { delete i; }