From 020e0168534f033da5bda1340b219f5e14846741 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 29 Mar 2026 17:44:46 -0700 Subject: [PATCH] fix: quest reward items stuck as 'Item #ID' due to stale pending queries Two fixes for item name resolution: 1. Clear entry from pendingItemQueries_ even when response parsing fails. Previously a malformed response left the entry stuck in pending forever, blocking all retries so the UI permanently showed "Item 12345". 2. Add 5-second periodic cleanup of pendingItemQueries_ so lost/dropped responses don't permanently block item info resolution. --- include/game/game_handler.hpp | 1 + src/game/game_handler.cpp | 11 +++++++++++ src/game/inventory_handler.cpp | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index ff5adc36..6504402b 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -2455,6 +2455,7 @@ private: std::unordered_map onlineItems_; std::unordered_map itemInfoCache_; std::unordered_set pendingItemQueries_; + float pendingItemQueryTimer_ = 0.0f; // Deferred SMSG_ITEM_PUSH_RESULT notifications for items whose info wasn't // cached at arrival time; emitted once the query response arrives. diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 766dec0a..f9906c8d 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1116,6 +1116,17 @@ for (auto& [guid, entity] : entityController_->getEntityManager().getEntities()) void GameHandler::updateTimers(float deltaTime) { if (spellHandler_) spellHandler_->updateTimers(deltaTime); + // Periodically clear stale pending item queries so they can be retried. + // Without this, a lost/malformed response leaves the entry stuck forever. + pendingItemQueryTimer_ += deltaTime; + if (pendingItemQueryTimer_ >= 5.0f) { + pendingItemQueryTimer_ = 0.0f; + if (!pendingItemQueries_.empty()) { + LOG_DEBUG("Clearing ", pendingItemQueries_.size(), " stale pending item queries"); + pendingItemQueries_.clear(); + } + } + if (auctionSearchDelayTimer_ > 0.0f) { auctionSearchDelayTimer_ -= deltaTime; if (auctionSearchDelayTimer_ < 0.0f) auctionSearchDelayTimer_ = 0.0f; diff --git a/src/game/inventory_handler.cpp b/src/game/inventory_handler.cpp index 09e4e622..b0ff75ca 100644 --- a/src/game/inventory_handler.cpp +++ b/src/game/inventory_handler.cpp @@ -2352,6 +2352,13 @@ void InventoryHandler::handleItemQueryResponse(network::Packet& packet) { ? owner_.packetParsers_->parseItemQueryResponse(packet, data) : ItemQueryResponseParser::parse(packet, data); if (!parsed) { + // Extract entry from raw packet so we can clear the pending query even on parse failure. + // Without this, the entry stays in pendingItemQueries_ forever, blocking retries. + if (packet.getSize() >= 4) { + packet.setReadPos(0); + uint32_t rawEntry = packet.readUInt32() & ~0x80000000u; + owner_.pendingItemQueries_.erase(rawEntry); + } LOG_WARNING("handleItemQueryResponse: parse failed, size=", packet.getSize()); return; }