fix: defer loot item notification until item name is known from server query

When SMSG_ITEM_PUSH_RESULT arrives for an item not yet in the cache, store
a PendingItemPushNotif and fire the 'Received: [item]' chat message only
after SMSG_ITEM_QUERY_SINGLE_RESPONSE resolves the name and quality, so the
notification always shows a proper item link instead of 'item #12345'.

Notifications that are already cached emit immediately as before; multiple
pending notifs for the same item are all flushed on the single response.
This commit is contained in:
Kelsi 2026-03-18 04:25:37 -07:00
parent 09b0bea981
commit c1765b6b39
2 changed files with 42 additions and 15 deletions

View file

@ -1956,22 +1956,22 @@ void GameHandler::handlePacket(network::Packet& packet) {
queryItemInfo(itemId, 0);
if (showInChat) {
std::string itemName = "item #" + std::to_string(itemId);
uint32_t quality = 1; // white default
if (const ItemQueryResponseData* info = getItemInfo(itemId)) {
if (!info->name.empty()) itemName = info->name;
quality = info->quality;
}
std::string link = buildItemLink(itemId, quality, itemName);
std::string msg = "Received: " + link;
if (count > 1) msg += " x" + std::to_string(count);
addSystemChatMessage(msg);
if (auto* renderer = core::Application::getInstance().getRenderer()) {
if (auto* sfx = renderer->getUiSoundManager())
sfx->playLootItem();
}
if (itemLootCallback_) {
itemLootCallback_(itemId, count, quality, itemName);
// Item info already cached — emit immediately.
std::string itemName = info->name.empty() ? ("item #" + std::to_string(itemId)) : info->name;
uint32_t quality = info->quality;
std::string link = buildItemLink(itemId, quality, itemName);
std::string msg = "Received: " + link;
if (count > 1) msg += " x" + std::to_string(count);
addSystemChatMessage(msg);
if (auto* renderer = core::Application::getInstance().getRenderer()) {
if (auto* sfx = renderer->getUiSoundManager())
sfx->playLootItem();
}
if (itemLootCallback_) itemLootCallback_(itemId, count, quality, itemName);
} else {
// Item info not yet cached; defer until SMSG_ITEM_QUERY_SINGLE_RESPONSE.
pendingItemPushNotifs_.push_back({itemId, count});
}
}
LOG_INFO("Item push: itemId=", itemId, " count=", count,
@ -14491,6 +14491,25 @@ void GameHandler::handleItemQueryResponse(network::Packet& packet) {
rebuildOnlineInventory();
maybeDetectVisibleItemLayout();
// Flush any deferred loot notifications waiting on this item's name/quality.
for (auto it = pendingItemPushNotifs_.begin(); it != pendingItemPushNotifs_.end(); ) {
if (it->itemId == data.entry) {
std::string itemName = data.name.empty() ? ("item #" + std::to_string(data.entry)) : data.name;
std::string link = buildItemLink(data.entry, data.quality, itemName);
std::string msg = "Received: " + link;
if (it->count > 1) msg += " x" + std::to_string(it->count);
addSystemChatMessage(msg);
if (auto* renderer = core::Application::getInstance().getRenderer()) {
if (auto* sfx = renderer->getUiSoundManager())
sfx->playLootItem();
}
if (itemLootCallback_) itemLootCallback_(data.entry, it->count, data.quality, itemName);
it = pendingItemPushNotifs_.erase(it);
} else {
++it;
}
}
// Selectively re-emit only players whose equipment references this item entry
const uint32_t resolvedEntry = data.entry;
for (const auto& [guid, entries] : otherPlayerVisibleItemEntries_) {