From 082f0f2d5732a1d005bbf6ec70004970cc06d8f8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 9 Sep 2021 17:11:59 +0100 Subject: [PATCH] Player: Generate an InventoryTransaction (with event) for crafting grid/cursor evacuation this fixes the crack in the armour that allows creative players to drop items even when all drops are being cancelled by plugins. closes #3284 --- src/player/Player.php | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/player/Player.php b/src/player/Player.php index 65c8798dd..7c77bd5a3 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -75,6 +75,11 @@ use pocketmine\form\Form; use pocketmine\form\FormValidationException; use pocketmine\inventory\Inventory; use pocketmine\inventory\PlayerCursorInventory; +use pocketmine\inventory\transaction\action\DropItemAction; +use pocketmine\inventory\transaction\InventoryTransaction; +use pocketmine\inventory\transaction\TransactionBuilderInventory; +use pocketmine\inventory\transaction\TransactionCancelledException; +use pocketmine\inventory\transaction\TransactionValidationException; use pocketmine\item\ConsumableItem; use pocketmine\item\Durable; use pocketmine\item\enchantment\EnchantmentInstance; @@ -2282,15 +2287,40 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ public function doCloseInventory() : void{ /** @var Inventory[] $inventories */ $inventories = [$this->craftingGrid, $this->cursorInventory]; + + $transaction = new InventoryTransaction($this); + $mainInventoryTransactionBuilder = new TransactionBuilderInventory($this->inventory); foreach($inventories as $inventory){ $contents = $inventory->getContents(); + if(count($contents) > 0){ - $drops = $this->inventory->addItem(...$contents); + $drops = $mainInventoryTransactionBuilder->addItem(...$contents); foreach($drops as $drop){ - $this->dropItem($drop); + $transaction->addAction(new DropItemAction($drop)); } - $inventory->clearAll(); + $clearedInventoryTransactionBuilder = new TransactionBuilderInventory($inventory); + $clearedInventoryTransactionBuilder->clearAll(); + foreach($clearedInventoryTransactionBuilder->generateActions() as $action){ + $transaction->addAction($action); + } + } + } + foreach($mainInventoryTransactionBuilder->generateActions() as $action){ + $transaction->addAction($action); + } + + if(count($transaction->getActions()) !== 0){ + try{ + $transaction->execute(); + $this->logger->debug("Successfully evacuated items from temporary inventories"); + }catch(TransactionCancelledException){ + $this->logger->debug("Plugin cancelled transaction evacuating items from temporary inventories; items will be destroyed"); + foreach($inventories as $inventory){ + $inventory->clearAll(); + } + }catch(TransactionValidationException $e){ + throw new AssumptionFailedError("This server-generated transaction should never be invalid", 0, $e); } }