From c7cdaeae8569c766df7db9f889c67f4e65a38d15 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 22 Mar 2021 21:21:11 +0000 Subject: [PATCH] Revert "Backport InventoryTransactionPacket impl from PM4" This reverts commit cb06be615aa3780d4c83a947520fa55c0d908618. we can't push this to stable because it would break plugins without any way to know (no protocol or API change). At most, this should have been wrapped into a protocol change. --- src/pocketmine/Player.php | 587 +++++++++--------- .../protocol/InventoryTransactionPacket.php | 133 ++-- .../inventory/MismatchTransactionData.php | 50 -- .../types/inventory/NormalTransactionData.php | 54 -- .../inventory/ReleaseItemTransactionData.php | 92 --- .../types/inventory/TransactionData.php | 72 --- .../UseItemOnEntityTransactionData.php | 109 ---- .../inventory/UseItemTransactionData.php | 130 ---- tests/phpstan/configs/l7-baseline.neon | 15 - 9 files changed, 392 insertions(+), 850 deletions(-) delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/MismatchTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/NormalTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/TransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php delete mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/UseItemTransactionData.php 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