From e61b23626afe1d1ad5fa881cb53baa2f2042402d Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 27 Mar 2026 18:28:36 -0700 Subject: [PATCH] perf: entity/skill/DBC/warden maps to unordered_map; fix 3x contacts scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Entity storage: std::map> → unordered_map for O(1) entity lookups instead of O(log n). No code depends on GUID ordering. Player skills: std::map → unordered_map. DBC ID cache: std::map → unordered_map. Warden: apiHandlers_ and allocations_ → unordered_map (freeBlocks_ kept as std::map since its coalescing logic requires ordered iteration). Contacts: handleFriendStatus() did 3 separate O(n) find_if scans per packet. Consolidated to single find_if with iterator reuse. O(3n) → O(n). --- include/game/entity.hpp | 5 +++-- include/game/game_handler.hpp | 4 ++-- include/game/warden_emulator.hpp | 4 ++-- include/pipeline/dbc_loader.hpp | 4 ++-- src/game/game_handler.cpp | 24 +++++++++++------------- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/include/game/entity.hpp b/include/game/entity.hpp index b4e08cca..74b1c1c1 100644 --- a/include/game/entity.hpp +++ b/include/game/entity.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace wowee { @@ -338,7 +339,7 @@ public: bool hasEntity(uint64_t guid) const; // Get all entities - const std::map>& getEntities() const { + const std::unordered_map>& getEntities() const { return entities; } @@ -353,7 +354,7 @@ public: } private: - std::map> entities; + std::unordered_map> entities; }; } // namespace game diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 915b8b54..22467c4e 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1106,7 +1106,7 @@ public: uint32_t getOverrideLightTransMs() const { return overrideLightTransMs_; } // Player skills - const std::map& getPlayerSkills() const { return playerSkills_; } + const std::unordered_map& getPlayerSkills() const { return playerSkills_; } const std::string& getSkillName(uint32_t skillId) const; uint32_t getSkillCategory(uint32_t skillId) const; bool isProfessionSpell(uint32_t spellId) const; @@ -3502,7 +3502,7 @@ private: uint32_t overrideLightTransMs_ = 0; // ---- Player skills ---- - std::map playerSkills_; + std::unordered_map playerSkills_; std::unordered_map skillLineNames_; std::unordered_map skillLineCategories_; std::unordered_map spellToSkillLine_; // spellID -> skillLineID diff --git a/include/game/warden_emulator.hpp b/include/game/warden_emulator.hpp index 3c6cddbf..4fe30e27 100644 --- a/include/game/warden_emulator.hpp +++ b/include/game/warden_emulator.hpp @@ -156,12 +156,12 @@ private: int argCount; std::function&)> handler; }; - std::map apiHandlers_; + std::unordered_map apiHandlers_; uint32_t nextApiStubAddr_; // tracks next free stub slot (replaces static local) bool apiCodeHookRegistered_; // true once UC_HOOK_CODE for stub range is added // Memory allocation tracking - std::map allocations_; + std::unordered_map allocations_; std::map freeBlocks_; // free-list keyed by base address uint32_t nextHeapAddr_; diff --git a/include/pipeline/dbc_loader.hpp b/include/pipeline/dbc_loader.hpp index 6e14d5da..24d1e756 100644 --- a/include/pipeline/dbc_loader.hpp +++ b/include/pipeline/dbc_loader.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -137,7 +137,7 @@ private: std::vector stringBlock; // String block // Cache for record ID -> index lookup - mutable std::map idToIndexCache; + mutable std::unordered_map idToIndexCache; mutable bool idCacheBuilt = false; void buildIdCache() const; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 58a7ecbe..a73ff62d 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -23802,16 +23802,16 @@ void GameHandler::handleFriendStatus(network::Packet& packet) { return; } + // Single lookup — reuse iterator for name resolution and update/erase below + auto cit = std::find_if(contacts_.begin(), contacts_.end(), + [&](const ContactEntry& e){ return e.guid == data.guid; }); + // Look up player name: contacts_ (populated by SMSG_FRIEND_LIST) > playerNameCache std::string playerName; - { - auto cit2 = std::find_if(contacts_.begin(), contacts_.end(), - [&](const ContactEntry& e){ return e.guid == data.guid; }); - if (cit2 != contacts_.end() && !cit2->name.empty()) { - playerName = cit2->name; - } else { - playerName = lookupName(data.guid); - } + if (cit != contacts_.end() && !cit->name.empty()) { + playerName = cit->name; + } else { + playerName = lookupName(data.guid); } // Update friends cache @@ -23823,11 +23823,9 @@ void GameHandler::handleFriendStatus(network::Packet& packet) { // Mirror into contacts_: update existing entry or add/remove as needed if (data.status == 0) { // Removed from friends list - contacts_.erase(std::remove_if(contacts_.begin(), contacts_.end(), - [&](const ContactEntry& e){ return e.guid == data.guid; }), contacts_.end()); + if (cit != contacts_.end()) + contacts_.erase(cit); } else { - auto cit = std::find_if(contacts_.begin(), contacts_.end(), - [&](const ContactEntry& e){ return e.guid == data.guid; }); if (cit != contacts_.end()) { if (!playerName.empty() && playerName != "Unknown") cit->name = playerName; // status: 2=online→1, 3=offline→0, 1=added→1 (online on add) @@ -24028,7 +24026,7 @@ void GameHandler::extractSkillFields(const std::map& fields) const uint16_t PLAYER_SKILL_INFO_START = fieldIndex(UF::PLAYER_SKILL_INFO_START); static constexpr int MAX_SKILL_SLOTS = 128; - std::map newSkills; + std::unordered_map newSkills; for (int slot = 0; slot < MAX_SKILL_SLOTS; slot++) { uint16_t baseField = PLAYER_SKILL_INFO_START + slot * 3;