From 4d15eb3327c0e551940025fda332a474cb8d09f7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 3 Jan 2019 16:54:42 +0000 Subject: [PATCH] Cleaned up the InventoryTransactionPacket decoding clusterfuck @shoghicp, y u do dis... I almost created a sub-packet architecture to deal with this shit :( This mess really ought to be split into multiple packets. Perhaps the PacketPool can be extended to do that in the future. --- .../mcpe/handler/SimpleSessionHandler.php | 210 ++++++++++-------- .../protocol/InventoryTransactionPacket.php | 118 ++-------- .../types/MismatchTransactionData.php | 48 ++++ .../protocol/types/NetworkInventoryAction.php | 41 ++-- .../protocol/types/NormalTransactionData.php | 53 +++++ .../types/ReleaseItemTransactionData.php | 99 +++++++++ .../mcpe/protocol/types/TransactionData.php | 75 +++++++ .../types/UseItemOnEntityTransactionData.php | 123 ++++++++++ .../protocol/types/UseItemTransactionData.php | 137 ++++++++++++ 9 files changed, 699 insertions(+), 205 deletions(-) create mode 100644 src/pocketmine/network/mcpe/protocol/types/MismatchTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/NormalTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/ReleaseItemTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/TransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/UseItemOnEntityTransactionData.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/UseItemTransactionData.php diff --git a/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php b/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php index 35b463645..c86362a17 100644 --- a/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php +++ b/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php @@ -63,6 +63,11 @@ use pocketmine\network\mcpe\protocol\ShowCreditsPacket; use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket; use pocketmine\network\mcpe\protocol\SubClientLoginPacket; use pocketmine\network\mcpe\protocol\TextPacket; +use pocketmine\network\mcpe\protocol\types\MismatchTransactionData; +use pocketmine\network\mcpe\protocol\types\NormalTransactionData; +use pocketmine\network\mcpe\protocol\types\ReleaseItemTransactionData; +use pocketmine\network\mcpe\protocol\types\UseItemOnEntityTransactionData; +use pocketmine\network\mcpe\protocol\types\UseItemTransactionData; use pocketmine\Player; /** @@ -112,22 +117,52 @@ class SimpleSessionHandler extends SessionHandler{ return true; } + $result = true; + + if($packet->trData instanceof NormalTransactionData){ + $result = $this->handleNormalTransaction($packet->trData); + }elseif($packet->trData instanceof MismatchTransactionData){ + $this->player->sendAllInventories(); + $result = true; + }elseif($packet->trData instanceof UseItemTransactionData){ + $result = $this->handleUseItemTransaction($packet->trData); + }elseif($packet->trData instanceof UseItemOnEntityTransactionData){ + $result = $this->handleUseItemOnEntityTransaction($packet->trData); + }elseif($packet->trData instanceof ReleaseItemTransactionData){ + $result = $this->handleReleaseItemTransaction($packet->trData); + } + + if(!$result){ + $this->player->sendAllInventories(); + } + return $result; + } + + private function handleNormalTransaction(NormalTransactionData $data) : bool{ /** @var InventoryAction[] $actions */ $actions = []; - foreach($packet->actions as $networkInventoryAction){ + + $isCrafting = false; + $isFinalCraftingPart = false; + foreach($data->getActions() as $networkInventoryAction){ + $isCrafting = $isCrafting || $networkInventoryAction->isCraftingPart(); + $isFinalCraftingPart = $isFinalCraftingPart || $networkInventoryAction->isFinalCraftingPart(); + try{ $action = $networkInventoryAction->createInventoryAction($this->player); if($action !== null){ $actions[] = $action; } - }catch(\Exception $e){ + }catch(\UnexpectedValueException $e){ $this->player->getServer()->getLogger()->debug("Unhandled inventory action from " . $this->player->getName() . ": " . $e->getMessage()); - $this->player->sendAllInventories(); return false; } } - if($packet->isCraftingPart){ + if($isCrafting){ + //we get the actions for this in several packets, so we need to wait until we have all the pieces before + //trying to execute it + if($this->craftingTransaction === null){ $this->craftingTransaction = new CraftingTransaction($this->player, $actions); }else{ @@ -136,113 +171,98 @@ class SimpleSessionHandler extends SessionHandler{ } } - if($packet->isFinalCraftingPart){ - //we get the actions for this in several packets, so we need to wait until we have all the pieces before - //trying to execute it - - $ret = true; + if($isFinalCraftingPart){ try{ $this->craftingTransaction->execute(); }catch(TransactionValidationException $e){ $this->player->getServer()->getLogger()->debug("Failed to execute crafting transaction for " . $this->player->getName() . ": " . $e->getMessage()); - $ret = false; + return false; + }finally{ + $this->craftingTransaction = null; } - + } + }else{ + //normal transaction fallthru + if($this->craftingTransaction !== null){ + $this->player->getServer()->getLogger()->debug("Got unexpected normal inventory action with incomplete crafting transaction from " . $this->player->getName() . ", refusing to execute crafting"); $this->craftingTransaction = null; - return $ret; + return false; } - return true; - } - if($this->craftingTransaction !== null){ - $this->player->getServer()->getLogger()->debug("Got unexpected normal inventory action with incomplete crafting transaction from " . $this->player->getName() . ", refusing to execute crafting"); - $this->craftingTransaction = null; + $transaction = new InventoryTransaction($this->player, $actions); + try{ + $transaction->execute(); + }catch(TransactionValidationException $e){ + $logger = $this->player->getServer()->getLogger(); + $logger->debug("Failed to execute inventory transaction from " . $this->player->getName() . ": " . $e->getMessage()); + $logger->debug("Actions: " . json_encode($data->getActions())); + + return false; + } + + //TODO: fix achievement for getting iron from furnace } - switch($packet->transactionType){ - case InventoryTransactionPacket::TYPE_NORMAL: - $transaction = new InventoryTransaction($this->player, $actions); + return true; + } - try{ - $transaction->execute(); - }catch(TransactionValidationException $e){ - $this->player->getServer()->getLogger()->debug("Failed to execute inventory transaction from " . $this->player->getName() . ": " . $e->getMessage()); - $this->player->getServer()->getLogger()->debug("Actions: " . json_encode($packet->actions)); - - return false; + private function handleUseItemTransaction(UseItemTransactionData $data) : bool{ + switch($data->getActionType()){ + case UseItemTransactionData::ACTION_CLICK_BLOCK: + //TODO: start hack for client spam bug + $clickPos = $data->getClickPos(); + $spamBug = ($this->lastRightClickPos !== null and + microtime(true) - $this->lastRightClickTime < 0.1 and //100ms + $this->lastRightClickPos->distanceSquared($clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error + ); + //get rid of continued spam if the player clicks and holds right-click + $this->lastRightClickPos = clone $clickPos; + $this->lastRightClickTime = microtime(true); + if($spamBug){ + return true; } - - //TODO: fix achievement for getting iron from furnace - + //TODO: end hack for client spam bug + $this->player->interactBlock($data->getBlockPos(), $data->getFace(), $clickPos); return true; - case InventoryTransactionPacket::TYPE_MISMATCH: - if(count($packet->actions) > 0){ - $this->player->getServer()->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions)); - } - $this->player->sendAllInventories(); - + case UseItemTransactionData::ACTION_BREAK_BLOCK: + $this->player->breakBlock($data->getBlockPos()); + return true; + case UseItemTransactionData::ACTION_CLICK_AIR: + $this->player->useHeldItem(); + return true; + } + + return false; + } + + private function handleUseItemOnEntityTransaction(UseItemOnEntityTransactionData $data) : bool{ + $target = $this->player->getLevel()->getEntity($data->getEntityRuntimeId()); + if($target === null){ + return false; + } + + switch($data->getActionType()){ + case UseItemOnEntityTransactionData::ACTION_INTERACT: + $this->player->interactEntity($target, $data->getClickPos()); + return true; + case UseItemOnEntityTransactionData::ACTION_ATTACK: + $this->player->attackEntity($target); + return true; + } + + return false; + } + + private function handleReleaseItemTransaction(ReleaseItemTransactionData $data) : bool{ + switch($data->getActionType()){ + case ReleaseItemTransactionData::ACTION_RELEASE: + $this->player->releaseHeldItem(); + return true; + case ReleaseItemTransactionData::ACTION_CONSUME: + $this->player->consumeHeldItem(); return true; - case InventoryTransactionPacket::TYPE_USE_ITEM: - $blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z); - $face = $packet->trData->face; - - $type = $packet->trData->actionType; - switch($type){ - case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK: - //TODO: start hack for client spam bug - $spamBug = ($this->lastRightClickPos !== null and - microtime(true) - $this->lastRightClickTime < 0.1 and //100ms - $this->lastRightClickPos->distanceSquared($packet->trData->clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error - ); - //get rid of continued spam if the player clicks and holds right-click - $this->lastRightClickPos = clone $packet->trData->clickPos; - $this->lastRightClickTime = microtime(true); - if($spamBug){ - return true; - } - //TODO: end hack for client spam bug - $this->player->interactBlock($blockVector, $face, $packet->trData->clickPos); - return true; - case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK: - $this->player->breakBlock($blockVector); - return true; - case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR: - $this->player->useHeldItem(); - return true; - } - break; - case InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY: - $target = $this->player->getLevel()->getEntity($packet->trData->entityRuntimeId); - if($target === null){ - return false; - } - - switch($packet->trData->actionType){ - case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_INTERACT: - $this->player->interactEntity($target, $packet->trData->clickPos); - return true; - case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_ATTACK: - $this->player->attackEntity($target); - return true; - } - - break; - case InventoryTransactionPacket::TYPE_RELEASE_ITEM: - switch($packet->trData->actionType){ - case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE: - $this->player->releaseHeldItem(); - return true; - case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME: - $this->player->consumeHeldItem(); - return true; - } - break; - default: - break; - } - $this->player->sendAllInventories(); return false; } diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index e6f57ac27..6cdb4c3a3 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php @@ -26,8 +26,16 @@ namespace pocketmine\network\mcpe\protocol; #include use pocketmine\network\mcpe\handler\SessionHandler; -use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; +use pocketmine\network\mcpe\protocol\types\MismatchTransactionData; +use pocketmine\network\mcpe\protocol\types\NormalTransactionData; +use pocketmine\network\mcpe\protocol\types\ReleaseItemTransactionData; +use pocketmine\network\mcpe\protocol\types\TransactionData; +use pocketmine\network\mcpe\protocol\types\UseItemOnEntityTransactionData; +use pocketmine\network\mcpe\protocol\types\UseItemTransactionData; +/** + * This packet effectively crams multiple packets into one. + */ class InventoryTransactionPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::INVENTORY_TRANSACTION_PACKET; @@ -37,118 +45,38 @@ class InventoryTransactionPacket extends DataPacket{ public const TYPE_USE_ITEM_ON_ENTITY = 3; public const TYPE_RELEASE_ITEM = 4; - public const USE_ITEM_ACTION_CLICK_BLOCK = 0; - public const USE_ITEM_ACTION_CLICK_AIR = 1; - public const USE_ITEM_ACTION_BREAK_BLOCK = 2; - - public const RELEASE_ITEM_ACTION_RELEASE = 0; //bow shoot - public const RELEASE_ITEM_ACTION_CONSUME = 1; //eat food, drink potion - - public const USE_ITEM_ON_ENTITY_ACTION_INTERACT = 0; - public const USE_ITEM_ON_ENTITY_ACTION_ATTACK = 1; - - /** @var int */ - public $transactionType; - - /** - * @var bool - * NOTE: THIS FIELD DOES NOT EXIST IN THE PROTOCOL, it's merely used for convenience for PocketMine-MP to easily - * determine whether we're doing a crafting transaction. - */ - public $isCraftingPart = false; - /** - * @var bool - * NOTE: THIS FIELD DOES NOT EXIST IN THE PROTOCOL, it's merely used for convenience for PocketMine-MP to easily - * determine whether we're doing a crafting transaction. - */ - public $isFinalCraftingPart = false; - - /** @var NetworkInventoryAction[] */ - public $actions = []; - - /** @var \stdClass */ + /** @var TransactionData */ public $trData; protected function decodePayload() : void{ - $this->transactionType = $this->getUnsignedVarInt(); + $transactionType = $this->getUnsignedVarInt(); - for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ - $this->actions[] = (new NetworkInventoryAction())->read($this); - } - - $this->trData = new \stdClass(); - - switch($this->transactionType){ + switch($transactionType){ case self::TYPE_NORMAL: + $this->trData = new NormalTransactionData(); + break; case self::TYPE_MISMATCH: - //Regular ComplexInventoryTransaction doesn't read any extra data + $this->trData = new MismatchTransactionData(); break; case self::TYPE_USE_ITEM: - $this->trData->actionType = $this->getUnsignedVarInt(); - $this->getBlockPosition($this->trData->x, $this->trData->y, $this->trData->z); - $this->trData->face = $this->getVarInt(); - $this->trData->hotbarSlot = $this->getVarInt(); - $this->trData->itemInHand = $this->getSlot(); - $this->trData->playerPos = $this->getVector3(); - $this->trData->clickPos = $this->getVector3(); + $this->trData = new UseItemTransactionData(); break; case self::TYPE_USE_ITEM_ON_ENTITY: - $this->trData->entityRuntimeId = $this->getEntityRuntimeId(); - $this->trData->actionType = $this->getUnsignedVarInt(); - $this->trData->hotbarSlot = $this->getVarInt(); - $this->trData->itemInHand = $this->getSlot(); - $this->trData->playerPos = $this->getVector3(); - $this->trData->clickPos = $this->getVector3(); + $this->trData = new UseItemOnEntityTransactionData(); break; case self::TYPE_RELEASE_ITEM: - $this->trData->actionType = $this->getUnsignedVarInt(); - $this->trData->hotbarSlot = $this->getVarInt(); - $this->trData->itemInHand = $this->getSlot(); - $this->trData->headPos = $this->getVector3(); + $this->trData = new ReleaseItemTransactionData(); break; default: - throw new \UnexpectedValueException("Unknown transaction type $this->transactionType"); + throw new \UnexpectedValueException("Unknown transaction type $transactionType"); } + + $this->trData->decode($this); } protected function encodePayload() : void{ - $this->putUnsignedVarInt($this->transactionType); - - $this->putUnsignedVarInt(count($this->actions)); - foreach($this->actions as $action){ - $action->write($this); - } - - switch($this->transactionType){ - case self::TYPE_NORMAL: - case self::TYPE_MISMATCH: - break; - case self::TYPE_USE_ITEM: - $this->putUnsignedVarInt($this->trData->actionType); - $this->putBlockPosition($this->trData->x, $this->trData->y, $this->trData->z); - $this->putVarInt($this->trData->face); - $this->putVarInt($this->trData->hotbarSlot); - $this->putSlot($this->trData->itemInHand); - $this->putVector3($this->trData->playerPos); - $this->putVector3($this->trData->clickPos); - break; - case self::TYPE_USE_ITEM_ON_ENTITY: - $this->putEntityRuntimeId($this->trData->entityRuntimeId); - $this->putUnsignedVarInt($this->trData->actionType); - $this->putVarInt($this->trData->hotbarSlot); - $this->putSlot($this->trData->itemInHand); - $this->putVector3($this->trData->playerPos); - $this->putVector3($this->trData->clickPos); - break; - case self::TYPE_RELEASE_ITEM: - $this->putUnsignedVarInt($this->trData->actionType); - $this->putVarInt($this->trData->hotbarSlot); - $this->putSlot($this->trData->itemInHand); - $this->putVector3($this->trData->headPos); - break; - default: - throw new \UnexpectedValueException("Unknown transaction type $this->transactionType"); - } + $this->putUnsignedVarInt($this->trData->getTypeId()); + $this->trData->encode($this); } public function handle(SessionHandler $handler) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/types/MismatchTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/MismatchTransactionData.php new file mode 100644 index 000000000..ad4d03904 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/MismatchTransactionData.php @@ -0,0 +1,48 @@ +actions)){ + throw new \UnexpectedValueException("Mismatch transaction type should not have any actions associated with it, but got " . count($this->actions)); + } + } + + protected function encodeData(NetworkBinaryStream $stream) : void{ + + } + + public static function new() : self{ + return new self; //no arguments + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php index 738f6743e..1c9bae16a 100644 --- a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php +++ b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php @@ -28,7 +28,7 @@ use pocketmine\inventory\transaction\action\DropItemAction; use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\item\Item; -use pocketmine\network\mcpe\protocol\InventoryTransactionPacket; +use pocketmine\network\mcpe\NetworkBinaryStream; use pocketmine\Player; class NetworkInventoryAction{ @@ -92,11 +92,13 @@ class NetworkInventoryAction{ public $newItem; /** - * @param InventoryTransactionPacket $packet + * @param NetworkBinaryStream $packet * * @return $this + * @throws \UnexpectedValueException + * @throws \OutOfBoundsException */ - public function read(InventoryTransactionPacket $packet) : NetworkInventoryAction{ + public function read(NetworkBinaryStream $packet) : NetworkInventoryAction{ $this->sourceType = $packet->getUnsignedVarInt(); switch($this->sourceType){ @@ -111,14 +113,6 @@ class NetworkInventoryAction{ case self::SOURCE_CRAFTING_GRID: case self::SOURCE_TODO: $this->windowId = $packet->getVarInt(); - switch($this->windowId){ - /** @noinspection PhpMissingBreakStatementInspection */ - case self::SOURCE_TYPE_CRAFTING_RESULT: - $packet->isFinalCraftingPart = true; - case self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT: - $packet->isCraftingPart = true; - break; - } break; default: throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType"); @@ -132,9 +126,11 @@ class NetworkInventoryAction{ } /** - * @param InventoryTransactionPacket $packet + * @param NetworkBinaryStream $packet + * + * @throws \InvalidArgumentException */ - public function write(InventoryTransactionPacket $packet) : void{ + public function write(NetworkBinaryStream $packet) : void{ $packet->putUnsignedVarInt($this->sourceType); switch($this->sourceType){ @@ -151,7 +147,7 @@ class NetworkInventoryAction{ $packet->putVarInt($this->windowId); break; default: - throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType"); + throw new \InvalidArgumentException("Unknown inventory action source type $this->sourceType"); } $packet->putUnsignedVarInt($this->inventorySlot); @@ -163,6 +159,7 @@ class NetworkInventoryAction{ * @param Player $player * * @return InventoryAction|null + * @throws \UnexpectedValueException */ public function createInventoryAction(Player $player) : ?InventoryAction{ switch($this->sourceType){ @@ -172,7 +169,7 @@ class NetworkInventoryAction{ return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem); } - throw new \InvalidStateException("Player " . $player->getName() . " has no open container with window ID $this->windowId"); + throw new \UnexpectedValueException("Player " . $player->getName() . " has no open container with window ID $this->windowId"); case self::SOURCE_WORLD: if($this->inventorySlot !== self::ACTION_MAGIC_SLOT_DROP_ITEM){ throw new \UnexpectedValueException("Only expecting drop-item world actions from the client!"); @@ -212,4 +209,18 @@ class NetworkInventoryAction{ throw new \UnexpectedValueException("Unknown inventory source type $this->sourceType"); } } + + /** + * Hack to allow identifying crafting transaction parts. + * + * @return bool + */ + public function isCraftingPart() : bool{ + return ($this->sourceType === self::SOURCE_TODO or $this->sourceType === self::SOURCE_CRAFTING_GRID) and + ($this->windowId === self::SOURCE_TYPE_CRAFTING_RESULT or $this->windowId === self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT); + } + + public function isFinalCraftingPart() : bool{ + return $this->isCraftingPart() and $this->windowId === self::SOURCE_TYPE_CRAFTING_RESULT; + } } diff --git a/src/pocketmine/network/mcpe/protocol/types/NormalTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/NormalTransactionData.php new file mode 100644 index 000000000..500c80d91 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/NormalTransactionData.php @@ -0,0 +1,53 @@ +actions = $actions; + return $result; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/ReleaseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/ReleaseItemTransactionData.php new file mode 100644 index 000000000..471801a83 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/ReleaseItemTransactionData.php @@ -0,0 +1,99 @@ +actionType; + } + + /** + * @return int + */ + public function getHotbarSlot() : int{ + return $this->hotbarSlot; + } + + /** + * @return Item + */ + public function getItemInHand() : Item{ + return $this->itemInHand; + } + + /** + * @return Vector3 + */ + public function getHeadPos() : Vector3{ + return $this->headPos; + } + + public function getTypeId() : int{ + return InventoryTransactionPacket::TYPE_RELEASE_ITEM; + } + + protected function decodeData(NetworkBinaryStream $stream) : void{ + $this->actionType = $stream->getUnsignedVarInt(); + $this->hotbarSlot = $stream->getVarInt(); + $this->itemInHand = $stream->getSlot(); + $this->headPos = $stream->getVector3(); + } + + protected function encodeData(NetworkBinaryStream $stream) : void{ + $stream->putUnsignedVarInt($this->actionType); + $stream->putVarInt($this->hotbarSlot); + $stream->putSlot($this->itemInHand); + $stream->putVector3($this->headPos); + } + + public static function new(array $actions, int $actionType, int $hotbarSlot, Item $itemInHand, Vector3 $headPos) : self{ + $result = new self; + $result->actions = $actions; + $result->actionType = $actionType; + $result->hotbarSlot = $hotbarSlot; + $result->itemInHand = $itemInHand; + $result->headPos = $headPos; + return $result; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/TransactionData.php b/src/pocketmine/network/mcpe/protocol/types/TransactionData.php new file mode 100644 index 000000000..115757d1e --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/TransactionData.php @@ -0,0 +1,75 @@ +actions; + } + + /** + * @return int + */ + abstract public function getTypeId() : int; + + /** + * @param NetworkBinaryStream $stream + * + * @throws \OutOfBoundsException + * @throws \UnexpectedValueException + */ + final public function decode(NetworkBinaryStream $stream) : void{ + $actionCount = $stream->getUnsignedVarInt(); + for($i = 0; $i < $actionCount; ++$i){ + $this->actions[] = (new NetworkInventoryAction())->read($stream); + } + $this->decodeData($stream); + } + + /** + * @param NetworkBinaryStream $stream + * + * @throws \OutOfBoundsException + * @throws \UnexpectedValueException + */ + abstract protected function decodeData(NetworkBinaryStream $stream) : void; + + final public function encode(NetworkBinaryStream $stream) : void{ + $stream->putUnsignedVarInt(count($this->actions)); + foreach($this->actions as $action){ + $action->write($stream); + } + $this->encodeData($stream); + } + + abstract protected function encodeData(NetworkBinaryStream $stream) : void; +} diff --git a/src/pocketmine/network/mcpe/protocol/types/UseItemOnEntityTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/UseItemOnEntityTransactionData.php new file mode 100644 index 000000000..560a5eb5e --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/UseItemOnEntityTransactionData.php @@ -0,0 +1,123 @@ +entityRuntimeId; + } + + /** + * @return int + */ + public function getActionType() : int{ + return $this->actionType; + } + + /** + * @return int + */ + public function getHotbarSlot() : int{ + return $this->hotbarSlot; + } + + /** + * @return Item + */ + public function getItemInHand() : Item{ + return $this->itemInHand; + } + + /** + * @return Vector3 + */ + public function getPlayerPos() : Vector3{ + return $this->playerPos; + } + + /** + * @return Vector3 + */ + public function getClickPos() : Vector3{ + return $this->clickPos; + } + + public function getTypeId() : int{ + return InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY; + } + + protected function decodeData(NetworkBinaryStream $stream) : void{ + $this->entityRuntimeId = $stream->getEntityRuntimeId(); + $this->actionType = $stream->getUnsignedVarInt(); + $this->hotbarSlot = $stream->getVarInt(); + $this->itemInHand = $stream->getSlot(); + $this->playerPos = $stream->getVector3(); + $this->clickPos = $stream->getVector3(); + } + + protected function encodeData(NetworkBinaryStream $stream) : void{ + $stream->putEntityRuntimeId($this->entityRuntimeId); + $stream->putUnsignedVarInt($this->actionType); + $stream->putVarInt($this->hotbarSlot); + $stream->putSlot($this->itemInHand); + $stream->putVector3($this->playerPos); + $stream->putVector3($this->clickPos); + } + + public static function new(array $actions, int $entityRuntimeId, int $actionType, int $hotbarSlot, Item $itemInHand, Vector3 $playerPos, Vector3 $clickPos) : self{ + $result = new self; + $result->actions = $actions; + $result->entityRuntimeId = $entityRuntimeId; + $result->actionType = $actionType; + $result->hotbarSlot = $hotbarSlot; + $result->itemInHand = $itemInHand; + $result->playerPos = $playerPos; + $result->clickPos = $clickPos; + return $result; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/UseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/UseItemTransactionData.php new file mode 100644 index 000000000..141b90a82 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/UseItemTransactionData.php @@ -0,0 +1,137 @@ +actionType; + } + + /** + * @return Vector3 + */ + public function getBlockPos() : Vector3{ + return $this->blockPos; + } + + /** + * @return int + */ + public function getFace() : int{ + return $this->face; + } + + /** + * @return int + */ + public function getHotbarSlot() : int{ + return $this->hotbarSlot; + } + + /** + * @return Item + */ + public function getItemInHand() : Item{ + return $this->itemInHand; + } + + /** + * @return Vector3 + */ + public function getPlayerPos() : Vector3{ + return $this->playerPos; + } + + /** + * @return Vector3 + */ + public function getClickPos() : Vector3{ + return $this->clickPos; + } + + public function getTypeId() : int{ + return InventoryTransactionPacket::TYPE_USE_ITEM; + } + + protected function decodeData(NetworkBinaryStream $stream) : void{ + $this->actionType = $stream->getUnsignedVarInt(); + $this->blockPos = new Vector3(); + $stream->getBlockPosition($this->blockPos->x, $this->blockPos->y, $this->blockPos->z); + $this->face = $stream->getVarInt(); + $this->hotbarSlot = $stream->getVarInt(); + $this->itemInHand = $stream->getSlot(); + $this->playerPos = $stream->getVector3(); + $this->clickPos = $stream->getVector3(); + } + + protected function encodeData(NetworkBinaryStream $stream) : void{ + $stream->putUnsignedVarInt($this->actionType); + $stream->putBlockPosition($this->blockPos->x, $this->blockPos->y, $this->blockPos->z); + $stream->putVarInt($this->face); + $stream->putVarInt($this->hotbarSlot); + $stream->putSlot($this->itemInHand); + $stream->putVector3($this->playerPos); + $stream->putVector3($this->clickPos); + } + + public static function new(array $actions, int $actionType, Vector3 $blockPos, int $face, int $hotbarSlot, Item $itemInHand, Vector3 $playerPos, Vector3 $clickPos) : self{ + $result = new self; + $result->actions = $actions; + $result->actionType = $actionType; + $result->blockPos = $blockPos; + $result->face = $face; + $result->hotbarSlot = $hotbarSlot; + $result->itemInHand = $itemInHand; + $result->playerPos = $playerPos; + $result->clickPos = $clickPos; + return $result; + } +}