From a4a39c7f0fd4f15c2bbe71c0d5fa85e807baa726 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 6 Feb 2026 12:08:47 -0800 Subject: [PATCH] Fix quest details parser, ImGui ID conflict, and gossip reopen guard Quest details parser now reads all 6 choice + 4 reward item slots (matching AzerothCore's fixed-size arrays) with bounds checking at every step. Use loop index for quest ImGui IDs instead of questId to avoid conflicts. Don't reopen gossip window while quest details are showing. --- src/game/game_handler.cpp | 1 + src/game/world_packets.cpp | 45 ++++++++++++++++++++++++-------------- src/ui/game_screen.cpp | 5 +++-- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index cefa6ece..8880aae4 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -3623,6 +3623,7 @@ void GameHandler::handleLootRemoved(network::Packet& packet) { void GameHandler::handleGossipMessage(network::Packet& packet) { if (!GossipMessageParser::parse(packet, currentGossip)) return; + if (questDetailsOpen) return; // Don't reopen gossip while viewing quest gossipWindowOpen = true; vendorWindowOpen = false; // Close vendor if gossip opens } diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 8668bc65..a1c0408d 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -1836,38 +1836,51 @@ network::Packet QuestgiverAcceptQuestPacket::build(uint64_t npcGuid, uint32_t qu } bool QuestDetailsParser::parse(network::Packet& packet, QuestDetailsData& data) { - if (packet.getSize() < 20) return false; + if (packet.getSize() < 28) return false; data.npcGuid = packet.readUInt64(); /*informUnit*/ packet.readUInt64(); data.questId = packet.readUInt32(); data.title = packet.readString(); data.details = packet.readString(); data.objectives = packet.readString(); + + if (packet.getReadPos() + 10 > packet.getSize()) { + LOG_INFO("Quest details (short): id=", data.questId, " title='", data.title, "'"); + return true; + } + /*activateAccept*/ packet.readUInt8(); /*flags*/ packet.readUInt32(); data.suggestedPlayers = packet.readUInt32(); /*isFinished*/ packet.readUInt8(); - // Reward choice items - uint32_t choiceCount = packet.readUInt32(); - for (uint32_t i = 0; i < choiceCount && i < 6; i++) { - packet.readUInt32(); // itemId - packet.readUInt32(); // count - packet.readUInt32(); // displayInfo + // Reward choice items: server always writes 6 entries (QUEST_REWARD_CHOICES_COUNT) + if (packet.getReadPos() + 4 <= packet.getSize()) { + /*choiceCount*/ packet.readUInt32(); + for (int i = 0; i < 6; i++) { + if (packet.getReadPos() + 12 > packet.getSize()) break; + packet.readUInt32(); // itemId + packet.readUInt32(); // count + packet.readUInt32(); // displayInfo + } } - // Reward items - uint32_t rewardCount = packet.readUInt32(); - for (uint32_t i = 0; i < rewardCount && i < 4; i++) { - packet.readUInt32(); // itemId - packet.readUInt32(); // count - packet.readUInt32(); // displayInfo + // Reward items: server always writes 4 entries (QUEST_REWARDS_COUNT) + if (packet.getReadPos() + 4 <= packet.getSize()) { + /*rewardCount*/ packet.readUInt32(); + for (int i = 0; i < 4; i++) { + if (packet.getReadPos() + 12 > packet.getSize()) break; + packet.readUInt32(); // itemId + packet.readUInt32(); // count + packet.readUInt32(); // displayInfo + } } - data.rewardMoney = packet.readUInt32(); - if (packet.getReadPos() < packet.getSize()) { + // Money and XP rewards + if (packet.getReadPos() + 4 <= packet.getSize()) + data.rewardMoney = packet.readUInt32(); + if (packet.getReadPos() + 4 <= packet.getSize()) data.rewardXp = packet.readUInt32(); - } LOG_INFO("Quest details: id=", data.questId, " title='", data.title, "'"); return true; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 86989402..f3e25a27 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -1610,8 +1610,9 @@ void GameScreen::renderGossipWindow(game::GameHandler& gameHandler) { ImGui::Spacing(); ImGui::Separator(); ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.3f, 1.0f), "Quests:"); - for (const auto& quest : gossip.quests) { - ImGui::PushID(static_cast(quest.questId)); + for (size_t qi = 0; qi < gossip.quests.size(); qi++) { + const auto& quest = gossip.quests[qi]; + ImGui::PushID(static_cast(qi)); char qlabel[256]; snprintf(qlabel, sizeof(qlabel), "[%d] %s", quest.questLevel, quest.title.c_str()); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.3f, 1.0f));