From 25138b56482bf348024bc0b91a51611f637e4489 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 18 Mar 2026 06:06:29 -0700 Subject: [PATCH] fix: use CMSG_OPEN_ITEM for locked containers (lockboxes) Right-clicking a locked container (e.g. Dead-Tooth's Strong Box) was sending CMSG_USE_ITEM with spellId=0, which the server rejects. Locked containers (itemClass==1, inventoryType==0) now send CMSG_OPEN_ITEM instead, letting the server auto-check the keyring for the required key. --- include/game/game_handler.hpp | 3 +++ include/game/world_packets.hpp | 6 ++++++ src/game/game_handler.cpp | 20 ++++++++++++++++++++ src/game/world_packets.cpp | 7 +++++++ src/ui/inventory_screen.cpp | 16 ++++++++++++++-- 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index fc592b4e..47997040 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -2007,6 +2007,9 @@ public: void autoEquipItemInBag(int bagIndex, int slotIndex); void useItemBySlot(int backpackIndex); void useItemInBag(int bagIndex, int slotIndex); + // CMSG_OPEN_ITEM — for locked containers (lockboxes); server checks keyring automatically + void openItemBySlot(int backpackIndex); + void openItemInBag(int bagIndex, int slotIndex); void destroyItem(uint8_t bag, uint8_t slot, uint8_t count = 1); void swapContainerItems(uint8_t srcBag, uint8_t srcSlot, uint8_t dstBag, uint8_t dstSlot); void swapBagSlots(int srcBagIndex, int dstBagIndex); diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index c7fc0ef4..c2aa581f 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -2027,6 +2027,12 @@ public: static network::Packet build(uint8_t bagIndex, uint8_t slotIndex, uint64_t itemGuid, uint32_t spellId = 0); }; +/** CMSG_OPEN_ITEM packet builder (for locked containers / lockboxes) */ +class OpenItemPacket { +public: + static network::Packet build(uint8_t bagIndex, uint8_t slotIndex); +}; + /** CMSG_AUTOEQUIP_ITEM packet builder */ class AutoEquipItemPacket { public: diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 465e6c5c..3591d97a 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -21165,6 +21165,26 @@ void GameHandler::useItemInBag(int bagIndex, int slotIndex) { } } +void GameHandler::openItemBySlot(int backpackIndex) { + if (backpackIndex < 0 || backpackIndex >= inventory.getBackpackSize()) return; + if (inventory.getBackpackSlot(backpackIndex).empty()) return; + if (state != WorldState::IN_WORLD || !socket) return; + auto packet = OpenItemPacket::build(0xFF, static_cast(23 + backpackIndex)); + LOG_INFO("openItemBySlot: CMSG_OPEN_ITEM bag=0xFF slot=", (23 + backpackIndex)); + socket->send(packet); +} + +void GameHandler::openItemInBag(int bagIndex, int slotIndex) { + if (bagIndex < 0 || bagIndex >= inventory.NUM_BAG_SLOTS) return; + if (slotIndex < 0 || slotIndex >= inventory.getBagSize(bagIndex)) return; + if (inventory.getBagSlot(bagIndex, slotIndex).empty()) return; + if (state != WorldState::IN_WORLD || !socket) return; + uint8_t wowBag = static_cast(19 + bagIndex); + auto packet = OpenItemPacket::build(wowBag, static_cast(slotIndex)); + LOG_INFO("openItemInBag: CMSG_OPEN_ITEM bag=", (int)wowBag, " slot=", slotIndex); + socket->send(packet); +} + void GameHandler::useItemById(uint32_t itemId) { if (itemId == 0) return; LOG_DEBUG("useItemById: searching for itemId=", itemId); diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index aaf18ca2..50c1208a 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -4271,6 +4271,13 @@ network::Packet UseItemPacket::build(uint8_t bagIndex, uint8_t slotIndex, uint64 return packet; } +network::Packet OpenItemPacket::build(uint8_t bagIndex, uint8_t slotIndex) { + network::Packet packet(wireOpcode(Opcode::CMSG_OPEN_ITEM)); + packet.writeUInt8(bagIndex); + packet.writeUInt8(slotIndex); + return packet; +} + network::Packet AutoEquipItemPacket::build(uint8_t srcBag, uint8_t srcSlot) { network::Packet packet(wireOpcode(Opcode::CMSG_AUTOEQUIP_ITEM)); packet.writeUInt8(srcBag); diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index 23c1ae92..b4e2ac89 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -2356,7 +2356,14 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite } else if (item.inventoryType > 0) { gameHandler_->autoEquipItemBySlot(backpackIndex); } else { - gameHandler_->useItemBySlot(backpackIndex); + // itemClass==1 (Container) with inventoryType==0 means a lockbox; + // use CMSG_OPEN_ITEM so the server checks keyring automatically. + auto* info = gameHandler_->getItemInfo(item.itemId); + if (info && info->valid && info->itemClass == 1) { + gameHandler_->openItemBySlot(backpackIndex); + } else { + gameHandler_->useItemBySlot(backpackIndex); + } } } else if (kind == SlotKind::BACKPACK && isBagSlot) { LOG_INFO("Right-click bag item: name='", item.name, @@ -2369,7 +2376,12 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite } else if (item.inventoryType > 0) { gameHandler_->autoEquipItemInBag(bagIndex, bagSlotIndex); } else { - gameHandler_->useItemInBag(bagIndex, bagSlotIndex); + auto* info = gameHandler_->getItemInfo(item.itemId); + if (info && info->valid && info->itemClass == 1) { + gameHandler_->openItemInBag(bagIndex, bagSlotIndex); + } else { + gameHandler_->useItemInBag(bagIndex, bagSlotIndex); + } } } }