diff --git a/include/game/packet_parsers.hpp b/include/game/packet_parsers.hpp index 8efcd5eb..47dc2a79 100644 --- a/include/game/packet_parsers.hpp +++ b/include/game/packet_parsers.hpp @@ -365,6 +365,9 @@ public: // TBC/Classic SMSG_QUESTGIVER_QUEST_DETAILS lacks informUnit(u64), flags(u32), // isFinished(u8) that WotLK added; uses variable item counts + emote section. bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) override; + // TBC 2.4.3 SMSG_GUILD_ROSTER: same rank structure as WotLK (variable rankCount + + // goldLimit + bank tabs), but NO gender byte per member (WotLK added it) + bool parseGuildRoster(network::Packet& packet, GuildRosterData& data) override; }; /** diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index 30dd8e05..fa97a5e7 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -1537,5 +1537,125 @@ bool TbcPacketParsers::parseSpellHealLog(network::Packet& packet, SpellHealLogDa return true; } +// ============================================================================ +// TBC 2.4.3 guild roster parser +// Same rank structure as WotLK (variable rankCount + goldLimit + bank tabs), +// but NO gender byte per member (WotLK added it). +// ============================================================================ + +bool TbcPacketParsers::parseGuildRoster(network::Packet& packet, GuildRosterData& data) { + if (packet.getSize() < 4) { + LOG_ERROR("TBC SMSG_GUILD_ROSTER too small: ", packet.getSize()); + return false; + } + uint32_t numMembers = packet.readUInt32(); + + const uint32_t MAX_GUILD_MEMBERS = 1000; + if (numMembers > MAX_GUILD_MEMBERS) { + LOG_WARNING("TBC GuildRoster: numMembers capped (requested=", numMembers, ")"); + numMembers = MAX_GUILD_MEMBERS; + } + + data.motd = packet.readString(); + data.guildInfo = packet.readString(); + + if (packet.getReadPos() + 4 > packet.getSize()) { + LOG_WARNING("TBC GuildRoster: truncated before rankCount"); + data.ranks.clear(); + data.members.clear(); + return true; + } + + uint32_t rankCount = packet.readUInt32(); + const uint32_t MAX_GUILD_RANKS = 20; + if (rankCount > MAX_GUILD_RANKS) { + LOG_WARNING("TBC GuildRoster: rankCount capped (requested=", rankCount, ")"); + rankCount = MAX_GUILD_RANKS; + } + + data.ranks.resize(rankCount); + for (uint32_t i = 0; i < rankCount; ++i) { + if (packet.getReadPos() + 4 > packet.getSize()) { + LOG_WARNING("TBC GuildRoster: truncated rank at index ", i); + break; + } + data.ranks[i].rights = packet.readUInt32(); + if (packet.getReadPos() + 4 > packet.getSize()) { + data.ranks[i].goldLimit = 0; + } else { + data.ranks[i].goldLimit = packet.readUInt32(); + } + // 6 bank tab flags + 6 bank tab items per day (guild banks added in TBC 2.3) + for (int t = 0; t < 6; ++t) { + if (packet.getReadPos() + 8 > packet.getSize()) break; + packet.readUInt32(); // tabFlags + packet.readUInt32(); // tabItemsPerDay + } + } + + data.members.resize(numMembers); + for (uint32_t i = 0; i < numMembers; ++i) { + if (packet.getReadPos() + 9 > packet.getSize()) { + LOG_WARNING("TBC GuildRoster: truncated member at index ", i); + break; + } + auto& m = data.members[i]; + m.guid = packet.readUInt64(); + m.online = (packet.readUInt8() != 0); + + if (packet.getReadPos() >= packet.getSize()) { + m.name.clear(); + } else { + m.name = packet.readString(); + } + + if (packet.getReadPos() + 1 > packet.getSize()) { + m.rankIndex = 0; + m.level = 1; + m.classId = 0; + m.gender = 0; + m.zoneId = 0; + } else { + m.rankIndex = packet.readUInt32(); + if (packet.getReadPos() + 2 > packet.getSize()) { + m.level = 1; + m.classId = 0; + } else { + m.level = packet.readUInt8(); + m.classId = packet.readUInt8(); + } + // TBC: NO gender byte (WotLK added it) + m.gender = 0; + if (packet.getReadPos() + 4 > packet.getSize()) { + m.zoneId = 0; + } else { + m.zoneId = packet.readUInt32(); + } + } + + if (!m.online) { + if (packet.getReadPos() + 4 > packet.getSize()) { + m.lastOnline = 0.0f; + } else { + m.lastOnline = packet.readFloat(); + } + } + + if (packet.getReadPos() >= packet.getSize()) { + m.publicNote.clear(); + m.officerNote.clear(); + } else { + m.publicNote = packet.readString(); + if (packet.getReadPos() >= packet.getSize()) { + m.officerNote.clear(); + } else { + m.officerNote = packet.readString(); + } + } + } + LOG_INFO("Parsed TBC SMSG_GUILD_ROSTER: ", numMembers, " members, motd=", data.motd); + return true; +} + } // namespace game } // namespace wowee