diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 37f23e158..12859be46 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -328,6 +328,9 @@ class InGamePacketHandler extends PacketHandler{ if(count($packet->trData->getActions()) > 50){ throw new PacketHandlingException("Too many actions in inventory transaction"); } + if(count($packet->requestChangedSlots) > 10){ + throw new PacketHandlingException("Too many slot sync requests in inventory transaction"); + } $this->inventoryManager->setCurrentItemStackRequestId($packet->requestId); $this->inventoryManager->addRawPredictedSlotChanges($packet->trData->getActions()); @@ -347,6 +350,21 @@ class InGamePacketHandler extends PacketHandler{ } $this->inventoryManager->syncMismatchedPredictedSlotChanges(); + + //requestChangedSlots asks the server to always send out the contents of the specified slots, even if they + //haven't changed. Handling these is necessary to ensure the client inventory stays in sync if the server + //rejects the transaction. The most common example of this is equipping armor by right-click, which doesn't send + //a legacy prediction action for the destination armor slot. + foreach($packet->requestChangedSlots as $containerInfo){ + $windowId = ItemStackContainerIdTranslator::translate($containerInfo->getContainerId(), $this->inventoryManager->getCurrentWindowId()); + foreach($containerInfo->getChangedSlotIndexes() as $slot){ + $inventoryAndSlot = $this->inventoryManager->locateWindowAndSlot($windowId, $slot); + if($inventoryAndSlot !== null){ //trigger the normal slot sync logic + $this->inventoryManager->onSlotChange($inventoryAndSlot[0], $inventoryAndSlot[1]); + } + } + } + $this->inventoryManager->setCurrentItemStackRequestId(null); return $result; }