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.
This commit is contained in:
Dylan K. Taylor 2019-01-03 16:54:42 +00:00
parent 134956ac58
commit 4d15eb3327
9 changed files with 699 additions and 205 deletions

View File

@ -63,6 +63,11 @@ use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket; use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
use pocketmine\network\mcpe\protocol\SubClientLoginPacket; use pocketmine\network\mcpe\protocol\SubClientLoginPacket;
use pocketmine\network\mcpe\protocol\TextPacket; 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; use pocketmine\Player;
/** /**
@ -112,22 +117,52 @@ class SimpleSessionHandler extends SessionHandler{
return true; 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 */ /** @var InventoryAction[] $actions */
$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{ try{
$action = $networkInventoryAction->createInventoryAction($this->player); $action = $networkInventoryAction->createInventoryAction($this->player);
if($action !== null){ if($action !== null){
$actions[] = $action; $actions[] = $action;
} }
}catch(\Exception $e){ }catch(\UnexpectedValueException $e){
$this->player->getServer()->getLogger()->debug("Unhandled inventory action from " . $this->player->getName() . ": " . $e->getMessage()); $this->player->getServer()->getLogger()->debug("Unhandled inventory action from " . $this->player->getName() . ": " . $e->getMessage());
$this->player->sendAllInventories();
return false; 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){ if($this->craftingTransaction === null){
$this->craftingTransaction = new CraftingTransaction($this->player, $actions); $this->craftingTransaction = new CraftingTransaction($this->player, $actions);
}else{ }else{
@ -136,113 +171,98 @@ class SimpleSessionHandler extends SessionHandler{
} }
} }
if($packet->isFinalCraftingPart){ if($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;
try{ try{
$this->craftingTransaction->execute(); $this->craftingTransaction->execute();
}catch(TransactionValidationException $e){ }catch(TransactionValidationException $e){
$this->player->getServer()->getLogger()->debug("Failed to execute crafting transaction for " . $this->player->getName() . ": " . $e->getMessage()); $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; $this->craftingTransaction = null;
return $ret; return false;
} }
return true; $transaction = new InventoryTransaction($this->player, $actions);
} try{
if($this->craftingTransaction !== null){ $transaction->execute();
$this->player->getServer()->getLogger()->debug("Got unexpected normal inventory action with incomplete crafting transaction from " . $this->player->getName() . ", refusing to execute crafting"); }catch(TransactionValidationException $e){
$this->craftingTransaction = null; $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){ return true;
case InventoryTransactionPacket::TYPE_NORMAL: }
$transaction = new InventoryTransaction($this->player, $actions);
try{ private function handleUseItemTransaction(UseItemTransactionData $data) : bool{
$transaction->execute(); switch($data->getActionType()){
}catch(TransactionValidationException $e){ case UseItemTransactionData::ACTION_CLICK_BLOCK:
$this->player->getServer()->getLogger()->debug("Failed to execute inventory transaction from " . $this->player->getName() . ": " . $e->getMessage()); //TODO: start hack for client spam bug
$this->player->getServer()->getLogger()->debug("Actions: " . json_encode($packet->actions)); $clickPos = $data->getClickPos();
$spamBug = ($this->lastRightClickPos !== null and
return false; 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: end hack for client spam bug
//TODO: fix achievement for getting iron from furnace $this->player->interactBlock($data->getBlockPos(), $data->getFace(), $clickPos);
return true; return true;
case InventoryTransactionPacket::TYPE_MISMATCH: case UseItemTransactionData::ACTION_BREAK_BLOCK:
if(count($packet->actions) > 0){ $this->player->breakBlock($data->getBlockPos());
$this->player->getServer()->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions)); return true;
} case UseItemTransactionData::ACTION_CLICK_AIR:
$this->player->sendAllInventories(); $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; 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; return false;
} }

View File

@ -26,8 +26,16 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h> #include <rules/DataPacket.h>
use pocketmine\network\mcpe\handler\SessionHandler; 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{ class InventoryTransactionPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::INVENTORY_TRANSACTION_PACKET; 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_USE_ITEM_ON_ENTITY = 3;
public const TYPE_RELEASE_ITEM = 4; public const TYPE_RELEASE_ITEM = 4;
public const USE_ITEM_ACTION_CLICK_BLOCK = 0; /** @var TransactionData */
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 */
public $trData; public $trData;
protected function decodePayload() : void{ protected function decodePayload() : void{
$this->transactionType = $this->getUnsignedVarInt(); $transactionType = $this->getUnsignedVarInt();
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ switch($transactionType){
$this->actions[] = (new NetworkInventoryAction())->read($this);
}
$this->trData = new \stdClass();
switch($this->transactionType){
case self::TYPE_NORMAL: case self::TYPE_NORMAL:
$this->trData = new NormalTransactionData();
break;
case self::TYPE_MISMATCH: case self::TYPE_MISMATCH:
//Regular ComplexInventoryTransaction doesn't read any extra data $this->trData = new MismatchTransactionData();
break; break;
case self::TYPE_USE_ITEM: case self::TYPE_USE_ITEM:
$this->trData->actionType = $this->getUnsignedVarInt(); $this->trData = new UseItemTransactionData();
$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();
break; break;
case self::TYPE_USE_ITEM_ON_ENTITY: case self::TYPE_USE_ITEM_ON_ENTITY:
$this->trData->entityRuntimeId = $this->getEntityRuntimeId(); $this->trData = new UseItemOnEntityTransactionData();
$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();
break; break;
case self::TYPE_RELEASE_ITEM: case self::TYPE_RELEASE_ITEM:
$this->trData->actionType = $this->getUnsignedVarInt(); $this->trData = new ReleaseItemTransactionData();
$this->trData->hotbarSlot = $this->getVarInt();
$this->trData->itemInHand = $this->getSlot();
$this->trData->headPos = $this->getVector3();
break; break;
default: default:
throw new \UnexpectedValueException("Unknown transaction type $this->transactionType"); throw new \UnexpectedValueException("Unknown transaction type $transactionType");
} }
$this->trData->decode($this);
} }
protected function encodePayload() : void{ protected function encodePayload() : void{
$this->putUnsignedVarInt($this->transactionType); $this->putUnsignedVarInt($this->trData->getTypeId());
$this->trData->encode($this);
$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");
}
} }
public function handle(SessionHandler $handler) : bool{ public function handle(SessionHandler $handler) : bool{

View File

@ -0,0 +1,48 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
class MismatchTransactionData extends TransactionData{
public function getTypeId() : int{
return InventoryTransactionPacket::TYPE_MISMATCH;
}
protected function decodeData(NetworkBinaryStream $stream) : void{
if(!empty($this->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
}
}

View File

@ -28,7 +28,7 @@ use pocketmine\inventory\transaction\action\DropItemAction;
use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\action\InventoryAction;
use pocketmine\inventory\transaction\action\SlotChangeAction; use pocketmine\inventory\transaction\action\SlotChangeAction;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket; use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\Player; use pocketmine\Player;
class NetworkInventoryAction{ class NetworkInventoryAction{
@ -92,11 +92,13 @@ class NetworkInventoryAction{
public $newItem; public $newItem;
/** /**
* @param InventoryTransactionPacket $packet * @param NetworkBinaryStream $packet
* *
* @return $this * @return $this
* @throws \UnexpectedValueException
* @throws \OutOfBoundsException
*/ */
public function read(InventoryTransactionPacket $packet) : NetworkInventoryAction{ public function read(NetworkBinaryStream $packet) : NetworkInventoryAction{
$this->sourceType = $packet->getUnsignedVarInt(); $this->sourceType = $packet->getUnsignedVarInt();
switch($this->sourceType){ switch($this->sourceType){
@ -111,14 +113,6 @@ class NetworkInventoryAction{
case self::SOURCE_CRAFTING_GRID: case self::SOURCE_CRAFTING_GRID:
case self::SOURCE_TODO: case self::SOURCE_TODO:
$this->windowId = $packet->getVarInt(); $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; break;
default: default:
throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType"); 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); $packet->putUnsignedVarInt($this->sourceType);
switch($this->sourceType){ switch($this->sourceType){
@ -151,7 +147,7 @@ class NetworkInventoryAction{
$packet->putVarInt($this->windowId); $packet->putVarInt($this->windowId);
break; break;
default: 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); $packet->putUnsignedVarInt($this->inventorySlot);
@ -163,6 +159,7 @@ class NetworkInventoryAction{
* @param Player $player * @param Player $player
* *
* @return InventoryAction|null * @return InventoryAction|null
* @throws \UnexpectedValueException
*/ */
public function createInventoryAction(Player $player) : ?InventoryAction{ public function createInventoryAction(Player $player) : ?InventoryAction{
switch($this->sourceType){ switch($this->sourceType){
@ -172,7 +169,7 @@ class NetworkInventoryAction{
return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem); 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: case self::SOURCE_WORLD:
if($this->inventorySlot !== self::ACTION_MAGIC_SLOT_DROP_ITEM){ if($this->inventorySlot !== self::ACTION_MAGIC_SLOT_DROP_ITEM){
throw new \UnexpectedValueException("Only expecting drop-item world actions from the client!"); 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"); 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;
}
} }

View File

@ -0,0 +1,53 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
class NormalTransactionData extends TransactionData{
public function getTypeId() : int{
return InventoryTransactionPacket::TYPE_NORMAL;
}
protected function decodeData(NetworkBinaryStream $stream) : void{
}
protected function encodeData(NetworkBinaryStream $stream) : void{
}
/**
* @param NetworkInventoryAction[] $actions
*
* @return NormalTransactionData
*/
public static function new(array $actions) : self{
$result = new self();
$result->actions = $actions;
return $result;
}
}

View File

@ -0,0 +1,99 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
class ReleaseItemTransactionData extends TransactionData{
public const ACTION_RELEASE = 0; //bow shoot
public const ACTION_CONSUME = 1; //eat food, drink potion
/** @var int */
private $actionType;
/** @var int */
private $hotbarSlot;
/** @var Item */
private $itemInHand;
/** @var Vector3 */
private $headPos;
/**
* @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 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;
}
}

View File

@ -0,0 +1,75 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\network\mcpe\NetworkBinaryStream;
abstract class TransactionData{
/** @var NetworkInventoryAction[] */
protected $actions;
/**
* @return NetworkInventoryAction[]
*/
final public function getActions() : array{
return $this->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;
}

View File

@ -0,0 +1,123 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
class UseItemOnEntityTransactionData extends TransactionData{
public const ACTION_INTERACT = 0;
public const ACTION_ATTACK = 1;
/** @var int */
private $entityRuntimeId;
/** @var int */
private $actionType;
/** @var int */
private $hotbarSlot;
/** @var Item */
private $itemInHand;
/** @var Vector3 */
private $playerPos;
/** @var Vector3 */
private $clickPos;
/**
* @return int
*/
public function getEntityRuntimeId() : int{
return $this->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;
}
}

View File

@ -0,0 +1,137 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
class UseItemTransactionData extends TransactionData{
public const ACTION_CLICK_BLOCK = 0;
public const ACTION_CLICK_AIR = 1;
public const ACTION_BREAK_BLOCK = 2;
/** @var int */
private $actionType;
/** @var Vector3 */
private $blockPos;
/** @var int */
private $face;
/** @var int */
private $hotbarSlot;
/** @var Item */
private $itemInHand;
/** @var Vector3 */
private $playerPos;
/** @var Vector3 */
private $clickPos;
/**
* @return int
*/
public function getActionType() : int{
return $this->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;
}
}