fix: visible item field base 284→408 (was reading quest log, not equipment)

PLAYER_VISIBLE_ITEM_1_ENTRYID for WotLK 3.3.5a is at UNIT_END(148) + 260
= field index 408 with stride 2. The previous default of 284 (UNIT_END+136)
was in the quest log field range, causing item IDs like "Lesser Invisibility
Potion" and "Deathstalker Report" to be read as equipment entries.

This was the root cause of other players appearing naked — item queries
returned valid responses but for the WRONG items (quest log entries instead
of equipment), so displayInfoIds were consumable/quest item appearances.

The heuristic auto-detection still overrides for Classic/TBC (different
stride per expansion), so this only affects the WotLK default before
detection runs.

Also filter addon whispers (GearScore GS_*, DBM, oRA, BigWigs, tab-prefixed)
from chat display — these are invisible in the real WoW client.
This commit is contained in:
Kelsi 2026-03-28 10:49:00 -07:00
parent 1bcb05aac4
commit f4a2a631ab
3 changed files with 24 additions and 6 deletions

View file

@ -196,6 +196,22 @@ void ChatHandler::handleMessageChat(network::Packet& packet) {
}
}
// Filter addon-to-addon whispers (GearScore, DBM, oRA, etc.) from player chat.
// These are invisible in the real WoW client.
if (data.type == ChatType::WHISPER || data.type == ChatType::WHISPER_INFORM) {
const auto& msg = data.message;
if (msg.size() >= 3 && (
msg.rfind("GS_", 0) == 0 || // GearScore
msg.rfind("DVNE", 0) == 0 || // DBM (DeadlyBossMods)
msg.rfind("oRA", 0) == 0 || // oRA raid addon
msg.rfind("BWVQ", 0) == 0 || // BigWigs
msg.rfind("AVR", 0) == 0 || // AVR (Augmented Virtual Reality)
msg.rfind("\t", 0) == 0 || // Tab-prefixed addon messages
(msg.size() > 4 && static_cast<unsigned char>(msg[0]) > 127))) { // Binary data
return; // Silently discard addon whisper
}
}
// Add to chat history
chatHistory_.push_back(data);
if (chatHistory_.size() > maxChatHistory_) {

View file

@ -2341,8 +2341,9 @@ void InventoryHandler::handleItemQueryResponse(network::Packet& packet) {
}
owner_.pendingItemQueries_.erase(data.entry);
LOG_DEBUG("handleItemQueryResponse: entry=", data.entry, " name='", data.name,
"' displayInfoId=", data.displayInfoId, " pending=", owner_.pendingItemQueries_.size());
LOG_WARNING("handleItemQueryResponse: entry=", data.entry, " name='", data.name,
"' displayInfoId=", data.displayInfoId, " valid=", data.valid,
" pending=", owner_.pendingItemQueries_.size());
if (data.valid) {
owner_.itemInfoCache_[data.entry] = data;
@ -3105,7 +3106,7 @@ void InventoryHandler::updateOtherPlayerVisibleItems(uint64_t guid, const std::m
int nonZero = 0;
for (uint32_t e : newEntries) { if (e != 0) nonZero++; }
if (nonZero > 0) {
LOG_INFO("updateOtherPlayerVisibleItems: guid=0x", std::hex, guid, std::dec,
LOG_WARNING("updateOtherPlayerVisibleItems: guid=0x", std::hex, guid, std::dec,
" nonZero=", nonZero, " base=", base, " stride=", stride,
" head=", newEntries[0], " shoulders=", newEntries[2],
" chest=", newEntries[4], " legs=", newEntries[6],
@ -3166,7 +3167,7 @@ void InventoryHandler::emitOtherPlayerEquipment(uint64_t guid) {
resolved++;
}
LOG_INFO("emitOtherPlayerEquipment: guid=0x", std::hex, guid, std::dec,
LOG_WARNING("emitOtherPlayerEquipment: guid=0x", std::hex, guid, std::dec,
" entries=", (anyEntry ? "yes" : "none"),
" resolved=", resolved, " unresolved=", unresolved,
" head=", displayIds[0], " shoulders=", displayIds[2],