Fix gossip message parsing for Vanilla/Turtle (flight master hang)

Classic gossip packets lack the menuId field and quest items don't have
questFlags/isRepeatable, causing the WotLK parser to read garbage counts
(541M quests) and hang. Added ClassicPacketParsers::parseGossipMessage
override with the correct vanilla format.
This commit is contained in:
Kelsi 2026-02-14 19:38:11 -08:00
parent a96dc4ebcc
commit cbb3035313
3 changed files with 60 additions and 1 deletions

View file

@ -117,6 +117,13 @@ public:
return NameQueryResponseParser::parse(packet, data);
}
// --- Gossip ---
/** Parse SMSG_GOSSIP_MESSAGE */
virtual bool parseGossipMessage(network::Packet& packet, GossipMessageData& data) {
return GossipMessageParser::parse(packet, data);
}
// --- Destroy Object ---
/** Parse SMSG_DESTROY_OBJECT */
@ -226,6 +233,7 @@ public:
network::Packet buildCastSpell(uint32_t spellId, uint64_t targetGuid, uint8_t castCount) override;
bool parseCastFailed(network::Packet& packet, CastFailedData& data) override;
bool parseMessageChat(network::Packet& packet, MessageChatData& data) override;
bool parseGossipMessage(network::Packet& packet, GossipMessageData& data) override;
bool parseGuildRoster(network::Packet& packet, GuildRosterData& data) override;
bool parseGuildQueryResponse(network::Packet& packet, GuildQueryResponseData& data) override;
network::Packet buildJoinChannel(const std::string& channelName, const std::string& password) override;

View file

@ -7655,7 +7655,9 @@ void GameHandler::handleLootRemoved(network::Packet& packet) {
}
void GameHandler::handleGossipMessage(network::Packet& packet) {
if (!GossipMessageParser::parse(packet, currentGossip)) return;
bool ok = packetParsers_ ? packetParsers_->parseGossipMessage(packet, currentGossip)
: GossipMessageParser::parse(packet, currentGossip);
if (!ok) return;
if (questDetailsOpen) return; // Don't reopen gossip while viewing quest
gossipWindowOpen = true;
vendorWindowOpen = false; // Close vendor if gossip opens

View file

@ -580,5 +580,54 @@ bool ClassicPacketParsers::parseGuildQueryResponse(network::Packet& packet, Guil
return true;
}
// ============================================================================
// Gossip — Classic has no menuId, and quest items lack questFlags + isRepeatable
// ============================================================================
bool ClassicPacketParsers::parseGossipMessage(network::Packet& packet, GossipMessageData& data) {
size_t remaining = packet.getSize() - packet.getReadPos();
if (remaining < 8 + 4 + 4) {
LOG_ERROR("Classic SMSG_GOSSIP_MESSAGE too small: ", remaining, " bytes");
return false;
}
data.npcGuid = packet.readUInt64();
// Classic: NO menuId field (WotLK adds uint32 menuId here)
data.menuId = 0;
data.titleTextId = packet.readUInt32();
uint32_t optionCount = packet.readUInt32();
data.options.clear();
data.options.reserve(optionCount);
for (uint32_t i = 0; i < optionCount; ++i) {
GossipOption opt;
opt.id = packet.readUInt32();
opt.icon = packet.readUInt8();
opt.isCoded = (packet.readUInt8() != 0);
opt.boxMoney = packet.readUInt32();
opt.text = packet.readString();
opt.boxText = packet.readString();
data.options.push_back(opt);
}
uint32_t questCount = packet.readUInt32();
data.quests.clear();
data.quests.reserve(questCount);
for (uint32_t i = 0; i < questCount; ++i) {
GossipQuestItem quest;
quest.questId = packet.readUInt32();
quest.questIcon = packet.readUInt32();
quest.questLevel = static_cast<int32_t>(packet.readUInt32());
// Classic: NO questFlags, NO isRepeatable
quest.questFlags = 0;
quest.isRepeatable = 0;
quest.title = packet.readString();
data.quests.push_back(quest);
}
LOG_INFO("Classic Gossip: ", optionCount, " options, ", questCount, " quests");
return true;
}
} // namespace game
} // namespace wowee