From 68e39a21928fc45e8e073efb4a10ab759ed64151 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Feb 2026 18:08:00 -0800 Subject: [PATCH] Fix vendor packet parsing and Tokens display Auto-detect whether SMSG_LIST_INVENTORY has 7 fields (28 bytes/item, no extendedCost) or 8 fields (32 bytes/item) per item from packet size. Servers that omit extendedCost caused every item after the first to have garbage prices due to misaligned field reads. Also remove the [+Tokens] hybrid indicator; only show [Tokens] on pure token-purchased items (buyPrice==0 && extendedCost!=0). --- src/game/world_packets.cpp | 10 ++++++++-- src/ui/game_screen.cpp | 7 ++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index ab160dea..1dc41409 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -3080,6 +3080,12 @@ bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data return true; } + // Auto-detect whether server sends 7 fields (28 bytes/item) or 8 fields (32 bytes/item). + // Some servers omit the extendedCost field entirely; reading 8 fields on a 7-field packet + // misaligns every item after the first and produces garbage prices. + size_t remaining = packet.getSize() - packet.getReadPos(); + bool hasExtendedCost = (remaining >= static_cast(itemCount) * 32); + data.items.reserve(itemCount); for (uint8_t i = 0; i < itemCount; ++i) { VendorItem item; @@ -3090,11 +3096,11 @@ bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data item.buyPrice = packet.readUInt32(); item.durability = packet.readUInt32(); item.stackCount = packet.readUInt32(); - item.extendedCost = packet.readUInt32(); + item.extendedCost = hasExtendedCost ? packet.readUInt32() : 0; data.items.push_back(item); } - LOG_INFO("Vendor inventory: ", (int)itemCount, " items"); + LOG_INFO("Vendor inventory: ", (int)itemCount, " items (extendedCost: ", hasExtendedCost ? "yes" : "no", ")"); return true; } diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 98f32ce4..56f07596 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -4813,7 +4813,8 @@ void GameScreen::renderVendorWindow(game::GameHandler& gameHandler) { } ImGui::TableSetColumnIndex(1); - if (item.extendedCost != 0 && item.buyPrice == 0) { + if (item.buyPrice == 0 && item.extendedCost != 0) { + // Token-only item (no gold cost) ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "[Tokens]"); } else { uint32_t g = item.buyPrice / 10000; @@ -4823,10 +4824,6 @@ void GameScreen::renderVendorWindow(game::GameHandler& gameHandler) { if (!canAfford) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.3f, 0.3f, 1.0f)); ImGui::Text("%ug %us %uc", g, s, c); if (!canAfford) ImGui::PopStyleColor(); - if (item.extendedCost != 0) { - ImGui::SameLine(); - ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "[+Tokens]"); - } } ImGui::TableSetColumnIndex(2);