Fix name query parsing for Classic/TBC

This commit is contained in:
Kelsi 2026-02-13 19:52:49 -08:00
parent bf39c0b900
commit 6af9d6ba2d
3 changed files with 86 additions and 2 deletions

View file

@ -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;
};
/**

View file

@ -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);

View file

@ -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<uint8_t>(race & 0xFF);
data.gender = static_cast<uint8_t>(gender & 0xFF);
data.classId = static_cast<uint8_t>(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<uint8_t>(race & 0xFF);
data.gender = static_cast<uint8_t>(gender & 0xFF);
data.classId = static_cast<uint8_t>(cls & 0xFF);
data.realmName.clear();
return true;
}
}
}
packet.setReadPos(start);
return false;
}
} // namespace game
} // namespace wowee