diff --git a/changelogs/3.19.md b/changelogs/3.19.md new file mode 100644 index 000000000..a59dc1b65 --- /dev/null +++ b/changelogs/3.19.md @@ -0,0 +1,17 @@ +**For Minecraft: Bedrock Edition 1.16.220** + +### Note about API versions +Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps. +Plugin developers should **only** update their required API to this version if you need the changes in this build. + +**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do. + +# 3.19.0 +- Added support for Minecraft: Bedrock Edition 1.16.220. +- Removed compatibility with earlier versions. + +## Known issues (please don't open issues for these) +- Walls don't connect to each other +- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon +- New blocks, items & mobs aren't implemented +- Nether doesn't exist diff --git a/src/entity/Human.php b/src/entity/Human.php index ee48388e5..6dc35ccc7 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -52,6 +52,7 @@ use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\player\Player; use pocketmine\utils\Limits; @@ -428,7 +429,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $pk->motion = $this->getMotion(); $pk->yaw = $this->location->yaw; $pk->pitch = $this->location->pitch; - $pk->item = TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand()); + $pk->item = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())); $pk->metadata = $this->getAllNetworkData(); $player->getNetworkSession()->sendDataPacket($pk); diff --git a/src/entity/object/ItemEntity.php b/src/entity/object/ItemEntity.php index d1e306340..4d5f80f76 100644 --- a/src/entity/object/ItemEntity.php +++ b/src/entity/object/ItemEntity.php @@ -35,6 +35,7 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\protocol\AddItemActorPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\player\Player; use function max; @@ -209,7 +210,7 @@ class ItemEntity extends Entity{ $pk->entityRuntimeId = $this->getId(); $pk->position = $this->location->asVector3(); $pk->motion = $this->getMotion(); - $pk->item = TypeConverter::getInstance()->coreItemStackToNet($this->getItem()); + $pk->item = ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getItem())); $pk->metadata = $this->getAllNetworkData(); $player->getNetworkSession()->sendDataPacket($pk); diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index f0a10429f..3f66c334e 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -234,7 +234,7 @@ class InventoryManager{ if($selected !== $this->clientSelectedHotbarSlot){ $this->session->sendDataPacket(MobEquipmentPacket::create( $this->player->getId(), - TypeConverter::getInstance()->coreItemStackToNet($this->player->getInventory()->getItemInHand()), + ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->player->getInventory()->getItemInHand())), $selected, ContainerIds::INVENTORY )); diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index a8b7432e3..676d244fd 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -91,6 +91,7 @@ use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\entity\Attribute as NetworkAttribute; use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; @@ -958,7 +959,7 @@ class NetworkSession{ public function onMobEquipmentChange(Human $mob) : void{ //TODO: we could send zero for slot here because remote players don't need to know which slot was selected $inv = $mob->getInventory(); - $this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand()), $inv->getHeldItemIndex(), ContainerIds::INVENTORY)); + $this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), ContainerIds::INVENTORY)); } public function onMobArmorChange(Living $mob) : void{ @@ -966,10 +967,10 @@ class NetworkSession{ $converter = TypeConverter::getInstance(); $this->sendDataPacket(MobArmorEquipmentPacket::create( $mob->getId(), - $converter->coreItemStackToNet($inv->getHelmet()), - $converter->coreItemStackToNet($inv->getChestplate()), - $converter->coreItemStackToNet($inv->getLeggings()), - $converter->coreItemStackToNet($inv->getBoots()) + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getHelmet())), + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getChestplate())), + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getLeggings())), + ItemStackWrapper::legacy($converter->coreItemStackToNet($inv->getBoots())) )); } diff --git a/src/network/mcpe/convert/TypeConverter.php b/src/network/mcpe/convert/TypeConverter.php index e856d4a51..48f2e5eca 100644 --- a/src/network/mcpe/convert/TypeConverter.php +++ b/src/network/mcpe/convert/TypeConverter.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\convert; +use pocketmine\block\BlockLegacyIds; use pocketmine\block\inventory\AnvilInventory; use pocketmine\block\inventory\EnchantInventory; use pocketmine\crafting\CraftingGrid; @@ -54,6 +55,7 @@ class TypeConverter{ private const DAMAGE_TAG = "Damage"; //TAG_Int private const DAMAGE_TAG_CONFLICT_RESOLUTION = "___Damage_ProtocolCollisionResolution___"; + private const PM_META_TAG = "___Meta___"; /** @var int */ private $shieldRuntimeId; @@ -136,6 +138,8 @@ class TypeConverter{ if($itemStack->hasNamedTag()){ $nbt = clone $itemStack->getNamedTag(); } + + $block = $itemStack->getBlock(); if($itemStack instanceof Durable and $itemStack->getDamage() > 0){ if($nbt !== null){ if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){ @@ -146,13 +150,24 @@ class TypeConverter{ $nbt = new CompoundTag(); } $nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage()); + }elseif($block->getId() !== BlockLegacyIds::AIR && $itemStack->getMeta() !== 0){ + //TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the + //client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata + //client-side. Aside from being very annoying, this also breaks various server-side behaviours. + if($nbt === null){ + $nbt = new CompoundTag(); + } + $nbt->setInt(self::PM_META_TAG, $itemStack->getMeta()); } [$id, $meta] = ItemTranslator::getInstance()->toNetworkId($itemStack->getId(), $itemStack->getMeta()); + $blockRuntimeId = $block->getId() === BlockLegacyIds::AIR ? 0 : RuntimeBlockMapping::getInstance()->toRuntimeId($block->getFullId()); + return new ItemStack( $id, $meta, $itemStack->getCount(), + $blockRuntimeId, $nbt, [], [], @@ -179,6 +194,15 @@ class TypeConverter{ }elseif($compound->count() === 0){ $compound = null; } + }elseif(($metaTag = $compound->getTag(self::PM_META_TAG)) instanceof IntTag){ + //TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the + //client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata + //client-side. Aside from being very annoying, this also breaks various server-side behaviours. + $meta = $metaTag->getValue(); + $compound->removeTag(self::PM_META_TAG); + if($compound->count() === 0){ + $compound = null; + } } } @@ -210,12 +234,12 @@ class TypeConverter{ * @throws \UnexpectedValueException */ public function createInventoryAction(NetworkInventoryAction $action, Player $player) : ?InventoryAction{ - if($action->oldItem->equals($action->newItem)){ + if($action->oldItem->getItemStack()->equals($action->newItem->getItemStack())){ //filter out useless noise in 1.13 return null; } - $old = $this->netItemStackToCore($action->oldItem); - $new = $this->netItemStackToCore($action->newItem); + $old = $this->netItemStackToCore($action->oldItem->getItemStack()); + $new = $this->netItemStackToCore($action->newItem->getItemStack()); switch($action->sourceType){ case NetworkInventoryAction::SOURCE_CONTAINER: if($action->windowId === ContainerIds::UI and $action->inventorySlot > 0){ diff --git a/src/network/mcpe/encryption/EncryptionContext.php b/src/network/mcpe/encryption/EncryptionContext.php index 1fc0ce031..1b4df11bb 100644 --- a/src/network/mcpe/encryption/EncryptionContext.php +++ b/src/network/mcpe/encryption/EncryptionContext.php @@ -32,7 +32,7 @@ use function strlen; use function substr; class EncryptionContext{ - private const ENCRYPTION_SCHEME = "AES-256-CFB8"; + private const ENCRYPTION_SCHEME = "AES-256-GCM"; private const CHECKSUM_ALGO = "sha256"; /** @var bool */ @@ -52,17 +52,15 @@ class EncryptionContext{ private $encryptCounter = 0; public function __construct(string $encryptionKey){ - //TODO: ext/crypto doesn't offer us a way to disable padding. This doesn't matter at the moment because we're - //using CFB8, but this might change in the future. This is supposed to be CFB8 no-padding. - $this->key = $encryptionKey; - $iv = substr($this->key, 0, 16); $this->decryptCipher = new Cipher(self::ENCRYPTION_SCHEME); - $this->decryptCipher->decryptInit($this->key, $iv); + $ivLength = $this->decryptCipher->getIVLength(); + $this->decryptCipher->decryptInit($this->key, substr($this->key, 0, $ivLength)); $this->encryptCipher = new Cipher(self::ENCRYPTION_SCHEME); - $this->encryptCipher->encryptInit($this->key, $iv); + $ivLength = $this->encryptCipher->getIVLength(); + $this->encryptCipher->encryptInit($this->key, substr($this->key, 0, $ivLength)); } /** diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index f47096715..bd49cb54e 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -253,7 +253,7 @@ class InGamePacketHandler extends PacketHandler{ ) ) or ( $this->craftingTransaction !== null && - !$networkInventoryAction->oldItem->equals($networkInventoryAction->newItem) && + !$networkInventoryAction->oldItem->getItemStack()->equals($networkInventoryAction->newItem->getItemStack()) && $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER && $networkInventoryAction->windowId === ContainerIds::UI && $networkInventoryAction->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT diff --git a/src/network/mcpe/protocol/AddItemActorPacket.php b/src/network/mcpe/protocol/AddItemActorPacket.php index 2a2b05874..bf6f445dd 100644 --- a/src/network/mcpe/protocol/AddItemActorPacket.php +++ b/src/network/mcpe/protocol/AddItemActorPacket.php @@ -28,7 +28,7 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\math\Vector3; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; -use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; class AddItemActorPacket extends DataPacket implements ClientboundPacket{ public const NETWORK_ID = ProtocolInfo::ADD_ITEM_ACTOR_PACKET; @@ -37,7 +37,7 @@ class AddItemActorPacket extends DataPacket implements ClientboundPacket{ public $entityUniqueId = null; //TODO /** @var int */ public $entityRuntimeId; - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $item; /** @var Vector3 */ public $position; @@ -54,7 +54,7 @@ class AddItemActorPacket extends DataPacket implements ClientboundPacket{ protected function decodePayload(PacketSerializer $in) : void{ $this->entityUniqueId = $in->getEntityUniqueId(); $this->entityRuntimeId = $in->getEntityRuntimeId(); - $this->item = $in->getSlot(); + $this->item = ItemStackWrapper::read($in); $this->position = $in->getVector3(); $this->motion = $in->getVector3(); $this->metadata = $in->getEntityMetadata(); @@ -64,7 +64,7 @@ class AddItemActorPacket extends DataPacket implements ClientboundPacket{ protected function encodePayload(PacketSerializer $out) : void{ $out->putEntityUniqueId($this->entityUniqueId ?? $this->entityRuntimeId); $out->putEntityRuntimeId($this->entityRuntimeId); - $out->putSlot($this->item); + $this->item->write($out); $out->putVector3($this->position); $out->putVector3Nullable($this->motion); $out->putEntityMetadata($this->metadata); diff --git a/src/network/mcpe/protocol/AddPlayerPacket.php b/src/network/mcpe/protocol/AddPlayerPacket.php index 3b1bd7d40..c3d45cd77 100644 --- a/src/network/mcpe/protocol/AddPlayerPacket.php +++ b/src/network/mcpe/protocol/AddPlayerPacket.php @@ -30,7 +30,7 @@ use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\types\DeviceOS; use pocketmine\network\mcpe\protocol\types\entity\EntityLink; use pocketmine\network\mcpe\protocol\types\entity\MetadataProperty; -use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use Ramsey\Uuid\UuidInterface; use function count; @@ -57,7 +57,7 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{ public $yaw = 0.0; /** @var float|null */ public $headYaw = null; //TODO - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $item; /** * @var MetadataProperty[] @@ -99,7 +99,7 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{ $this->pitch = $in->getLFloat(); $this->yaw = $in->getLFloat(); $this->headYaw = $in->getLFloat(); - $this->item = $in->getSlot(); + $this->item = ItemStackWrapper::read($in); $this->metadata = $in->getEntityMetadata(); $this->uvarint1 = $in->getUnsignedVarInt(); @@ -130,7 +130,7 @@ class AddPlayerPacket extends DataPacket implements ClientboundPacket{ $out->putLFloat($this->pitch); $out->putLFloat($this->yaw); $out->putLFloat($this->headYaw ?? $this->yaw); - $out->putSlot($this->item); + $this->item->write($out); $out->putEntityMetadata($this->metadata); $out->putUnsignedVarInt($this->uvarint1); diff --git a/src/network/mcpe/protocol/CraftingEventPacket.php b/src/network/mcpe/protocol/CraftingEventPacket.php index e49893869..e35186a55 100644 --- a/src/network/mcpe/protocol/CraftingEventPacket.php +++ b/src/network/mcpe/protocol/CraftingEventPacket.php @@ -26,7 +26,7 @@ namespace pocketmine\network\mcpe\protocol; #include use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; -use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use Ramsey\Uuid\UuidInterface; use function count; @@ -39,9 +39,9 @@ class CraftingEventPacket extends DataPacket implements ServerboundPacket{ public $type; /** @var UuidInterface */ public $id; - /** @var ItemStack[] */ + /** @var ItemStackWrapper[] */ public $input = []; - /** @var ItemStack[] */ + /** @var ItemStackWrapper[] */ public $output = []; protected function decodePayload(PacketSerializer $in) : void{ @@ -51,12 +51,12 @@ class CraftingEventPacket extends DataPacket implements ServerboundPacket{ $size = $in->getUnsignedVarInt(); for($i = 0; $i < $size and $i < 128; ++$i){ - $this->input[] = $in->getSlot(); + $this->input[] = ItemStackWrapper::read($in); } $size = $in->getUnsignedVarInt(); for($i = 0; $i < $size and $i < 128; ++$i){ - $this->output[] = $in->getSlot(); + $this->output[] = ItemStackWrapper::read($in); } } @@ -67,12 +67,12 @@ class CraftingEventPacket extends DataPacket implements ServerboundPacket{ $out->putUnsignedVarInt(count($this->input)); foreach($this->input as $item){ - $out->putSlot($item); + $item->write($out); } $out->putUnsignedVarInt(count($this->output)); foreach($this->output as $item){ - $out->putSlot($item); + $item->write($out); } } diff --git a/src/network/mcpe/protocol/InventoryTransactionPacket.php b/src/network/mcpe/protocol/InventoryTransactionPacket.php index 020af1c5f..a5af13c40 100644 --- a/src/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/network/mcpe/protocol/InventoryTransactionPacket.php @@ -51,8 +51,6 @@ class InventoryTransactionPacket extends DataPacket implements ClientboundPacket public $requestId; /** @var InventoryTransactionChangedSlotsHack[] */ public $requestChangedSlots; - /** @var bool */ - public $hasItemStackIds; /** @var TransactionData */ public $trData; @@ -67,8 +65,6 @@ class InventoryTransactionPacket extends DataPacket implements ClientboundPacket $transactionType = $in->getUnsignedVarInt(); - $this->hasItemStackIds = $in->getBool(); - switch($transactionType){ case self::TYPE_NORMAL: $this->trData = new NormalTransactionData(); @@ -89,7 +85,7 @@ class InventoryTransactionPacket extends DataPacket implements ClientboundPacket throw new PacketDecodeException("Unknown transaction type $transactionType"); } - $this->trData->decode($in, $this->hasItemStackIds); + $this->trData->decode($in); } protected function encodePayload(PacketSerializer $out) : void{ @@ -103,9 +99,7 @@ class InventoryTransactionPacket extends DataPacket implements ClientboundPacket $out->putUnsignedVarInt($this->trData->getTypeId()); - $out->putBool($this->hasItemStackIds); - - $this->trData->encode($out, $this->hasItemStackIds); + $this->trData->encode($out); } public function handle(PacketHandlerInterface $handler) : bool{ diff --git a/src/network/mcpe/protocol/MobArmorEquipmentPacket.php b/src/network/mcpe/protocol/MobArmorEquipmentPacket.php index b7e9ed301..813101d6b 100644 --- a/src/network/mcpe/protocol/MobArmorEquipmentPacket.php +++ b/src/network/mcpe/protocol/MobArmorEquipmentPacket.php @@ -26,7 +26,7 @@ namespace pocketmine\network\mcpe\protocol; #include use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; -use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; class MobArmorEquipmentPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{ public const NETWORK_ID = ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET; @@ -36,16 +36,16 @@ class MobArmorEquipmentPacket extends DataPacket implements ClientboundPacket, S //this intentionally doesn't use an array because we don't want any implicit dependencies on internal order - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $head; - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $chest; - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $legs; - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $feet; - public static function create(int $entityRuntimeId, ItemStack $head, ItemStack $chest, ItemStack $legs, ItemStack $feet) : self{ + public static function create(int $entityRuntimeId, ItemStackWrapper $head, ItemStackWrapper $chest, ItemStackWrapper $legs, ItemStackWrapper $feet) : self{ $result = new self; $result->entityRuntimeId = $entityRuntimeId; $result->head = $head; @@ -58,18 +58,18 @@ class MobArmorEquipmentPacket extends DataPacket implements ClientboundPacket, S protected function decodePayload(PacketSerializer $in) : void{ $this->entityRuntimeId = $in->getEntityRuntimeId(); - $this->head = $in->getSlot(); - $this->chest = $in->getSlot(); - $this->legs = $in->getSlot(); - $this->feet = $in->getSlot(); + $this->head = ItemStackWrapper::read($in); + $this->chest = ItemStackWrapper::read($in); + $this->legs = ItemStackWrapper::read($in); + $this->feet = ItemStackWrapper::read($in); } protected function encodePayload(PacketSerializer $out) : void{ $out->putEntityRuntimeId($this->entityRuntimeId); - $out->putSlot($this->head); - $out->putSlot($this->chest); - $out->putSlot($this->legs); - $out->putSlot($this->feet); + $this->head->write($out); + $this->chest->write($out); + $this->legs->write($out); + $this->feet->write($out); } public function handle(PacketHandlerInterface $handler) : bool{ diff --git a/src/network/mcpe/protocol/MobEquipmentPacket.php b/src/network/mcpe/protocol/MobEquipmentPacket.php index 083b2f6b2..2f36f1cb5 100644 --- a/src/network/mcpe/protocol/MobEquipmentPacket.php +++ b/src/network/mcpe/protocol/MobEquipmentPacket.php @@ -26,14 +26,14 @@ namespace pocketmine\network\mcpe\protocol; #include use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; -use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; class MobEquipmentPacket extends DataPacket implements ClientboundPacket, ServerboundPacket{ public const NETWORK_ID = ProtocolInfo::MOB_EQUIPMENT_PACKET; /** @var int */ public $entityRuntimeId; - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $item; /** @var int */ public $inventorySlot; @@ -42,7 +42,7 @@ class MobEquipmentPacket extends DataPacket implements ClientboundPacket, Server /** @var int */ public $windowId = 0; - public static function create(int $entityRuntimeId, ItemStack $item, int $inventorySlot, int $windowId) : self{ + public static function create(int $entityRuntimeId, ItemStackWrapper $item, int $inventorySlot, int $windowId) : self{ $result = new self; $result->entityRuntimeId = $entityRuntimeId; $result->item = $item; @@ -54,7 +54,7 @@ class MobEquipmentPacket extends DataPacket implements ClientboundPacket, Server protected function decodePayload(PacketSerializer $in) : void{ $this->entityRuntimeId = $in->getEntityRuntimeId(); - $this->item = $in->getSlot(); + $this->item = ItemStackWrapper::read($in); $this->inventorySlot = $in->getByte(); $this->hotbarSlot = $in->getByte(); $this->windowId = $in->getByte(); @@ -62,7 +62,7 @@ class MobEquipmentPacket extends DataPacket implements ClientboundPacket, Server protected function encodePayload(PacketSerializer $out) : void{ $out->putEntityRuntimeId($this->entityRuntimeId); - $out->putSlot($this->item); + $this->item->write($out); $out->putByte($this->inventorySlot); $out->putByte($this->hotbarSlot); $out->putByte($this->windowId); diff --git a/src/network/mcpe/protocol/ProtocolInfo.php b/src/network/mcpe/protocol/ProtocolInfo.php index 85772792a..7cc178699 100644 --- a/src/network/mcpe/protocol/ProtocolInfo.php +++ b/src/network/mcpe/protocol/ProtocolInfo.php @@ -41,11 +41,11 @@ final class ProtocolInfo{ */ /** Actual Minecraft: PE protocol version */ - public const CURRENT_PROTOCOL = 428; + public const CURRENT_PROTOCOL = 431; /** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */ - public const MINECRAFT_VERSION = 'v1.16.210'; + public const MINECRAFT_VERSION = 'v1.16.220'; /** Version number sent to clients in ping responses. */ - public const MINECRAFT_VERSION_NETWORK = '1.16.210'; + public const MINECRAFT_VERSION_NETWORK = '1.16.220'; public const LOGIN_PACKET = 0x01; public const PLAY_STATUS_PACKET = 0x02; diff --git a/src/network/mcpe/protocol/serializer/PacketSerializer.php b/src/network/mcpe/protocol/serializer/PacketSerializer.php index 8e8fed263..1a5b4aeba 100644 --- a/src/network/mcpe/protocol/serializer/PacketSerializer.php +++ b/src/network/mcpe/protocol/serializer/PacketSerializer.php @@ -25,7 +25,9 @@ namespace pocketmine\network\mcpe\protocol\serializer; #include +use pocketmine\entity\Entity; use pocketmine\math\Vector3; +use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NbtDataException; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\TreeRoot; @@ -216,49 +218,83 @@ class PacketSerializer extends BinaryStream{ * @throws PacketDecodeException * @throws BinaryDataException */ - public function getSlot() : ItemStack{ + public function getItemStackWithoutStackId() : ItemStack{ + return $this->getItemStack(function() : void{ + //NOOP + }); + } + + public function putItemStackWithoutStackId(ItemStack $item) : void{ + $this->putItemStack($item, function() : void{ + //NOOP + }); + } + + /** + * @phpstan-param \Closure(PacketSerializer) : void $readExtraCrapInTheMiddle + * + * @throws PacketDecodeException + * @throws BinaryDataException + */ + public function getItemStack(\Closure $readExtraCrapInTheMiddle) : ItemStack{ $id = $this->getVarInt(); if($id === 0){ return ItemStack::null(); } - $auxValue = $this->getVarInt(); - $meta = $auxValue >> 8; - $count = $auxValue & 0xff; + $count = $this->getLShort(); + $meta = $this->getUnsignedVarInt(); - $nbtLen = $this->getLShort(); + $readExtraCrapInTheMiddle($this); - /** @var CompoundTag|null $compound */ - $compound = null; - if($nbtLen === 0xffff){ - $nbtDataVersion = $this->getByte(); - if($nbtDataVersion !== 1){ - throw new PacketDecodeException("Unexpected NBT data version $nbtDataVersion"); + $blockRuntimeId = $this->getVarInt(); + $extraData = new PacketSerializer($this->getString()); + $shieldItemRuntimeId = $this->shieldItemRuntimeId; + return (static function() use ($extraData, $id, $meta, $count, $blockRuntimeId, $shieldItemRuntimeId) : ItemStack{ + $nbtLen = $extraData->getLShort(); + + /** @var CompoundTag|null $compound */ + $compound = null; + if($nbtLen === 0xffff){ + $nbtDataVersion = $extraData->getByte(); + if($nbtDataVersion !== 1){ + throw new PacketDecodeException("Unexpected NBT data version $nbtDataVersion"); + } + $offset = $extraData->getOffset(); + try{ + $compound = (new LittleEndianNbtSerializer())->read($extraData->getBuffer(), $offset, 512)->mustGetCompoundTag(); + }catch(NbtDataException $e){ + throw PacketDecodeException::wrap($e, "Failed decoding NBT root"); + }finally{ + $extraData->setOffset($offset); + } + }elseif($nbtLen !== 0){ + throw new PacketDecodeException("Unexpected fake NBT length $nbtLen"); } - $compound = $this->getNbtCompoundRoot(); - }elseif($nbtLen !== 0){ - throw new PacketDecodeException("Unexpected fake NBT length $nbtLen"); - } - $canPlaceOn = []; - for($i = 0, $canPlaceOnCount = $this->getVarInt(); $i < $canPlaceOnCount; ++$i){ - $canPlaceOn[] = $this->getString(); - } + $canPlaceOn = []; + for($i = 0, $canPlaceOnCount = $extraData->getLInt(); $i < $canPlaceOnCount; ++$i){ + $canPlaceOn[] = $extraData->get($extraData->getLShort()); + } - $canDestroy = []; - for($i = 0, $canDestroyCount = $this->getVarInt(); $i < $canDestroyCount; ++$i){ - $canDestroy[] = $this->getString(); - } + $canDestroy = []; + for($i = 0, $canDestroyCount = $extraData->getLInt(); $i < $canDestroyCount; ++$i){ + $canDestroy[] = $extraData->get($extraData->getLShort()); + } - $shieldBlockingTick = null; - if($id === $this->shieldItemRuntimeId){ - $shieldBlockingTick = $this->getVarLong(); - } + $shieldBlockingTick = null; + if($id === $shieldItemRuntimeId){ + $shieldBlockingTick = $extraData->getLLong(); + } - return new ItemStack($id, $meta, $count, $compound, $canPlaceOn, $canDestroy, $shieldBlockingTick); + return new ItemStack($id, $meta, $count, $blockRuntimeId, $compound, $canPlaceOn, $canDestroy, $shieldBlockingTick); + })(); } - public function putSlot(ItemStack $item) : void{ + /** + * @phpstan-param \Closure(PacketSerializer) : void $writeExtraCrapInTheMiddle + */ + public function putItemStack(ItemStack $item, \Closure $writeExtraCrapInTheMiddle) : void{ if($item->getId() === 0){ $this->putVarInt(0); @@ -266,31 +302,42 @@ class PacketSerializer extends BinaryStream{ } $this->putVarInt($item->getId()); - $auxValue = (($item->getMeta() & 0x7fff) << 8) | $item->getCount(); - $this->putVarInt($auxValue); + $this->putLShort($item->getCount()); + $this->putUnsignedVarInt($item->getMeta()); - $nbt = $item->getNbt(); - if($nbt !== null){ - $this->putLShort(0xffff); - $this->putByte(1); //TODO: NBT data version (?) - $this->put((new NetworkNbtSerializer())->write(new TreeRoot($nbt))); - }else{ - $this->putLShort(0); - } + $writeExtraCrapInTheMiddle($this); - $this->putVarInt(count($item->getCanPlaceOn())); - foreach($item->getCanPlaceOn() as $entry){ - $this->putString($entry); - } - $this->putVarInt(count($item->getCanDestroy())); - foreach($item->getCanDestroy() as $entry){ - $this->putString($entry); - } + $this->putVarInt($item->getBlockRuntimeId()); + $shieldItemRuntimeId = $this->shieldItemRuntimeId; + $this->putString((static function() use ($item, $shieldItemRuntimeId) : string{ + $extraData = new PacketSerializer(); - $blockingTick = $item->getShieldBlockingTick(); - if($item->getId() === $this->shieldItemRuntimeId){ - $this->putVarLong($blockingTick ?? 0); - } + $nbt = $item->getNbt(); + if($nbt !== null){ + $extraData->putLShort(0xffff); + $extraData->putByte(1); //TODO: NBT data version (?) + $extraData->put((new LittleEndianNbtSerializer())->write(new TreeRoot($nbt))); + }else{ + $extraData->putLShort(0); + } + + $extraData->putLInt(count($item->getCanPlaceOn())); + foreach($item->getCanPlaceOn() as $entry){ + $extraData->putLShort(strlen($entry)); + $extraData->put($entry); + } + $extraData->putLInt(count($item->getCanDestroy())); + foreach($item->getCanDestroy() as $entry){ + $extraData->putLShort(strlen($entry)); + $extraData->put($entry); + } + + $blockingTick = $item->getShieldBlockingTick(); + if($item->getId() === $shieldItemRuntimeId){ + $extraData->putLLong($blockingTick ?? 0); + } + return $extraData->getBuffer(); + })()); } public function getRecipeIngredient() : RecipeIngredient{ diff --git a/src/network/mcpe/protocol/types/inventory/CreativeContentEntry.php b/src/network/mcpe/protocol/types/inventory/CreativeContentEntry.php index 3204bed14..a07d09873 100644 --- a/src/network/mcpe/protocol/types/inventory/CreativeContentEntry.php +++ b/src/network/mcpe/protocol/types/inventory/CreativeContentEntry.php @@ -43,12 +43,12 @@ final class CreativeContentEntry{ public static function read(PacketSerializer $in) : self{ $entryId = $in->readGenericTypeNetworkId(); - $item = $in->getSlot(); + $item = $in->getItemStackWithoutStackId(); return new self($entryId, $item); } public function write(PacketSerializer $out) : void{ $out->writeGenericTypeNetworkId($this->entryId); - $out->putSlot($this->item); + $out->putItemStackWithoutStackId($this->item); } } diff --git a/src/network/mcpe/protocol/types/inventory/ItemStack.php b/src/network/mcpe/protocol/types/inventory/ItemStack.php index 5ab1058b8..2250c33ad 100644 --- a/src/network/mcpe/protocol/types/inventory/ItemStack.php +++ b/src/network/mcpe/protocol/types/inventory/ItemStack.php @@ -37,6 +37,8 @@ final class ItemStack implements \JsonSerializable{ private $meta; /** @var int */ private $count; + /** @var int */ + private $blockRuntimeId; /** @var string[] */ private $canPlaceOn; /** @var string[] */ @@ -50,10 +52,11 @@ final class ItemStack implements \JsonSerializable{ * @param string[] $canPlaceOn * @param string[] $canDestroy */ - public function __construct(int $id, int $meta, int $count, ?CompoundTag $nbt, array $canPlaceOn, array $canDestroy, ?int $shieldBlockingTick = null){ + public function __construct(int $id, int $meta, int $count, int $blockRuntimeId, ?CompoundTag $nbt, array $canPlaceOn, array $canDestroy, ?int $shieldBlockingTick = null){ $this->id = $id; $this->meta = $meta; $this->count = $count; + $this->blockRuntimeId = $blockRuntimeId; $this->canPlaceOn = $canPlaceOn; $this->canDestroy = $canDestroy; $this->nbt = $nbt; @@ -61,7 +64,7 @@ final class ItemStack implements \JsonSerializable{ } public static function null() : self{ - return new self(0, 0, 0, null, [], [], null); + return new self(0, 0, 0, 0, null, [], [], null); } public function getId() : int{ @@ -76,6 +79,8 @@ final class ItemStack implements \JsonSerializable{ return $this->count; } + public function getBlockRuntimeId() : int{ return $this->blockRuntimeId; } + /** * @return string[] */ @@ -103,6 +108,7 @@ final class ItemStack implements \JsonSerializable{ $this->id === $itemStack->id && $this->meta === $itemStack->meta && $this->count === $itemStack->count && + $this->blockRuntimeId === $itemStack->blockRuntimeId && $this->canPlaceOn === $itemStack->canPlaceOn && $this->canDestroy === $itemStack->canDestroy && $this->shieldBlockingTick === $itemStack->shieldBlockingTick && ( @@ -117,6 +123,7 @@ final class ItemStack implements \JsonSerializable{ "id" => $this->id, "meta" => $this->meta, "count" => $this->count, + "blockRuntimeId" => $this->blockRuntimeId, ]; if(count($this->canPlaceOn) > 0){ $result["canPlaceOn"] = $this->canPlaceOn; diff --git a/src/network/mcpe/protocol/types/inventory/ItemStackWrapper.php b/src/network/mcpe/protocol/types/inventory/ItemStackWrapper.php index 63c1d9896..1ed56454d 100644 --- a/src/network/mcpe/protocol/types/inventory/ItemStackWrapper.php +++ b/src/network/mcpe/protocol/types/inventory/ItemStackWrapper.php @@ -46,13 +46,22 @@ final class ItemStackWrapper{ public function getItemStack() : ItemStack{ return $this->itemStack; } public static function read(PacketSerializer $in) : self{ - $stackId = $in->readGenericTypeNetworkId(); - $stack = $in->getSlot(); + $stackId = 0; + $stack = $in->getItemStack(function(PacketSerializer $in) use (&$stackId) : void{ + $hasNetId = $in->getBool(); + if($hasNetId){ + $stackId = $in->readGenericTypeNetworkId(); + } + }); return new self($stackId, $stack); } public function write(PacketSerializer $out) : void{ - $out->writeGenericTypeNetworkId($this->stackId); - $out->putSlot($this->itemStack); + $out->putItemStack($this->itemStack, function(PacketSerializer $out) : void{ + $out->putBool($this->stackId !== 0); + if($this->stackId !== 0){ + $out->writeGenericTypeNetworkId($this->stackId); + } + }); } } diff --git a/src/network/mcpe/protocol/types/inventory/NetworkInventoryAction.php b/src/network/mcpe/protocol/types/inventory/NetworkInventoryAction.php index 37b63ee3d..f966be26d 100644 --- a/src/network/mcpe/protocol/types/inventory/NetworkInventoryAction.php +++ b/src/network/mcpe/protocol/types/inventory/NetworkInventoryAction.php @@ -72,12 +72,10 @@ class NetworkInventoryAction{ public $sourceFlags = 0; /** @var int */ public $inventorySlot; - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $oldItem; - /** @var ItemStack */ + /** @var ItemStackWrapper */ public $newItem; - /** @var int|null */ - public $newItemStackId = null; /** * @return $this @@ -85,7 +83,7 @@ class NetworkInventoryAction{ * @throws BinaryDataException * @throws PacketDecodeException */ - public function read(PacketSerializer $packet, bool $hasItemStackIds) : NetworkInventoryAction{ + public function read(PacketSerializer $packet) : NetworkInventoryAction{ $this->sourceType = $packet->getUnsignedVarInt(); switch($this->sourceType){ @@ -105,11 +103,8 @@ class NetworkInventoryAction{ } $this->inventorySlot = $packet->getUnsignedVarInt(); - $this->oldItem = $packet->getSlot(); - $this->newItem = $packet->getSlot(); - if($hasItemStackIds){ - $this->newItemStackId = $packet->readGenericTypeNetworkId(); - } + $this->oldItem = ItemStackWrapper::read($packet); + $this->newItem = ItemStackWrapper::read($packet); return $this; } @@ -117,7 +112,7 @@ class NetworkInventoryAction{ /** * @throws \InvalidArgumentException */ - public function write(PacketSerializer $packet, bool $hasItemStackIds) : void{ + public function write(PacketSerializer $packet) : void{ $packet->putUnsignedVarInt($this->sourceType); switch($this->sourceType){ @@ -137,13 +132,7 @@ class NetworkInventoryAction{ } $packet->putUnsignedVarInt($this->inventorySlot); - $packet->putSlot($this->oldItem); - $packet->putSlot($this->newItem); - if($hasItemStackIds){ - if($this->newItemStackId === null){ - throw new \InvalidStateException("Item stack ID for newItem must be provided"); - } - $packet->writeGenericTypeNetworkId($this->newItemStackId); - } + $this->oldItem->write($packet); + $this->newItem->write($packet); } } diff --git a/src/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php b/src/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php index f60086d6d..5db088e41 100644 --- a/src/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php +++ b/src/network/mcpe/protocol/types/inventory/ReleaseItemTransactionData.php @@ -35,7 +35,7 @@ class ReleaseItemTransactionData extends TransactionData{ private $actionType; /** @var int */ private $hotbarSlot; - /** @var ItemStack */ + /** @var ItemStackWrapper */ private $itemInHand; /** @var Vector3 */ private $headPos; @@ -48,7 +48,7 @@ class ReleaseItemTransactionData extends TransactionData{ return $this->hotbarSlot; } - public function getItemInHand() : ItemStack{ + public function getItemInHand() : ItemStackWrapper{ return $this->itemInHand; } @@ -63,21 +63,21 @@ class ReleaseItemTransactionData extends TransactionData{ protected function decodeData(PacketSerializer $stream) : void{ $this->actionType = $stream->getUnsignedVarInt(); $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); + $this->itemInHand = ItemStackWrapper::read($stream); $this->headPos = $stream->getVector3(); } protected function encodeData(PacketSerializer $stream) : void{ $stream->putUnsignedVarInt($this->actionType); $stream->putVarInt($this->hotbarSlot); - $stream->putSlot($this->itemInHand); + $this->itemInHand->write($stream); $stream->putVector3($this->headPos); } /** * @param NetworkInventoryAction[] $actions */ - public static function new(array $actions, int $actionType, int $hotbarSlot, ItemStack $itemInHand, Vector3 $headPos) : self{ + public static function new(array $actions, int $actionType, int $hotbarSlot, ItemStackWrapper $itemInHand, Vector3 $headPos) : self{ $result = new self; $result->actions = $actions; $result->actionType = $actionType; diff --git a/src/network/mcpe/protocol/types/inventory/TransactionData.php b/src/network/mcpe/protocol/types/inventory/TransactionData.php index 426ddbe34..ea10fce25 100644 --- a/src/network/mcpe/protocol/types/inventory/TransactionData.php +++ b/src/network/mcpe/protocol/types/inventory/TransactionData.php @@ -45,10 +45,10 @@ abstract class TransactionData{ * @throws BinaryDataException * @throws PacketDecodeException */ - final public function decode(PacketSerializer $stream, bool $hasItemStackIds) : void{ + final public function decode(PacketSerializer $stream) : void{ $actionCount = $stream->getUnsignedVarInt(); for($i = 0; $i < $actionCount; ++$i){ - $this->actions[] = (new NetworkInventoryAction())->read($stream, $hasItemStackIds); + $this->actions[] = (new NetworkInventoryAction())->read($stream); } $this->decodeData($stream); } @@ -59,10 +59,10 @@ abstract class TransactionData{ */ abstract protected function decodeData(PacketSerializer $stream) : void; - final public function encode(PacketSerializer $stream, bool $hasItemStackIds) : void{ + final public function encode(PacketSerializer $stream) : void{ $stream->putUnsignedVarInt(count($this->actions)); foreach($this->actions as $action){ - $action->write($stream, $hasItemStackIds); + $action->write($stream); } $this->encodeData($stream); } diff --git a/src/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php b/src/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php index e2e1eb229..9b9de7c1e 100644 --- a/src/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php +++ b/src/network/mcpe/protocol/types/inventory/UseItemOnEntityTransactionData.php @@ -37,7 +37,7 @@ class UseItemOnEntityTransactionData extends TransactionData{ private $actionType; /** @var int */ private $hotbarSlot; - /** @var ItemStack */ + /** @var ItemStackWrapper */ private $itemInHand; /** @var Vector3 */ private $playerPos; @@ -56,7 +56,7 @@ class UseItemOnEntityTransactionData extends TransactionData{ return $this->hotbarSlot; } - public function getItemInHand() : ItemStack{ + public function getItemInHand() : ItemStackWrapper{ return $this->itemInHand; } @@ -76,7 +76,7 @@ class UseItemOnEntityTransactionData extends TransactionData{ $this->entityRuntimeId = $stream->getEntityRuntimeId(); $this->actionType = $stream->getUnsignedVarInt(); $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); + $this->itemInHand = ItemStackWrapper::read($stream); $this->playerPos = $stream->getVector3(); $this->clickPos = $stream->getVector3(); } @@ -85,7 +85,7 @@ class UseItemOnEntityTransactionData extends TransactionData{ $stream->putEntityRuntimeId($this->entityRuntimeId); $stream->putUnsignedVarInt($this->actionType); $stream->putVarInt($this->hotbarSlot); - $stream->putSlot($this->itemInHand); + $this->itemInHand->write($stream); $stream->putVector3($this->playerPos); $stream->putVector3($this->clickPos); } @@ -93,7 +93,7 @@ class UseItemOnEntityTransactionData extends TransactionData{ /** * @param NetworkInventoryAction[] $actions */ - public static function new(array $actions, int $entityRuntimeId, int $actionType, int $hotbarSlot, ItemStack $itemInHand, Vector3 $playerPos, Vector3 $clickPos) : self{ + public static function new(array $actions, int $entityRuntimeId, int $actionType, int $hotbarSlot, ItemStackWrapper $itemInHand, Vector3 $playerPos, Vector3 $clickPos) : self{ $result = new self; $result->actions = $actions; $result->entityRuntimeId = $entityRuntimeId; diff --git a/src/network/mcpe/protocol/types/inventory/UseItemTransactionData.php b/src/network/mcpe/protocol/types/inventory/UseItemTransactionData.php index 0ef87c032..0a425e9d6 100644 --- a/src/network/mcpe/protocol/types/inventory/UseItemTransactionData.php +++ b/src/network/mcpe/protocol/types/inventory/UseItemTransactionData.php @@ -40,7 +40,7 @@ class UseItemTransactionData extends TransactionData{ private $face; /** @var int */ private $hotbarSlot; - /** @var ItemStack */ + /** @var ItemStackWrapper */ private $itemInHand; /** @var Vector3 */ private $playerPos; @@ -65,7 +65,7 @@ class UseItemTransactionData extends TransactionData{ return $this->hotbarSlot; } - public function getItemInHand() : ItemStack{ + public function getItemInHand() : ItemStackWrapper{ return $this->itemInHand; } @@ -92,7 +92,7 @@ class UseItemTransactionData extends TransactionData{ $this->blockPos = new Vector3($x, $y, $z); $this->face = $stream->getVarInt(); $this->hotbarSlot = $stream->getVarInt(); - $this->itemInHand = $stream->getSlot(); + $this->itemInHand = ItemStackWrapper::read($stream); $this->playerPos = $stream->getVector3(); $this->clickPos = $stream->getVector3(); $this->blockRuntimeId = $stream->getUnsignedVarInt(); @@ -103,7 +103,7 @@ class UseItemTransactionData extends TransactionData{ $stream->putBlockPosition($this->blockPos->x, $this->blockPos->y, $this->blockPos->z); $stream->putVarInt($this->face); $stream->putVarInt($this->hotbarSlot); - $stream->putSlot($this->itemInHand); + $this->itemInHand->write($stream); $stream->putVector3($this->playerPos); $stream->putVector3($this->clickPos); $stream->putUnsignedVarInt($this->blockRuntimeId); @@ -112,7 +112,7 @@ class UseItemTransactionData extends TransactionData{ /** * @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{ + public static function new(array $actions, int $actionType, Vector3 $blockPos, int $face, int $hotbarSlot, ItemStackWrapper $itemInHand, Vector3 $playerPos, Vector3 $clickPos, int $blockRuntimeId) : self{ $result = new self; $result->actions = $actions; $result->actionType = $actionType; diff --git a/src/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingResultsStackRequestAction.php b/src/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingResultsStackRequestAction.php index b9832a742..4ca9fd1f7 100644 --- a/src/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingResultsStackRequestAction.php +++ b/src/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingResultsStackRequestAction.php @@ -58,7 +58,7 @@ final class DeprecatedCraftingResultsStackRequestAction extends ItemStackRequest public static function read(PacketSerializer $in) : self{ $results = []; for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ - $results[] = $in->getSlot(); + $results[] = $in->getItemStackWithoutStackId(); } $iterations = $in->getByte(); return new self($results, $iterations); @@ -67,7 +67,7 @@ final class DeprecatedCraftingResultsStackRequestAction extends ItemStackRequest public function write(PacketSerializer $out) : void{ $out->putUnsignedVarInt(count($this->results)); foreach($this->results as $result){ - $out->putSlot($result); + $out->putItemStackWithoutStackId($result); } $out->putByte($this->iterations); } diff --git a/src/network/mcpe/protocol/types/recipe/FurnaceRecipe.php b/src/network/mcpe/protocol/types/recipe/FurnaceRecipe.php index 5d122fc24..7563acc23 100644 --- a/src/network/mcpe/protocol/types/recipe/FurnaceRecipe.php +++ b/src/network/mcpe/protocol/types/recipe/FurnaceRecipe.php @@ -68,7 +68,7 @@ final class FurnaceRecipe extends RecipeWithTypeId{ if($typeId === CraftingDataPacket::ENTRY_FURNACE_DATA){ $inputData = $in->getVarInt(); } - $output = $in->getSlot(); + $output = $in->getItemStackWithoutStackId(); $block = $in->getString(); return new self($typeId, $inputId, $inputData, $output, $block); @@ -79,7 +79,7 @@ final class FurnaceRecipe extends RecipeWithTypeId{ if($this->getTypeId() === CraftingDataPacket::ENTRY_FURNACE_DATA){ $out->putVarInt($this->inputMeta); } - $out->putSlot($this->result); + $out->putItemStackWithoutStackId($this->result); $out->putString($this->blockName); } } diff --git a/src/network/mcpe/protocol/types/recipe/ShapedRecipe.php b/src/network/mcpe/protocol/types/recipe/ShapedRecipe.php index 1a9aee0b8..55f10bdc5 100644 --- a/src/network/mcpe/protocol/types/recipe/ShapedRecipe.php +++ b/src/network/mcpe/protocol/types/recipe/ShapedRecipe.php @@ -127,7 +127,7 @@ final class ShapedRecipe extends RecipeWithTypeId{ $output = []; for($k = 0, $resultCount = $in->getUnsignedVarInt(); $k < $resultCount; ++$k){ - $output[] = $in->getSlot(); + $output[] = $in->getItemStackWithoutStackId(); } $uuid = $in->getUUID(); $block = $in->getString(); @@ -149,7 +149,7 @@ final class ShapedRecipe extends RecipeWithTypeId{ $out->putUnsignedVarInt(count($this->output)); foreach($this->output as $item){ - $out->putSlot($item); + $out->putItemStackWithoutStackId($item); } $out->putUUID($this->uuid); diff --git a/src/network/mcpe/protocol/types/recipe/ShapelessRecipe.php b/src/network/mcpe/protocol/types/recipe/ShapelessRecipe.php index 0facbab7c..c97dd40bd 100644 --- a/src/network/mcpe/protocol/types/recipe/ShapelessRecipe.php +++ b/src/network/mcpe/protocol/types/recipe/ShapelessRecipe.php @@ -102,7 +102,7 @@ final class ShapelessRecipe extends RecipeWithTypeId{ } $output = []; for($k = 0, $resultCount = $in->getUnsignedVarInt(); $k < $resultCount; ++$k){ - $output[] = $in->getSlot(); + $output[] = $in->getItemStackWithoutStackId(); } $uuid = $in->getUUID(); $block = $in->getString(); @@ -121,7 +121,7 @@ final class ShapelessRecipe extends RecipeWithTypeId{ $out->putUnsignedVarInt(count($this->outputs)); foreach($this->outputs as $item){ - $out->putSlot($item); + $out->putItemStackWithoutStackId($item); } $out->putUUID($this->uuid); diff --git a/src/world/particle/FloatingTextParticle.php b/src/world/particle/FloatingTextParticle.php index b2caf0606..fd7e14f0c 100644 --- a/src/world/particle/FloatingTextParticle.php +++ b/src/world/particle/FloatingTextParticle.php @@ -35,6 +35,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty; use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use Ramsey\Uuid\Uuid; use function str_repeat; @@ -100,7 +101,7 @@ class FloatingTextParticle implements Particle{ $pk->username = $name; $pk->entityRuntimeId = $this->entityId; $pk->position = $pos; //TODO: check offset - $pk->item = ItemStack::null(); + $pk->item = ItemStackWrapper::legacy(ItemStack::null()); $flags = ( 1 << EntityMetadataFlags::IMMOBILE