From 463e3f5ed158d1e222d3a70989eb465d9b793dbe Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 06:21:05 -0700 Subject: [PATCH] game: fix party frame duplication and player name on entity re-creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SMSG_GROUP_LIST is a full replacement packet, not a delta. handleGroupList() was not resetting partyData before parsing, so repeated GROUP_LIST packets pushed duplicate members onto the existing vector — a 2-player party would show the same name 5 times if the packet was sent 5 times. Fix: reset partyData = GroupListData{} before calling GroupListParser::parse(). Also fix player names staying "Unknown" when an entity moves out of range and comes back: queryPlayerName() now applies the cached name to the new entity object immediately instead of skipping when the name is already in cache. This was causing other players' names to appear as unknown after zoning or crossing render distance boundaries. --- src/game/game_handler.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 20da8625..d530de95 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -9855,7 +9855,20 @@ void GameHandler::addLocalChatMessage(const MessageChatData& msg) { // ============================================================ void GameHandler::queryPlayerName(uint64_t guid) { - if (playerNameCache.count(guid) || pendingNameQueries.count(guid)) return; + // If already cached, apply the name to the entity (handles entity recreation after + // moving out/in range — the entity object is new but the cached name is valid). + auto cacheIt = playerNameCache.find(guid); + if (cacheIt != playerNameCache.end()) { + auto entity = entityManager.getEntity(guid); + if (entity && entity->getType() == ObjectType::PLAYER) { + auto player = std::static_pointer_cast(entity); + if (player->getName().empty()) { + player->setName(cacheIt->second); + } + } + return; + } + if (pendingNameQueries.count(guid)) return; if (state != WorldState::IN_WORLD || !socket) { LOG_INFO("queryPlayerName: skipped guid=0x", std::hex, guid, std::dec, " state=", worldStateName(state), " socket=", (socket ? "yes" : "no")); @@ -13291,6 +13304,9 @@ void GameHandler::handleGroupList(network::Packet& packet) { // WotLK 3.3.5a added a roles byte (group level + per-member) for the dungeon finder. // Classic 1.12 and TBC 2.4.3 do not send the roles byte. const bool hasRoles = isActiveExpansion("wotlk"); + // Reset before parsing — SMSG_GROUP_LIST is a full replacement, not a delta. + // Without this, repeated GROUP_LIST packets push duplicate members. + partyData = GroupListData{}; if (!GroupListParser::parse(packet, partyData, hasRoles)) return; if (partyData.isEmpty()) {