From 6af9d6ba2d87e9f52b0a6ddd9d19673838eae393 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 13 Feb 2026 19:52:49 -0800 Subject: [PATCH] Fix name query parsing for Classic/TBC --- include/game/packet_parsers.hpp | 6 +++ src/game/game_handler.cpp | 9 +++- src/game/packet_parsers_tbc.cpp | 73 +++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/include/game/packet_parsers.hpp b/include/game/packet_parsers.hpp index 291bdfad..cd2a31c2 100644 --- a/include/game/packet_parsers.hpp +++ b/include/game/packet_parsers.hpp @@ -102,6 +102,11 @@ public: return MessageChatParser::parse(packet, data); } + /** Parse SMSG_NAME_QUERY_RESPONSE */ + virtual bool parseNameQueryResponse(network::Packet& packet, NameQueryResponseData& data) { + return NameQueryResponseParser::parse(packet, data); + } + // --- Destroy Object --- /** Parse SMSG_DESTROY_OBJECT */ @@ -158,6 +163,7 @@ public: bool parseUpdateObject(network::Packet& packet, UpdateObjectData& data) override; bool parseCharEnum(network::Packet& packet, CharEnumResponse& response) override; bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) override; + bool parseNameQueryResponse(network::Packet& packet, NameQueryResponseData& data) override; }; /** diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index ae626f69..c803286a 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -995,8 +995,10 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_PERIODICAURALOG: case Opcode::SMSG_SPELLENERGIZELOG: case Opcode::SMSG_ENVIRONMENTALDAMAGELOG: + break; + case Opcode::SMSG_LOOT_MONEY_NOTIFY: { - // uint32 money + uint8 soleLooter + // Format: uint32 money + uint8 soleLooter if (packet.getSize() - packet.getReadPos() >= 4) { uint32_t amount = packet.readUInt32(); playerMoneyCopper_ += amount; @@ -4873,7 +4875,10 @@ std::string GameHandler::getCachedCreatureName(uint32_t entry) const { void GameHandler::handleNameQueryResponse(network::Packet& packet) { NameQueryResponseData data; - if (!NameQueryResponseParser::parse(packet, data)) return; + if (!packetParsers_ || !packetParsers_->parseNameQueryResponse(packet, data)) { + LOG_WARNING("Failed to parse SMSG_NAME_QUERY_RESPONSE (size=", packet.getSize(), ")"); + return; + } pendingNameQueries.erase(data.guid); diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index 0444f2b8..8916048b 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -459,5 +459,78 @@ bool TbcPacketParsers::parseAuraUpdate(network::Packet& /*packet*/, AuraUpdateDa return false; } +// ============================================================================ +// TBC/Classic parseNameQueryResponse +// +// WotLK uses: packedGuid + uint8 found + name + realmName + u8 race + u8 gender + u8 class +// Classic/TBC commonly use: uint64 guid + [optional uint8 found] + CString name + uint32 race + uint32 gender + uint32 class +// +// Implement a robust parser that handles both classic-era variants. +// ============================================================================ +static bool hasNullWithin(const network::Packet& p, size_t start, size_t maxLen) { + const auto& d = p.getData(); + size_t end = std::min(d.size(), start + maxLen); + for (size_t i = start; i < end; i++) { + if (d[i] == 0) return true; + } + return false; +} + +bool TbcPacketParsers::parseNameQueryResponse(network::Packet& packet, NameQueryResponseData& data) { + // Default all fields + data = NameQueryResponseData{}; + + size_t start = packet.getReadPos(); + if (packet.getSize() - start < 8) return false; + + // Variant A: guid(u64) + name + race(u32) + gender(u32) + class(u32) + { + packet.setReadPos(start); + data.guid = packet.readUInt64(); + data.found = 0; + data.name = packet.readString(); + if (!data.name.empty() && (packet.getSize() - packet.getReadPos()) >= 12) { + uint32_t race = packet.readUInt32(); + uint32_t gender = packet.readUInt32(); + uint32_t cls = packet.readUInt32(); + data.race = static_cast(race & 0xFF); + data.gender = static_cast(gender & 0xFF); + data.classId = static_cast(cls & 0xFF); + data.realmName.clear(); + return true; + } + } + + // Variant B: guid(u64) + found(u8) + [if found==0: name + race(u32)+gender(u32)+class(u32)] + { + packet.setReadPos(start); + data.guid = packet.readUInt64(); + if (packet.getSize() - packet.getReadPos() < 1) { + packet.setReadPos(start); + return false; + } + uint8_t found = packet.readUInt8(); + // Guard: only treat it as a found flag if a CString likely follows. + if ((found == 0 || found == 1) && hasNullWithin(packet, packet.getReadPos(), 64)) { + data.found = found; + if (data.found != 0) return true; + data.name = packet.readString(); + if (!data.name.empty() && (packet.getSize() - packet.getReadPos()) >= 12) { + uint32_t race = packet.readUInt32(); + uint32_t gender = packet.readUInt32(); + uint32_t cls = packet.readUInt32(); + data.race = static_cast(race & 0xFF); + data.gender = static_cast(gender & 0xFF); + data.classId = static_cast(cls & 0xFF); + data.realmName.clear(); + return true; + } + } + } + + packet.setReadPos(start); + return false; +} + } // namespace game } // namespace wowee