From 8561d5c58cfea1400018cdde44e678f3f92e6d4e Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 21:20:37 -0700 Subject: [PATCH] tbc: fix gossip message quest parsing for TBC 2.4.3 SMSG_GOSSIP_MESSAGE quest entries in TBC 2.4.3 do not include questFlags(u32) or isRepeatable(u8) that WotLK 3.3.5a added. The WotLK default parser reads these 5 bytes, causing all quest titles in gossip dialogs to be shifted/corrupted on TBC servers. Add TbcPacketParsers::parseGossipMessage() which parses quest entries without those fields, fixing NPC quest list display. --- include/game/packet_parsers.hpp | 2 ++ src/game/packet_parsers_tbc.cpp | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/include/game/packet_parsers.hpp b/include/game/packet_parsers.hpp index fea1f88a..35e90f6b 100644 --- a/include/game/packet_parsers.hpp +++ b/include/game/packet_parsers.hpp @@ -293,6 +293,8 @@ public: network::Packet buildUseItem(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid, uint32_t spellId = 0) override; // TBC 2.4.3 SMSG_MONSTER_MOVE has no unk byte after packed GUID (WotLK added it) bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) override; + // TBC 2.4.3 SMSG_GOSSIP_MESSAGE quests lack questFlags(u32)+isRepeatable(u8) (WotLK added them) + bool parseGossipMessage(network::Packet& packet, GossipMessageData& data) override; }; /** diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index 956c5af0..72f2bbf1 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -497,6 +497,53 @@ bool TbcPacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjectDa return false; } +// ============================================================================ +// TBC 2.4.3 SMSG_GOSSIP_MESSAGE +// Identical to WotLK except each quest entry lacks questFlags(u32) and +// isRepeatable(u8) that WotLK added. Without this override the WotLK parser +// reads those 5 bytes as part of the quest title, corrupting all gossip quests. +// ============================================================================ +bool TbcPacketParsers::parseGossipMessage(network::Packet& packet, GossipMessageData& data) { + if (packet.getSize() - packet.getReadPos() < 16) return false; + + data.npcGuid = packet.readUInt64(); + data.menuId = packet.readUInt32(); // TBC added menuId (Classic doesn't have it) + 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(packet.readUInt32()); + // TBC 2.4.3: NO questFlags(u32) and NO isRepeatable(u8) here + // WotLK adds these 5 bytes — reading them from TBC garbles the quest title + quest.questFlags = 0; + quest.isRepeatable = 0; + quest.title = normalizeWowTextTokens(packet.readString()); + data.quests.push_back(quest); + } + + LOG_INFO("[TBC] Gossip: ", optionCount, " options, ", questCount, " quests"); + return true; +} + // ============================================================================ // TBC 2.4.3 SMSG_MONSTER_MOVE // Identical to WotLK except WotLK added a uint8 unk byte immediately after the