diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index a61d435cd..dcec663c4 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -152,12 +152,7 @@ use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\Experiments; use pocketmine\network\mcpe\protocol\types\GameMode; -use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData; use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData; use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor; use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece; @@ -416,7 +411,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var float */ protected $lastRightClickTime = 0.0; - /** @var UseItemTransactionData|null */ + /** @var \stdClass|null */ protected $lastRightClickData = null; /** @@ -2431,7 +2426,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ /** @var InventoryAction[] $actions */ $actions = []; $isCraftingPart = false; - foreach($packet->trData->getActions() as $networkInventoryAction){ + foreach($packet->actions as $networkInventoryAction){ if( $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and ( $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or @@ -2489,313 +2484,321 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->craftingTransaction = null; } - if($packet->trData instanceof NormalTransactionData){ - $this->setUsingItem(false); - $transaction = new InventoryTransaction($this, $actions); + switch($packet->transactionType){ + case InventoryTransactionPacket::TYPE_NORMAL: + $this->setUsingItem(false); + $transaction = new InventoryTransaction($this, $actions); - try{ - $transaction->execute(); - }catch(TransactionValidationException $e){ - $this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage()); - $this->server->getLogger()->debug("Actions: " . json_encode($packet->trData->getActions())); + try{ + $transaction->execute(); + }catch(TransactionValidationException $e){ + $this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage()); + $this->server->getLogger()->debug("Actions: " . json_encode($packet->actions)); - return false; - } + return false; + } - //TODO: fix achievement for getting iron from furnace + //TODO: fix achievement for getting iron from furnace - return true; - }elseif($packet->trData instanceof MismatchTransactionData){ - if(count($packet->trData->getActions()) > 0){ - $this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->trData->getActions()) . ", " . json_encode($packet->trData->getActions())); - } - $this->setUsingItem(false); - $this->sendAllInventories(); + 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)); + } + $this->setUsingItem(false); + $this->sendAllInventories(); - return true; - }elseif($packet->trData instanceof UseItemTransactionData){ + return true; + case InventoryTransactionPacket::TYPE_USE_ITEM: + $blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z); + $face = $packet->trData->face; - $blockVector = $packet->trData->getBlockPos(); - $face = $packet->trData->getFace(); - - switch($packet->trData->getActionType()){ - case UseItemTransactionData::ACTION_CLICK_BLOCK: - //TODO: start hack for client spam bug - $spamBug = ($this->lastRightClickData !== null and - microtime(true) - $this->lastRightClickTime < 0.1 and //100ms - $this->lastRightClickData->getPlayerPos()->distanceSquared($packet->trData->getPlayerPos()) < 0.00001 and - $this->lastRightClickData->getBlockPos()->equals($packet->trData->getBlockPos()) and - $this->lastRightClickData->getClickPos()->distanceSquared($packet->trData->getClickPos()) < 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->lastRightClickData = $packet->trData; - $this->lastRightClickTime = microtime(true); - if($spamBug){ - return true; - } - //TODO: end hack for client spam bug - - $this->setUsingItem(false); - - if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){ - }elseif($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){ + $type = $packet->trData->actionType; + switch($type){ + case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK: + //TODO: start hack for client spam bug + $spamBug = ($this->lastRightClickData !== null and + microtime(true) - $this->lastRightClickTime < 0.1 and //100ms + $this->lastRightClickData->playerPos->distanceSquared($packet->trData->playerPos) < 0.00001 and + $this->lastRightClickData->x === $packet->trData->x and + $this->lastRightClickData->y === $packet->trData->y and + $this->lastRightClickData->z === $packet->trData->z and + $this->lastRightClickData->clickPos->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->lastRightClickData = $packet->trData; + $this->lastRightClickTime = microtime(true); + if($spamBug){ return true; } - }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand())){ - $this->inventory->sendHeldItem($this); - }else{ - $item = $this->inventory->getItemInHand(); - $oldItem = clone $item; - if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){ - if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } + //TODO: end hack for client spam bug - return true; - } - } + $this->setUsingItem(false); - $this->inventory->sendHeldItem($this); - - if($blockVector->distanceSquared($this) > 10000){ - return true; - } - - $target = $this->level->getBlock($blockVector); - $block = $target->getSide($face); - - /** @var Block[] $blocks */ - $blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other - - $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); - - return true; - case UseItemTransactionData::ACTION_BREAK_BLOCK: - $this->doCloseInventory(); - - $item = $this->inventory->getItemInHand(); - $oldItem = clone $item; - - if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){ - if($this->isSurvival()){ - if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ - $this->inventory->setItemInHand($item); - $this->inventory->sendHeldItem($this->hasSpawned); - } - - $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); - } - return true; - } - - $this->inventory->sendContents($this); - $this->inventory->sendHeldItem($this); - - $target = $this->level->getBlock($blockVector); - /** @var Block[] $blocks */ - $blocks = $target->getAllSides(); - $blocks[] = $target; - - $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); - - foreach($blocks as $b){ - $tile = $this->level->getTile($b); - if($tile instanceof Spawnable){ - $tile->spawnTo($this); - } - } - - return true; - case UseItemTransactionData::ACTION_CLICK_AIR: - if($this->isUsingItem()){ - $slot = $this->inventory->getItemInHand(); - if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){ - $ev = new PlayerItemConsumeEvent($this, $slot); - if($this->hasItemCooldown($slot)){ - $ev->setCancelled(); - } - $ev->call(); - if($ev->isCancelled() or !$this->consumeObject($slot)){ - $this->inventory->sendContents($this); + if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){ + }elseif($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){ return true; } - $this->resetItemCooldown($slot); - if($this->isSurvival()){ - $slot->pop(); - $this->inventory->setItemInHand($slot); - $this->inventory->addItem($slot->getResidue()); - } - $this->setUsingItem(false); - } - } - $directionVector = $this->getDirectionVector(); - - if($this->isCreative()){ - $item = $this->inventory->getItemInHand(); - }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand())){ - $this->inventory->sendHeldItem($this); - return true; - }else{ - $item = $this->inventory->getItemInHand(); - } - - $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); - if($this->hasItemCooldown($item) or $this->isSpectator()){ - $ev->setCancelled(); - } - - $ev->call(); - if($ev->isCancelled()){ - $this->inventory->sendHeldItem($this); - return true; - } - - if($item->onClickAir($this, $directionVector)){ - $this->resetItemCooldown($item); - if($this->isSurvival()){ - $this->inventory->setItemInHand($item); - } - } - - $this->setUsingItem(true); - - return true; - default: - //unknown - break; - } - - $this->inventory->sendContents($this); - return false; - }elseif($packet->trData instanceof UseItemOnEntityTransactionData){ - $target = $this->level->getEntity($packet->trData->getEntityRuntimeId()); - if($target === null){ - return false; - } - - switch($packet->trData->getActionType()){ - case UseItemOnEntityTransactionData::ACTION_INTERACT: - break; //TODO - case UseItemOnEntityTransactionData::ACTION_ATTACK: - if(!$target->isAlive()){ - return true; - } - if($target instanceof ItemEntity or $target instanceof Arrow){ - $this->kick("Attempting to attack an invalid entity"); - $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); - return false; - } - - $cancelled = false; - - $heldItem = $this->inventory->getItemInHand(); - $oldItem = clone $heldItem; - - if(!$this->canInteract($target, 8) or $this->isSpectator()){ - $cancelled = true; - }elseif($target instanceof Player){ - if(!$this->server->getConfigBool("pvp")){ - $cancelled = true; - } - } - - $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints()); - - $meleeEnchantmentDamage = 0; - /** @var EnchantmentInstance[] $meleeEnchantments */ - $meleeEnchantments = []; - foreach($heldItem->getEnchantments() as $enchantment){ - $type = $enchantment->getType(); - if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){ - $meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel()); - $meleeEnchantments[] = $enchantment; - } - } - $ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS); - - if($cancelled){ - $ev->setCancelled(); - } - - if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){ - $ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL); - } - - $target->attack($ev); - - if($ev->isCancelled()){ - if($heldItem instanceof Durable and $this->isSurvival()){ - $this->inventory->sendContents($this); - } - return true; - } - - if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){ - $pk = new AnimatePacket(); - $pk->action = AnimatePacket::ACTION_CRITICAL_HIT; - $pk->entityRuntimeId = $target->getId(); - $this->server->broadcastPacket($target->getViewers(), $pk); - if($target instanceof Player){ - $target->dataPacket($pk); - } - } - - foreach($meleeEnchantments as $enchantment){ - $type = $enchantment->getType(); - assert($type instanceof MeleeWeaponEnchantment); - $type->onPostAttack($this, $target, $enchantment->getLevel()); - } - - if($this->isAlive()){ - //reactive damage like thorns might cause us to be killed by attacking another mob, which - //would mean we'd already have dropped the inventory by the time we reached here - if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival - $this->inventory->setItemInHand($heldItem); - } - - $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); - } - - return true; - default: - break; //unknown - } - - $this->inventory->sendContents($this); - return false; - }elseif($packet->trData instanceof ReleaseItemTransactionData){ - try{ - switch($packet->trData->getActionType()){ - case ReleaseItemTransactionData::ACTION_RELEASE: - if($this->isUsingItem()){ + }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){ + $this->inventory->sendHeldItem($this); + }else{ $item = $this->inventory->getItemInHand(); - if($this->hasItemCooldown($item)){ - $this->inventory->sendContents($this); - return false; + $oldItem = clone $item; + if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){ + if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); + } + + return true; } - if($item->onReleaseUsing($this)){ - $this->resetItemCooldown($item); - $this->inventory->setItemInHand($item); + } + + $this->inventory->sendHeldItem($this); + + if($blockVector->distanceSquared($this) > 10000){ + return true; + } + + $target = $this->level->getBlock($blockVector); + $block = $target->getSide($face); + + /** @var Block[] $blocks */ + $blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other + + $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); + + return true; + case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK: + $this->doCloseInventory(); + + $item = $this->inventory->getItemInHand(); + $oldItem = clone $item; + + if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){ + if($this->isSurvival()){ + if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){ + $this->inventory->setItemInHand($item); + $this->inventory->sendHeldItem($this->hasSpawned); + } + + $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); } return true; } - break; + + $this->inventory->sendContents($this); + $this->inventory->sendHeldItem($this); + + $target = $this->level->getBlock($blockVector); + /** @var Block[] $blocks */ + $blocks = $target->getAllSides(); + $blocks[] = $target; + + $this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); + + foreach($blocks as $b){ + $tile = $this->level->getTile($b); + if($tile instanceof Spawnable){ + $tile->spawnTo($this); + } + } + + return true; + case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR: + if($this->isUsingItem()){ + $slot = $this->inventory->getItemInHand(); + if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){ + $ev = new PlayerItemConsumeEvent($this, $slot); + if($this->hasItemCooldown($slot)){ + $ev->setCancelled(); + } + $ev->call(); + if($ev->isCancelled() or !$this->consumeObject($slot)){ + $this->inventory->sendContents($this); + return true; + } + $this->resetItemCooldown($slot); + if($this->isSurvival()){ + $slot->pop(); + $this->inventory->setItemInHand($slot); + $this->inventory->addItem($slot->getResidue()); + } + $this->setUsingItem(false); + } + } + $directionVector = $this->getDirectionVector(); + + if($this->isCreative()){ + $item = $this->inventory->getItemInHand(); + }elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){ + $this->inventory->sendHeldItem($this); + return true; + }else{ + $item = $this->inventory->getItemInHand(); + } + + $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); + if($this->hasItemCooldown($item) or $this->isSpectator()){ + $ev->setCancelled(); + } + + $ev->call(); + if($ev->isCancelled()){ + $this->inventory->sendHeldItem($this); + return true; + } + + if($item->onClickAir($this, $directionVector)){ + $this->resetItemCooldown($item); + if($this->isSurvival()){ + $this->inventory->setItemInHand($item); + } + } + + $this->setUsingItem(true); + + return true; default: + //unknown break; } - }finally{ - $this->setUsingItem(false); - } + break; + case InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY: + $target = $this->level->getEntity($packet->trData->entityRuntimeId); + if($target === null){ + return false; + } + + $type = $packet->trData->actionType; + + switch($type){ + case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_INTERACT: + break; //TODO + case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_ATTACK: + if(!$target->isAlive()){ + return true; + } + if($target instanceof ItemEntity or $target instanceof Arrow){ + $this->kick("Attempting to attack an invalid entity"); + $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); + return false; + } + + $cancelled = false; + + $heldItem = $this->inventory->getItemInHand(); + $oldItem = clone $heldItem; + + if(!$this->canInteract($target, 8) or $this->isSpectator()){ + $cancelled = true; + }elseif($target instanceof Player){ + if(!$this->server->getConfigBool("pvp")){ + $cancelled = true; + } + } + + $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints()); + + $meleeEnchantmentDamage = 0; + /** @var EnchantmentInstance[] $meleeEnchantments */ + $meleeEnchantments = []; + foreach($heldItem->getEnchantments() as $enchantment){ + $type = $enchantment->getType(); + if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){ + $meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel()); + $meleeEnchantments[] = $enchantment; + } + } + $ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS); + + if($cancelled){ + $ev->setCancelled(); + } + + if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){ + $ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL); + } + + $target->attack($ev); + + if($ev->isCancelled()){ + if($heldItem instanceof Durable and $this->isSurvival()){ + $this->inventory->sendContents($this); + } + return true; + } + + if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){ + $pk = new AnimatePacket(); + $pk->action = AnimatePacket::ACTION_CRITICAL_HIT; + $pk->entityRuntimeId = $target->getId(); + $this->server->broadcastPacket($target->getViewers(), $pk); + if($target instanceof Player){ + $target->dataPacket($pk); + } + } + + foreach($meleeEnchantments as $enchantment){ + $type = $enchantment->getType(); + assert($type instanceof MeleeWeaponEnchantment); + $type->onPostAttack($this, $target, $enchantment->getLevel()); + } + + if($this->isAlive()){ + //reactive damage like thorns might cause us to be killed by attacking another mob, which + //would mean we'd already have dropped the inventory by the time we reached here + if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival + $this->inventory->setItemInHand($heldItem); + } + + $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); + } + + return true; + default: + break; //unknown + } + + break; + case InventoryTransactionPacket::TYPE_RELEASE_ITEM: + try{ + $type = $packet->trData->actionType; + switch($type){ + case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE: + if($this->isUsingItem()){ + $item = $this->inventory->getItemInHand(); + if($this->hasItemCooldown($item)){ + $this->inventory->sendContents($this); + return false; + } + if($item->onReleaseUsing($this)){ + $this->resetItemCooldown($item); + $this->inventory->setItemInHand($item); + } + }else{ + break; + } + + return true; + default: + break; + } + }finally{ + $this->setUsingItem(false); + } + + $this->inventory->sendContents($this); + break; + default: + $this->inventory->sendContents($this); + break; - $this->inventory->sendContents($this); - return false; - }else{ - $this->inventory->sendContents($this); - return false; } + + return false; //TODO } public function handleMobEquipment(MobEquipmentPacket $packet) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index 0fa447ed6..1c8662fd8 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php @@ -25,15 +25,9 @@ namespace pocketmine\network\mcpe\protocol; #include -use pocketmine\network\mcpe\NetworkSession as PacketHandlerInterface; +use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\inventory\InventoryTransactionChangedSlotsHack; -use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\TransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData; -use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData; -use UnexpectedValueException as PacketDecodeException; +use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use function count; class InventoryTransactionPacket extends DataPacket{ @@ -45,70 +39,137 @@ 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 $requestId; /** @var InventoryTransactionChangedSlotsHack[] */ public $requestChangedSlots; + + /** @var int */ + public $transactionType; /** @var bool */ public $hasItemStackIds; - /** @var TransactionData */ + + /** @var NetworkInventoryAction[] */ + public $actions = []; + + /** @var \stdClass */ public $trData; - protected function decodePayload() : void{ - $in = $this; - $this->requestId = $in->readGenericTypeNetworkId(); + protected function decodePayload(){ + $this->requestId = $this->readGenericTypeNetworkId(); $this->requestChangedSlots = []; if($this->requestId !== 0){ - for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ - $this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($in); + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($this); } } - $transactionType = $in->getUnsignedVarInt(); + $this->transactionType = $this->getUnsignedVarInt(); - $this->hasItemStackIds = $in->getBool(); + $this->hasItemStackIds = $this->getBool(); - switch($transactionType){ + for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ + $this->actions[] = $action = (new NetworkInventoryAction())->read($this, $this->hasItemStackIds); + } + + $this->trData = new \stdClass(); + + switch($this->transactionType){ case self::TYPE_NORMAL: - $this->trData = new NormalTransactionData(); - break; case self::TYPE_MISMATCH: - $this->trData = new MismatchTransactionData(); + //Regular ComplexInventoryTransaction doesn't read any extra data break; case self::TYPE_USE_ITEM: - $this->trData = new UseItemTransactionData(); + $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->blockRuntimeId = $this->getUnsignedVarInt(); break; case self::TYPE_USE_ITEM_ON_ENTITY: - $this->trData = new UseItemOnEntityTransactionData(); + $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(); break; case self::TYPE_RELEASE_ITEM: - $this->trData = new ReleaseItemTransactionData(); + $this->trData->actionType = $this->getUnsignedVarInt(); + $this->trData->hotbarSlot = $this->getVarInt(); + $this->trData->itemInHand = $this->getSlot(); + $this->trData->headPos = $this->getVector3(); break; default: - throw new PacketDecodeException("Unknown transaction type $transactionType"); + throw new \UnexpectedValueException("Unknown transaction type $this->transactionType"); } - - $this->trData->decode($in, $this->hasItemStackIds); } - protected function encodePayload() : void{ - $out = $this; - $out->writeGenericTypeNetworkId($this->requestId); + protected function encodePayload(){ + $this->writeGenericTypeNetworkId($this->requestId); if($this->requestId !== 0){ - $out->putUnsignedVarInt(count($this->requestChangedSlots)); + $this->putUnsignedVarInt(count($this->requestChangedSlots)); foreach($this->requestChangedSlots as $changedSlots){ - $changedSlots->write($out); + $changedSlots->write($this); } } - $out->putUnsignedVarInt($this->trData->getTypeId()); + $this->putUnsignedVarInt($this->transactionType); - $out->putBool($this->hasItemStackIds); + $this->putBool($this->hasItemStackIds); - $this->trData->encode($out, $this->hasItemStackIds); + $this->putUnsignedVarInt(count($this->actions)); + foreach($this->actions as $action){ + $action->write($this, $this->hasItemStackIds); + } + + 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); + $this->putUnsignedVarInt($this->trData->blockRuntimeId); + 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 \InvalidArgumentException("Unknown transaction type $this->transactionType"); + } } - public function handle(PacketHandlerInterface $handler) : bool{ - return $handler->handleInventoryTransaction($this); + public function handle(NetworkSession $session) : bool{ + return $session->handleInventoryTransaction($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php deleted file mode 100644 index 2ee19c64d..000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php +++ /dev/null @@ -1,50 +0,0 @@ -actions) > 0){ - throw new PacketDecodeException("Mismatch transaction type should not have any actions associated with it, but got " . count($this->actions)); - } - } - - protected function encodeData(PacketSerializer $stream) : void{ - - } - - public static function new() : self{ - return new self; //no arguments - } -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php deleted file mode 100644 index 757bf9d62..000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php +++ /dev/null @@ -1,54 +0,0 @@ -actions = $actions; - return $result; - } -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php deleted file mode 100644 index 96b359d17..000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php +++ /dev/null @@ -1,92 +0,0 @@ -actionType; - } - - public function getHotbarSlot() : int{ - return $this->hotbarSlot; - } - - public function getItemInHand() : ItemStack{ - return $this->itemInHand; - } - - public function getHeadPos() : Vector3{ - return $this->headPos; - } - - public function getTypeId() : int{ - return InventoryTransactionPacket::TYPE_RELEASE_ITEM; - } - - protected function decodeData(PacketSerializer $stream) : void{ - $this->actionType = $stream->getUnsignedVarInt(); - $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); - $this->headPos = $stream->getVector3(); - } - - protected function encodeData(PacketSerializer $stream) : void{ - $stream->putUnsignedVarInt($this->actionType); - $stream->putVarInt($this->hotbarSlot); - $stream->putSlot($this->itemInHand); - $stream->putVector3($this->headPos); - } - - /** - * @param NetworkInventoryAction[] $actions - */ - public static function new(array $actions, int $actionType, int $hotbarSlot, ItemStack $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/inventory/TransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php deleted file mode 100644 index 23089c71b..000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php +++ /dev/null @@ -1,72 +0,0 @@ -actions; - } - - abstract public function getTypeId() : int; - - /** - * @throws BinaryDataException - * @throws PacketDecodeException - */ - final public function decode(PacketSerializer $stream, bool $hasItemStackIds) : void{ - $actionCount = $stream->getUnsignedVarInt(); - for($i = 0; $i < $actionCount; ++$i){ - $this->actions[] = (new NetworkInventoryAction())->read($stream, $hasItemStackIds); - } - $this->decodeData($stream); - } - - /** - * @throws BinaryDataException - * @throws PacketDecodeException - */ - abstract protected function decodeData(PacketSerializer $stream) : void; - - final public function encode(PacketSerializer $stream, bool $hasItemStackIds) : void{ - $stream->putUnsignedVarInt(count($this->actions)); - foreach($this->actions as $action){ - $action->write($stream, $hasItemStackIds); - } - $this->encodeData($stream); - } - - abstract protected function encodeData(PacketSerializer $stream) : void; -} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php deleted file mode 100644 index f88bbca73..000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php +++ /dev/null @@ -1,109 +0,0 @@ -entityRuntimeId; - } - - public function getActionType() : int{ - return $this->actionType; - } - - public function getHotbarSlot() : int{ - return $this->hotbarSlot; - } - - public function getItemInHand() : ItemStack{ - return $this->itemInHand; - } - - public function getPlayerPos() : Vector3{ - return $this->playerPos; - } - - public function getClickPos() : Vector3{ - return $this->clickPos; - } - - public function getTypeId() : int{ - return InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY; - } - - protected function decodeData(PacketSerializer $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(PacketSerializer $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); - } - - /** - * @param NetworkInventoryAction[] $actions - */ - public static function new(array $actions, int $entityRuntimeId, int $actionType, int $hotbarSlot, ItemStack $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/inventory/UseItemTransactionData.php b/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php deleted file mode 100644 index 5c1872bea..000000000 --- a/src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php +++ /dev/null @@ -1,130 +0,0 @@ -actionType; - } - - public function getBlockPos() : Vector3{ - return $this->blockPos; - } - - public function getFace() : int{ - return $this->face; - } - - public function getHotbarSlot() : int{ - return $this->hotbarSlot; - } - - public function getItemInHand() : ItemStack{ - return $this->itemInHand; - } - - public function getPlayerPos() : Vector3{ - return $this->playerPos; - } - - public function getClickPos() : Vector3{ - return $this->clickPos; - } - - public function getBlockRuntimeId() : int{ - return $this->blockRuntimeId; - } - - public function getTypeId() : int{ - return InventoryTransactionPacket::TYPE_USE_ITEM; - } - - protected function decodeData(PacketSerializer $stream) : void{ - $this->actionType = $stream->getUnsignedVarInt(); - $x = $y = $z = 0; - $stream->getBlockPosition($x, $y, $z); - $this->blockPos = new Vector3($x, $y, $z); - $this->face = $stream->getVarInt(); - $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); - $this->playerPos = $stream->getVector3(); - $this->clickPos = $stream->getVector3(); - $this->blockRuntimeId = $stream->getUnsignedVarInt(); - } - - protected function encodeData(PacketSerializer $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); - $stream->putUnsignedVarInt($this->blockRuntimeId); - } - - /** - * @param NetworkInventoryAction[] $actions - */ - public static function new(array $actions, int $actionType, Vector3 $blockPos, int $face, int $hotbarSlot, ItemStack $itemInHand, Vector3 $playerPos, Vector3 $clickPos, int $blockRuntimeId) : 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; - $result->blockRuntimeId = $blockRuntimeId; - return $result; - } -} diff --git a/tests/phpstan/configs/l7-baseline.neon b/tests/phpstan/configs/l7-baseline.neon index 3fb827c3d..5b8a923c8 100644 --- a/tests/phpstan/configs/l7-baseline.neon +++ b/tests/phpstan/configs/l7-baseline.neon @@ -695,21 +695,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php - - - message: "#^Parameter \\#1 \\$x of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php - - - - message: "#^Parameter \\#2 \\$y of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php - - - - message: "#^Parameter \\#3 \\$z of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putBlockPosition\\(\\) expects int, float\\|int given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php - - message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#" count: 2