fix: add TBC guild roster parser to avoid gender byte misalignment

TBC 2.4.3 SMSG_GUILD_ROSTER has the same rank structure as WotLK
(variable rankCount + goldLimit + bank tab permissions), but does NOT
include a gender byte per member (WotLK added it). Without this
override, TBC fell through to the WotLK parser which read a spurious
gender byte, causing every subsequent field in each member entry to
misalign.
This commit is contained in:
Kelsi 2026-03-17 11:10:54 -07:00
parent a7f7c4aa93
commit dc8619464a
2 changed files with 123 additions and 0 deletions

View file

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