diff --git a/include/game/inventory.hpp b/include/game/inventory.hpp index 6ae07a2d..f510d6ab 100644 --- a/include/game/inventory.hpp +++ b/include/game/inventory.hpp @@ -70,6 +70,8 @@ class Inventory { public: static constexpr int BACKPACK_SLOTS = 16; static constexpr int KEYRING_SLOTS = 32; + // WoW slot layout: 0-22 are equipment (head, neck, ... tabard, mainhand, offhand, ranged, ammo). + // Backpack inventory starts at slot 23 in bag 0xFF, so packet slot = NUM_EQUIP_SLOTS + backpackIndex. static constexpr int NUM_EQUIP_SLOTS = 23; static constexpr int NUM_BAG_SLOTS = 4; static constexpr int MAX_BAG_SIZE = 36; diff --git a/src/game/chat_handler.cpp b/src/game/chat_handler.cpp index ef3484bd..ec9d6e46 100644 --- a/src/game/chat_handler.cpp +++ b/src/game/chat_handler.cpp @@ -435,11 +435,7 @@ void ChatHandler::handleChannelNotify(network::Packet& packet) { switch (data.notifyType) { case ChannelNotifyType::YOU_JOINED: { - bool found = false; - for (const auto& ch : joinedChannels_) { - if (ch == data.channelName) { found = true; break; } - } - if (!found) { + if (std::find(joinedChannels_.begin(), joinedChannels_.end(), data.channelName) == joinedChannels_.end()) { joinedChannels_.push_back(data.channelName); } MessageChatData msg; @@ -461,11 +457,9 @@ void ChatHandler::handleChannelNotify(network::Packet& packet) { break; } case ChannelNotifyType::PLAYER_ALREADY_MEMBER: { - bool found = false; - for (const auto& ch : joinedChannels_) { - if (ch == data.channelName) { found = true; break; } - } - if (!found) { + // Server confirms we're in this channel but our local list doesn't have it yet — + // can happen after reconnect or if the join notification was missed. + if (std::find(joinedChannels_.begin(), joinedChannels_.end(), data.channelName) == joinedChannels_.end()) { joinedChannels_.push_back(data.channelName); LOG_INFO("Already in channel: ", data.channelName); } diff --git a/src/game/inventory.cpp b/src/game/inventory.cpp index 83fcc5fe..d2f0f488 100644 --- a/src/game/inventory.cpp +++ b/src/game/inventory.cpp @@ -239,7 +239,7 @@ std::vector Inventory::computeSortSwaps() const { entries.reserve(BACKPACK_SLOTS + NUM_BAG_SLOTS * MAX_BAG_SIZE); for (int i = 0; i < BACKPACK_SLOTS; ++i) { - entries.push_back({0xFF, static_cast(23 + i), + entries.push_back({0xFF, static_cast(NUM_EQUIP_SLOTS + i), backpack[i].item.itemId, backpack[i].item.quality, backpack[i].item.stackCount}); } diff --git a/src/game/inventory_handler.cpp b/src/game/inventory_handler.cpp index cc646130..5e05167b 100644 --- a/src/game/inventory_handler.cpp +++ b/src/game/inventory_handler.cpp @@ -1047,7 +1047,7 @@ void InventoryHandler::autoEquipItemBySlot(int backpackIndex) { if (slot.empty()) return; if (owner_.state == WorldState::IN_WORLD && owner_.socket) { - auto packet = AutoEquipItemPacket::build(0xFF, static_cast(23 + backpackIndex)); + auto packet = AutoEquipItemPacket::build(0xFF, static_cast(Inventory::NUM_EQUIP_SLOTS + backpackIndex)); owner_.socket->send(packet); } } @@ -1087,8 +1087,8 @@ void InventoryHandler::useItemBySlot(int backpackIndex) { " spellId=", useSpellId, " spellCount=", info->spells.size()); } auto packet = owner_.packetParsers_ - ? owner_.packetParsers_->buildUseItem(0xFF, static_cast(23 + backpackIndex), itemGuid, useSpellId) - : UseItemPacket::build(0xFF, static_cast(23 + backpackIndex), itemGuid, useSpellId); + ? owner_.packetParsers_->buildUseItem(0xFF, static_cast(Inventory::NUM_EQUIP_SLOTS + backpackIndex), itemGuid, useSpellId) + : UseItemPacket::build(0xFF, static_cast(Inventory::NUM_EQUIP_SLOTS + backpackIndex), itemGuid, useSpellId); owner_.socket->send(packet); } else if (itemGuid == 0) { LOG_WARNING("useItemBySlot: itemGuid=0 for item='", slot.item.name, @@ -1145,8 +1145,8 @@ void InventoryHandler::openItemBySlot(int backpackIndex) { if (backpackIndex < 0 || backpackIndex >= owner_.inventory.getBackpackSize()) return; if (owner_.inventory.getBackpackSlot(backpackIndex).empty()) return; if (owner_.state != WorldState::IN_WORLD || !owner_.socket) return; - auto packet = OpenItemPacket::build(0xFF, static_cast(23 + backpackIndex)); - LOG_INFO("openItemBySlot: CMSG_OPEN_ITEM bag=0xFF slot=", (23 + backpackIndex)); + auto packet = OpenItemPacket::build(0xFF, static_cast(Inventory::NUM_EQUIP_SLOTS + backpackIndex)); + LOG_INFO("openItemBySlot: CMSG_OPEN_ITEM bag=0xFF slot=", (Inventory::NUM_EQUIP_SLOTS + backpackIndex)); owner_.socket->send(packet); } @@ -1181,7 +1181,7 @@ void InventoryHandler::splitItem(uint8_t srcBag, uint8_t srcSlot, uint8_t count) int freeBp = owner_.inventory.findFreeBackpackSlot(); if (freeBp >= 0) { uint8_t dstBag = 0xFF; - uint8_t dstSlot = static_cast(23 + freeBp); + uint8_t dstSlot = static_cast(Inventory::NUM_EQUIP_SLOTS + freeBp); LOG_INFO("splitItem: src(bag=", (int)srcBag, " slot=", (int)srcSlot, ") count=", (int)count, " -> dst(bag=0xFF slot=", (int)dstSlot, ")"); auto packet = SplitItemPacket::build(srcBag, srcSlot, dstBag, dstSlot, count); @@ -1248,7 +1248,7 @@ void InventoryHandler::unequipToBackpack(EquipSlot equipSlot) { uint8_t srcBag = 0xFF; uint8_t srcSlot = static_cast(equipSlot); uint8_t dstBag = 0xFF; - uint8_t dstSlot = static_cast(23 + freeSlot); + uint8_t dstSlot = static_cast(Inventory::NUM_EQUIP_SLOTS + freeSlot); LOG_INFO("UnequipToBackpack: equipSlot=", (int)srcSlot, " -> backpackIndex=", freeSlot, " (dstSlot=", (int)dstSlot, ")"); @@ -1538,7 +1538,7 @@ bool InventoryHandler::attachItemFromBackpack(int backpackIndex) { mailAttachments_[i].itemGuid = itemGuid; mailAttachments_[i].item = slot.item; mailAttachments_[i].srcBag = 0xFF; - mailAttachments_[i].srcSlot = static_cast(23 + backpackIndex); + mailAttachments_[i].srcSlot = static_cast(Inventory::NUM_EQUIP_SLOTS + backpackIndex); return true; } } @@ -1730,7 +1730,7 @@ void InventoryHandler::withdrawItem(uint8_t srcBag, uint8_t srcSlot) { owner_.addSystemChatMessage("Inventory is full."); return; } - uint8_t dstSlot = static_cast(23 + freeSlot); + uint8_t dstSlot = static_cast(Inventory::NUM_EQUIP_SLOTS + freeSlot); auto packet = SwapItemPacket::build(0xFF, dstSlot, srcBag, srcSlot); owner_.socket->send(packet); } @@ -2225,7 +2225,7 @@ void InventoryHandler::useEquipmentSet(uint32_t setId) { for (int bp = 0; bp < 16 && !found; ++bp) { if (owner_.getBackpackItemGuid(bp) == itemGuid) { srcBag = 0xFF; - srcSlot = static_cast(23 + bp); + srcSlot = static_cast(Inventory::NUM_EQUIP_SLOTS + bp); found = true; } } @@ -2355,6 +2355,8 @@ void InventoryHandler::handleItemQueryResponse(network::Packet& packet) { // Without this, the entry stays in pendingItemQueries_ forever, blocking retries. if (packet.getSize() >= 4) { packet.setReadPos(0); + // High bit indicates a negative (invalid/missing) item entry response; + // mask it off so we can still clear the pending query by entry ID. uint32_t rawEntry = packet.readUInt32() & ~0x80000000u; owner_.pendingItemQueries_.erase(rawEntry); } diff --git a/src/game/spell_handler.cpp b/src/game/spell_handler.cpp index 0502f84e..a265e33a 100644 --- a/src/game/spell_handler.cpp +++ b/src/game/spell_handler.cpp @@ -471,8 +471,8 @@ void SpellHandler::useItemBySlot(int backpackIndex) { if (itemGuid != 0 && owner_.state == WorldState::IN_WORLD && owner_.socket) { uint32_t useSpellId = findOnUseSpellId(slot.item.itemId); auto packet = owner_.packetParsers_ - ? owner_.packetParsers_->buildUseItem(0xFF, static_cast(23 + backpackIndex), itemGuid, useSpellId) - : UseItemPacket::build(0xFF, static_cast(23 + backpackIndex), itemGuid, useSpellId); + ? owner_.packetParsers_->buildUseItem(0xFF, static_cast(Inventory::NUM_EQUIP_SLOTS + backpackIndex), itemGuid, useSpellId) + : UseItemPacket::build(0xFF, static_cast(Inventory::NUM_EQUIP_SLOTS + backpackIndex), itemGuid, useSpellId); owner_.socket->send(packet); } else if (itemGuid == 0) { owner_.addSystemChatMessage("Cannot use that item right now.");