diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index d916f3ea97..dc0b7bcba2 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -73,7 +73,6 @@ use pocketmine\event\server\DataPacketSendEvent; use pocketmine\event\TextContainer; use pocketmine\event\Timings; use pocketmine\event\TranslationContainer; -use pocketmine\inventory\BaseTransaction; use pocketmine\inventory\BigShapedRecipe; use pocketmine\inventory\BigShapelessRecipe; use pocketmine\inventory\FurnaceInventory; @@ -82,7 +81,7 @@ use pocketmine\inventory\PlayerCursorInventory; use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\ShapedRecipe; use pocketmine\inventory\ShapelessRecipe; -use pocketmine\inventory\SimpleTransactionGroup; +use pocketmine\inventory\transaction\SimpleInventoryTransaction; use pocketmine\item\Item; use pocketmine\level\ChunkLoader; use pocketmine\level\format\Chunk; @@ -120,7 +119,6 @@ use pocketmine\network\mcpe\protocol\ContainerClosePacket; use pocketmine\network\mcpe\protocol\CraftingEventPacket; use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; -use pocketmine\network\mcpe\protocol\DropItemPacket; use pocketmine\network\mcpe\protocol\EntityEventPacket; use pocketmine\network\mcpe\protocol\InteractPacket; use pocketmine\network\mcpe\protocol\InventoryTransactionPacket; @@ -235,8 +233,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public $speed = null; public $achievements = []; - /** @var SimpleTransactionGroup */ - protected $currentTransaction = null; + public $craftingType = 0; //0 = 2x2 crafting, 1 = 3x3 crafting, 2 = stonecutter /** @var PlayerCursorInventory */ @@ -2161,28 +2158,38 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{ switch($packet->transactionData->transactionType){ case InventoryTransactionPacket::TYPE_NORMAL: - $transaction = new SimpleTransactionGroup($this); + $transaction = new SimpleInventoryTransaction($this); foreach($packet->actions as $action){ - if($action->inventorySource->sourceType !== InventoryTransactionPacket::SOURCE_CONTAINER){ - return false; //TODO: handle other source types - } - - if($action->inventorySource->containerId === ContainerIds::ARMOR){ - $transaction->addTransaction(new BaseTransaction($this->inventory, $action->inventorySlot + $this->inventory->getSize(), $action->oldItem, $action->newItem)); - continue; - } - - $transaction->addTransaction(new BaseTransaction($this->windowIndex[$action->inventorySource->containerId], $action->inventorySlot, $action->oldItem, $action->newItem)); + $transaction->addAction($action); } if(!$transaction->execute()){ - //need to resend/revert/whatever (TODO) + foreach($transaction->getInventories() as $inventory){ + $inventory->sendContents($this); + if($inventory instanceof PlayerInventory){ + $inventory->sendArmorContents($this); + } + } + + //TODO: check more stuff that might need reversion return false; //oops! } //TODO: fix achievement for getting iron from furnace + return true; + case InventoryTransactionPacket::TYPE_MISMATCH: + if(count($packet->actions) > 0){ + $this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions)); + } + foreach($this->windowIndex as $id => $inventory){ + $inventory->sendContents($this); + if($inventory instanceof PlayerInventory){ + $inventory->sendArmorContents($this); + } + } + $this->inventory->sendHotbar(); return true; case InventoryTransactionPacket::TYPE_USE_ITEM: $blockVector = new Vector3($packet->transactionData->x, $packet->transactionData->y, $packet->transactionData->z); @@ -2463,6 +2470,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ case InventoryTransactionPacket::TYPE_RELEASE_ITEM: //TODO break; + default: + $this->inventory->sendContents($this); + break; } return false; //TODO @@ -2805,25 +2815,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } - public function handleDropItem(DropItemPacket $packet) : bool{ - if($this->spawned === false or !$this->isAlive()){ + /** + * Drops an item on the ground in front of the player. Returns if the item drop was successful. + * + * @param Item $item + * @return bool if the item was dropped or if the item was null + */ + public function dropItem(Item $item){ + if(!$this->spawned or !$this->isAlive()){ + return false; + } + + if($item->isNull()){ + $this->server->getLogger()->debug($this->getName() . " attempted to drop a null item (" . $item . ")"); return true; } - if($packet->item->getId() === Item::AIR){ - // Windows 10 Edition drops the contents of the crafting grid on container close - including air. - return true; - } - - $item = $this->inventory->getItemInHand(); - $ev = new PlayerDropItemEvent($this, $item); - $this->server->getPluginManager()->callEvent($ev); + $this->server->getPluginManager()->callEvent($ev = new PlayerDropItemEvent($this, $item)); if($ev->isCancelled()){ - $this->inventory->sendContents($this); - return true; + return false; } - $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); $motion = $this->getDirectionVector()->multiply(0.4); $this->level->dropItem($this->add(0, 1.3, 0), $item, $motion, 40); @@ -2839,7 +2851,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } $this->craftingType = 0; - $this->currentTransaction = null; + if(isset($this->windowIndex[$packet->windowId])){ $this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this)); $this->removeWindow($this->windowIndex[$packet->windowId]); @@ -3477,12 +3489,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->perm->clearPermissions(); $this->perm = null; } - - if($this->inventory !== null){ - $this->inventory = null; - $this->currentTransaction = null; - } - }catch(\Throwable $e){ $this->server->getLogger()->logException($e); }finally{ @@ -3809,6 +3815,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return ContainerIds::NONE; } + /** + * @param int $windowId + * + * @return Inventory|null + */ + public function getWindow(int $windowId){ + return $this->windowIndex[$windowId] ?? null; + } + /** * Returns the created/existing window id * diff --git a/src/pocketmine/event/inventory/InventoryTransactionEvent.php b/src/pocketmine/event/inventory/InventoryTransactionEvent.php index 6be46f46f3..2dcb531155 100644 --- a/src/pocketmine/event/inventory/InventoryTransactionEvent.php +++ b/src/pocketmine/event/inventory/InventoryTransactionEvent.php @@ -25,7 +25,7 @@ namespace pocketmine\event\inventory; use pocketmine\event\Cancellable; use pocketmine\event\Event; -use pocketmine\inventory\TransactionGroup; +use pocketmine\inventory\transaction\InventoryTransaction; /** * Called when there is a transaction between two Inventory objects. @@ -34,21 +34,21 @@ use pocketmine\inventory\TransactionGroup; class InventoryTransactionEvent extends Event implements Cancellable{ public static $handlerList = null; - /** @var TransactionGroup */ - private $ts; + /** @var InventoryTransaction */ + private $transaction; /** - * @param TransactionGroup $ts + * @param InventoryTransaction $transaction */ - public function __construct(TransactionGroup $ts){ - $this->ts = $ts; + public function __construct(InventoryTransaction $transaction){ + $this->transaction = $transaction; } /** - * @return TransactionGroup + * @return InventoryTransaction */ - public function getTransaction() : TransactionGroup{ - return $this->ts; + public function getTransaction() : InventoryTransaction{ + return $this->transaction; } } diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index abdff4ea89..b67d0b1266 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -140,7 +140,6 @@ abstract class BaseInventory implements Inventory{ if($holder instanceof Entity){ Server::getInstance()->getPluginManager()->callEvent($ev = new EntityInventoryChangeEvent($holder, $this->getItem($index), $item, $index)); if($ev->isCancelled()){ - $this->sendSlot($index, $this->getViewers()); return false; } $item = $ev->getNewItem(); diff --git a/src/pocketmine/inventory/BaseTransaction.php b/src/pocketmine/inventory/BaseTransaction.php deleted file mode 100644 index ffe844a9ef..0000000000 --- a/src/pocketmine/inventory/BaseTransaction.php +++ /dev/null @@ -1,73 +0,0 @@ -inventory = $inventory; - $this->slot = $slot; - $this->sourceItem = clone $sourceItem; - $this->targetItem = clone $targetItem; - $this->creationTime = microtime(true); - } - - public function getCreationTime() : float{ - return $this->creationTime; - } - - public function getInventory() : Inventory{ - return $this->inventory; - } - - public function getSlot() : int{ - return $this->slot; - } - - public function getSourceItem() : Item{ - return clone $this->sourceItem; - } - - public function getTargetItem() : Item{ - return clone $this->targetItem; - } -} \ No newline at end of file diff --git a/src/pocketmine/inventory/PlayerInventory.php b/src/pocketmine/inventory/PlayerInventory.php index e5631e8641..45c13fd3d8 100644 --- a/src/pocketmine/inventory/PlayerInventory.php +++ b/src/pocketmine/inventory/PlayerInventory.php @@ -351,6 +351,12 @@ class PlayerInventory extends BaseInventory{ } public function setItem(int $index, Item $item, bool $send = true) : bool{ + if($item->isNull()){ + $item = Item::get(Item::AIR, 0, 0); + }else{ + $item = clone $item; + } + if($index >= $this->getSize()){ //Armor change Server::getInstance()->getPluginManager()->callEvent($ev = new EntityArmorChangeEvent($this->getHolder(), $this->getItem($index), $item, $index)); if($ev->isCancelled() and $this->getHolder() instanceof Human){ @@ -367,9 +373,8 @@ class PlayerInventory extends BaseInventory{ $item = $ev->getNewItem(); } - $old = $this->getItem($index); - $this->slots[$index] = clone $item; + $this->slots[$index] = $item; $this->onSlotChange($index, $old, $send); return true; diff --git a/src/pocketmine/inventory/TransactionGroup.php b/src/pocketmine/inventory/transaction/InventoryTransaction.php similarity index 76% rename from src/pocketmine/inventory/TransactionGroup.php rename to src/pocketmine/inventory/transaction/InventoryTransaction.php index d953f2e6d7..bdacf0cce4 100644 --- a/src/pocketmine/inventory/TransactionGroup.php +++ b/src/pocketmine/inventory/transaction/InventoryTransaction.php @@ -21,9 +21,12 @@ declare(strict_types=1); -namespace pocketmine\inventory; +namespace pocketmine\inventory\transaction; -interface TransactionGroup{ +use pocketmine\inventory\Inventory; +use pocketmine\inventory\transaction\action\InventoryAction; + +interface InventoryTransaction{ /** * @return float @@ -31,9 +34,9 @@ interface TransactionGroup{ public function getCreationTime() : float; /** - * @return Transaction[] + * @return InventoryAction[] */ - public function getTransactions() : array; + public function getActions() : array; /** * @return Inventory[] @@ -41,9 +44,9 @@ interface TransactionGroup{ public function getInventories() : array; /** - * @param Transaction $transaction + * @param InventoryAction $action */ - public function addTransaction(Transaction $transaction); + public function addAction(InventoryAction $action); /** * @return bool diff --git a/src/pocketmine/inventory/SimpleTransactionGroup.php b/src/pocketmine/inventory/transaction/SimpleInventoryTransaction.php similarity index 60% rename from src/pocketmine/inventory/SimpleTransactionGroup.php rename to src/pocketmine/inventory/transaction/SimpleInventoryTransaction.php index f2947b7ada..6b4ebf5377 100644 --- a/src/pocketmine/inventory/SimpleTransactionGroup.php +++ b/src/pocketmine/inventory/transaction/SimpleInventoryTransaction.php @@ -21,17 +21,21 @@ declare(strict_types=1); -namespace pocketmine\inventory; +namespace pocketmine\inventory\transaction; use pocketmine\event\inventory\InventoryTransactionEvent; +use pocketmine\inventory\Inventory; +use pocketmine\inventory\transaction\InventoryTransaction; +use pocketmine\inventory\transaction\action\InventoryAction; +use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\item\Item; use pocketmine\Player; use pocketmine\Server; /** - * This TransactionGroup only allows doing Transaction between one / two inventories + * This InventoryTransaction only allows doing Transaction between one / two inventories */ -class SimpleTransactionGroup implements TransactionGroup{ +class SimpleInventoryTransaction implements InventoryTransaction{ /** @var float */ private $creationTime; protected $hasExecuted = false; @@ -41,8 +45,8 @@ class SimpleTransactionGroup implements TransactionGroup{ /** @var Inventory[] */ protected $inventories = []; - /** @var Transaction[] */ - protected $transactions = []; + /** @var InventoryAction[] */ + protected $actions = []; /** * @param Player $source @@ -71,27 +75,22 @@ class SimpleTransactionGroup implements TransactionGroup{ } /** - * @return Transaction[] + * @return InventoryAction[] */ - public function getTransactions() : array{ - return $this->transactions; + public function getActions() : array{ + return $this->actions; } - public function addTransaction(Transaction $transaction){ - if(isset($this->transactions[spl_object_hash($transaction)])){ + public function addAction(InventoryAction $action){ + if(isset($this->actions[spl_object_hash($action)])){ return; } - foreach($this->transactions as $hash => $tx){ - if($tx->getInventory() === $transaction->getInventory() and $tx->getSlot() === $transaction->getSlot()){ - if($transaction->getCreationTime() >= $tx->getCreationTime()){ - unset($this->transactions[$hash]); - }else{ - return; - } - } + + $this->actions[spl_object_hash($action)] = $action; + if($action instanceof SlotChangeAction){ + $action->setInventoryFrom($this->source); + $this->inventories[spl_object_hash($action->getInventory())] = $action->getInventory(); } - $this->transactions[spl_object_hash($transaction)] = $transaction; - $this->inventories[spl_object_hash($transaction->getInventory())] = $transaction->getInventory(); } /** @@ -101,17 +100,17 @@ class SimpleTransactionGroup implements TransactionGroup{ * @return bool */ protected function matchItems(array &$needItems, array &$haveItems) : bool{ - foreach($this->transactions as $key => $ts){ - if($ts->getTargetItem()->getId() !== Item::AIR){ - $needItems[] = $ts->getTargetItem(); + foreach($this->actions as $key => $action){ + if($action->getTargetItem()->getId() !== Item::AIR){ + $needItems[] = $action->getTargetItem(); } - $checkSourceItem = $ts->getInventory()->getItem($ts->getSlot()); - $sourceItem = $ts->getSourceItem(); - if(!$checkSourceItem->equals($sourceItem) or $sourceItem->getCount() !== $checkSourceItem->getCount()){ + + if(!$action->isValid($this->source)){ return false; } - if($sourceItem->getId() !== Item::AIR){ - $haveItems[] = $sourceItem; + + if($action->getSourceItem()->getId() !== Item::AIR){ + $haveItems[] = $action->getSourceItem(); } } @@ -139,7 +138,7 @@ class SimpleTransactionGroup implements TransactionGroup{ $haveItems = []; $needItems = []; - return $this->matchItems($needItems, $haveItems) and count($this->transactions) > 0 and ((count($haveItems) === 0 and count($needItems) === 0) or $this->source->isCreative(true)); + return $this->matchItems($needItems, $haveItems) and count($this->actions) > 0 and count($haveItems) === 0 and count($needItems) === 0; } /** @@ -152,18 +151,15 @@ class SimpleTransactionGroup implements TransactionGroup{ Server::getInstance()->getPluginManager()->callEvent($ev = new InventoryTransactionEvent($this)); if($ev->isCancelled()){ - foreach($this->inventories as $inventory){ - if($inventory instanceof PlayerInventory){ - $inventory->sendArmorContents($this->getSource()); - } - $inventory->sendContents($this->getSource()); - } - return false; } - foreach($this->transactions as $transaction){ - $transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem()); + foreach($this->actions as $action){ + if($action->execute($this->source)){ + $action->onExecuteSuccess($this->source); + }else{ + $action->onExecuteFail($this->source); + } } $this->hasExecuted = true; diff --git a/src/pocketmine/inventory/transaction/action/CreativeInventoryAction.php b/src/pocketmine/inventory/transaction/action/CreativeInventoryAction.php new file mode 100644 index 0000000000..c1848f95bd --- /dev/null +++ b/src/pocketmine/inventory/transaction/action/CreativeInventoryAction.php @@ -0,0 +1,85 @@ +actionType = $actionType; + } + + /** + * Checks that the player is in creative, and (if creating an item) that the item exists in the creative inventory. + * + * @param Player $source + * + * @return bool + */ + public function isValid(Player $source) : bool{ + return $source->isCreative(true) and + ($this->actionType === self::TYPE_DELETE_ITEM or Item::getCreativeItemIndex($this->sourceItem) !== -1); + } + + /** + * Returns the type of the action. + */ + public function getActionType() : int{ + return $this->actionType; + } + + /** + * No need to do anything extra here: this type just provides a place for items to disappear or appear from. + * + * @param Player $source + * + * @return bool + */ + public function execute(Player $source) : bool{ + return true; + } + + public function onExecuteSuccess(Player $source){ + + } + + public function onExecuteFail(Player $source){ + + } + +} \ No newline at end of file diff --git a/src/pocketmine/inventory/Transaction.php b/src/pocketmine/inventory/transaction/action/DropItemAction.php similarity index 50% rename from src/pocketmine/inventory/Transaction.php rename to src/pocketmine/inventory/transaction/action/DropItemAction.php index 2ebcc247e8..5cde91b2c0 100644 --- a/src/pocketmine/inventory/Transaction.php +++ b/src/pocketmine/inventory/transaction/action/DropItemAction.php @@ -21,34 +21,41 @@ declare(strict_types=1); -namespace pocketmine\inventory; +namespace pocketmine\inventory\transaction\action; -use pocketmine\item\Item; +use pocketmine\Player; -interface Transaction{ +/** + * Represents an action involving dropping an item into the world. + */ +class DropItemAction extends InventoryAction{ /** - * @return Inventory + * Verifies that the source item of a drop-item action must be air. This is not strictly necessary, just a sanity + * check. + * + * @param Player $source + * @return bool */ - public function getInventory() : Inventory; + public function isValid(Player $source) : bool{ + return $this->sourceItem->isNull(); + } /** - * @return int + * Drops the target item in front of the player. + * + * @param Player $source + * @return bool */ - public function getSlot() : int; + public function execute(Player $source) : bool{ + return $source->dropItem($this->targetItem); + } - /** - * @return Item - */ - public function getSourceItem() : Item; + public function onExecuteSuccess(Player $source){ - /** - * @return Item - */ - public function getTargetItem() : Item; + } - /** - * @return float - */ - public function getCreationTime() : float; + public function onExecuteFail(Player $source){ + + } } \ No newline at end of file diff --git a/src/pocketmine/inventory/transaction/action/InventoryAction.php b/src/pocketmine/inventory/transaction/action/InventoryAction.php new file mode 100644 index 0000000000..e2496862ad --- /dev/null +++ b/src/pocketmine/inventory/transaction/action/InventoryAction.php @@ -0,0 +1,102 @@ +sourceItem = $sourceItem; + $this->targetItem = $targetItem; + + $this->creationTime = microtime(true); + } + + public function getCreationTime() : float{ + return $this->creationTime; + } + + /** + * Returns the item that was present before the action took place. + * @return Item + */ + public function getSourceItem() : Item{ + return clone $this->sourceItem; + } + + /** + * Returns the item that the action attempted to replace the source item with. + * @return Item + */ + public function getTargetItem() : Item{ + return clone $this->targetItem; + } + + /** + * Returns whether this action is currently valid. This should perform any necessary sanity checks. + * + * @param Player $source + * + * @return bool + */ + abstract public function isValid(Player $source) : bool; + + /** + * Performs actions needed to complete the inventory-action server-side. Returns if it was successful. Will return + * false if plugins cancelled events. This will only be called if the transaction which it is part of is considered + * valid. + * + * @param Player $source + * + * @return bool + */ + abstract public function execute(Player $source) : bool; + + /** + * Performs additional actions when this inventory-action completed successfully. + * + * @param Player $source + */ + abstract public function onExecuteSuccess(Player $source); + + /** + * Performs additional actions when this inventory-action did not complete successfully. + * + * @param Player $source + */ + abstract public function onExecuteFail(Player $source); + +} \ No newline at end of file diff --git a/src/pocketmine/inventory/transaction/action/SlotChangeAction.php b/src/pocketmine/inventory/transaction/action/SlotChangeAction.php new file mode 100644 index 0000000000..212f63acb6 --- /dev/null +++ b/src/pocketmine/inventory/transaction/action/SlotChangeAction.php @@ -0,0 +1,126 @@ +inventorySlot = $inventorySlot; + $this->containerId = $containerId; + } + + public function getContainerId() : int{ + return $this->containerId; + } + + /** + * Returns the inventory involved in this action. Will return null if the action has not yet been fully initialized. + * + * @return Inventory|null + */ + public function getInventory(){ + return $this->inventory; + } + + public function setInventoryFrom(Player $player){ + $inventory = $player->getWindow($this->containerId); + if($inventory === null){ + throw new \InvalidStateException("Player " . $player->getName() . " has no open container with ID " . $this->containerId); + } + + $this->inventory = $inventory; + } + + /** + * Returns the slot in the inventory which this action modified. + * @return int + */ + public function getSlot() : int{ + return $this->inventorySlot; + } + + /** + * Checks if the item in the inventory at the specified slot is the same as this action's source item. + * + * @param Player $source + * + * @return bool + */ + public function isValid(Player $source) : bool{ + $check = $this->inventory->getItem($this->inventorySlot); + return $check->equals($this->sourceItem) and $check->getCount() === $this->sourceItem->getCount(); + } + + /** + * Sets the item into the target inventory. + * + * @param Player $source + * + * @return bool + */ + public function execute(Player $source) : bool{ + return $this->inventory->setItem($this->inventorySlot, $this->targetItem, false); + } + + /** + * Sends slot changes to other viewers of the inventory. This will not send any change back to the source Player. + * + * @param Player $source + */ + public function onExecuteSuccess(Player $source){ + $viewers = $this->inventory->getViewers(); + unset($viewers[spl_object_hash($source)]); + $this->inventory->sendSlot($this->inventorySlot, $viewers); + } + + /** + * Sends the original slot contents to the source player to revert the action. + * + * @param Player $source + */ + public function onExecuteFail(Player $source){ + $this->inventory->sendSlot($this->inventorySlot, $source); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php index 4e2e8db2b0..224d30b87d 100644 --- a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php +++ b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php @@ -37,7 +37,6 @@ use pocketmine\network\mcpe\protocol\CommandRequestPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; use pocketmine\network\mcpe\protocol\CraftingEventPacket; use pocketmine\network\mcpe\protocol\DataPacket; -use pocketmine\network\mcpe\protocol\DropItemPacket; use pocketmine\network\mcpe\protocol\EntityEventPacket; use pocketmine\network\mcpe\protocol\EntityFallPacket; use pocketmine\network\mcpe\protocol\EntityPickRequestPacket; @@ -164,16 +163,6 @@ class PlayerNetworkSessionAdapter extends NetworkSession{ return $this->player->handleAnimate($packet); } - /** - * TODO: REMOVE - * @param DropItemPacket $packet - * - * @return bool - */ - public function handleDropItem(DropItemPacket $packet) : bool{ - return $this->player->handleDropItem($packet); - } - public function handleContainerClose(ContainerClosePacket $packet) : bool{ return $this->player->handleContainerClose($packet); } diff --git a/src/pocketmine/network/mcpe/protocol/DropItemPacket.php b/src/pocketmine/network/mcpe/protocol/DropItemPacket.php deleted file mode 100644 index 02be637e72..0000000000 --- a/src/pocketmine/network/mcpe/protocol/DropItemPacket.php +++ /dev/null @@ -1,56 +0,0 @@ - - - -use pocketmine\item\Item; -use pocketmine\network\mcpe\NetworkSession; - -/** - * @removed - */ -class DropItemPacket extends DataPacket{ - const NETWORK_ID = ProtocolInfo::DROP_ITEM_PACKET; - - public $type; - /** @var Item */ - public $item; - - protected function decodePayload(){ - $this->type = $this->getByte(); - $this->item = $this->getSlot(); - } - - protected function encodePayload(){ - $this->putByte($this->type); - $this->putSlot($this->item); - } - - public function handle(NetworkSession $session) : bool{ - return $session->handleDropItem($this); - } - -} diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index c9ff1429ec..24dd003b05 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php @@ -25,13 +25,18 @@ namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\inventory\transaction\action\CreativeInventoryAction; +use pocketmine\inventory\transaction\action\DropItemAction; +use pocketmine\inventory\transaction\action\InventoryAction; +use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\ContainerIds; class InventoryTransactionPacket extends DataPacket{ const NETWORK_ID = ProtocolInfo::INVENTORY_TRANSACTION_PACKET; const TYPE_NORMAL = 0; - + const TYPE_MISMATCH = 1; const TYPE_USE_ITEM = 2; const TYPE_USE_ITEM_ON_ENTITY = 3; const TYPE_RELEASE_ITEM = 4; @@ -87,6 +92,7 @@ class InventoryTransactionPacket extends DataPacket{ const ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM = 0; const ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM = 1; + /** @var InventoryAction[] */ public $actions = []; public $transactionData; @@ -104,7 +110,7 @@ class InventoryTransactionPacket extends DataPacket{ switch($type){ case self::TYPE_NORMAL: - case 1: + case self::TYPE_MISMATCH: //Regular ComplexInventoryTransaction doesn't read any extra data break; case self::TYPE_USE_ITEM: @@ -133,8 +139,6 @@ class InventoryTransactionPacket extends DataPacket{ default: throw new \UnexpectedValueException("Unknown transaction type $type"); } - - //TODO } protected function encodePayload(){ @@ -142,45 +146,58 @@ class InventoryTransactionPacket extends DataPacket{ } protected function decodeInventoryAction(){ - $actionBucket = new \stdClass(); - $actionBucket->inventorySource = $this->decodeInventorySource(); + $sourceType = $this->getUnsignedVarInt(); + $containerId = ContainerIds::NONE; + $unknown = 0; - $actionBucket->inventorySlot = $this->getUnsignedVarInt(); - $actionBucket->oldItem = $this->getSlot(); - $actionBucket->newItem = $this->getSlot(); - return $actionBucket; - } - protected function decodeInventorySource(){ - $bucket = new \stdClass(); - $bucket->sourceType = $this->getUnsignedVarInt(); - - switch($bucket->sourceType){ + switch($sourceType){ case self::SOURCE_CONTAINER: - - $bucket->containerId = $this->getVarInt(); - $bucket->field_2 = 0; + $containerId = $this->getVarInt(); break; - case 1: - break; //unused case self::SOURCE_WORLD: - $bucket->containerId = -1; - $bucket->field_2 = $this->getUnsignedVarInt(); + $unknown = $this->getUnsignedVarInt(); break; case self::SOURCE_CREATIVE: - $bucket->containerId = -1; - $bucket->field_2 = 0; break; case self::SOURCE_TODO: - $bucket->containerId = $this->getVarInt(); - $bucket->field_2 = 0; + $containerId = $this->getVarInt(); break; default: - throw new \UnexpectedValueException("Unexpected inventory source type $bucket->sourceType"); - + throw new \UnexpectedValueException("Unexpected inventory source type $sourceType"); } - return $bucket; + $inventorySlot = $this->getUnsignedVarInt(); + $sourceItem = $this->getSlot(); + $targetItem = $this->getSlot(); + + switch($sourceType){ + case self::SOURCE_CONTAINER: + if($containerId === ContainerIds::ARMOR){ + //TODO: HACK! + $inventorySlot += 36; + $containerId = ContainerIds::INVENTORY; + } + return new SlotChangeAction($sourceItem, $targetItem, $containerId, $inventorySlot); + case self::SOURCE_WORLD: + if($inventorySlot !== self::ACTION_MAGIC_SLOT_DROP_ITEM){ + throw new \UnexpectedValueException("Only expect drop item world action types from client"); + } + + return new DropItemAction($sourceItem, $targetItem); + case self::SOURCE_CREATIVE: + if($inventorySlot === self::ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM){ + return new CreativeInventoryAction($sourceItem, $targetItem, CreativeInventoryAction::TYPE_DELETE_ITEM); + }elseif($inventorySlot === self::ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM){ + return new CreativeInventoryAction($sourceItem, $targetItem, CreativeInventoryAction::TYPE_CREATE_ITEM); + }else{ + throw new \UnexpectedValueException("Unknown creative inventory action type $inventorySlot"); + } + case self::SOURCE_TODO: + return new SlotChangeAction($sourceItem, $targetItem, $containerId, $inventorySlot); + default: + throw new \UnexpectedValueException("Unknown source type $sourceType"); + } } public function handle(NetworkSession $session) : bool{