From e990c5a0a57a651c5d6a0796d0692a1d6aae868d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 26 Jun 2020 12:46:52 +0100 Subject: [PATCH] Protocol changes for 1.16.0 --- src/pocketmine/Player.php | 65 ++++++--- src/pocketmine/block/Block.php | 2 +- src/pocketmine/block/BlockFactory.php | 2 +- src/pocketmine/block/CraftingTable.php | 12 ++ src/pocketmine/entity/Entity.php | 15 ++- src/pocketmine/inventory/ArmorInventory.php | 6 +- src/pocketmine/inventory/BaseInventory.php | 6 +- src/pocketmine/inventory/PlayerInventory.php | 17 +-- .../transaction/CraftingTransaction.php | 4 +- .../item/enchantment/Enchantment.php | 1 + src/pocketmine/level/Level.php | 2 +- .../network/mcpe/NetworkBinaryStream.php | 16 ++- .../network/mcpe/NetworkSession.php | 65 ++++++++- .../network/mcpe/RakLibInterface.php | 2 +- .../convert/R12ToCurrentBlockMapEntry.php | 58 ++++++++ .../types => convert}/RuntimeBlockMapping.php | 33 +++-- .../mcpe/protocol/ActorEventPacket.php | 1 + .../network/mcpe/protocol/BatchPacket.php | 3 +- .../mcpe/protocol/CodeBuilderPacket.php | 66 +++++++++ .../mcpe/protocol/CraftingDataPacket.php | 17 ++- .../mcpe/protocol/CreativeContentPacket.php | 67 ++++++++++ .../network/mcpe/protocol/DebugInfoPacket.php | 65 +++++++++ .../mcpe/protocol/EducationSettingsPacket.php | 36 ++++- .../network/mcpe/protocol/EmoteListPacket.php | 74 ++++++++++ .../network/mcpe/protocol/HurtArmorPacket.php | 4 + .../mcpe/protocol/InventoryContentPacket.php | 8 +- .../mcpe/protocol/InventorySlotPacket.php | 8 +- .../protocol/InventoryTransactionPacket.php | 30 ++++- .../mcpe/protocol/ItemStackRequestPacket.php | 67 ++++++++++ .../mcpe/protocol/ItemStackResponsePacket.php | 67 ++++++++++ .../network/mcpe/protocol/PacketPool.php | 13 +- .../protocol/PacketViolationWarningPacket.php | 84 ++++++++++++ .../mcpe/protocol/PlayerActionPacket.php | 2 +- .../mcpe/protocol/PlayerArmorDamagePacket.php | 108 +++++++++++++++ .../protocol/PlayerEnchantOptionsPacket.php | 69 ++++++++++ ...PositionTrackingDBClientRequestPacket.php} | 42 +++--- ...ositionTrackingDBServerBroadcastPacket.php | 81 +++++++++++ .../network/mcpe/protocol/ProtocolInfo.php | 20 ++- .../mcpe/protocol/SetSpawnPositionPacket.php | 16 ++- .../network/mcpe/protocol/StartGamePacket.php | 48 +++++-- .../network/mcpe/protocol/TextPacket.php | 5 +- .../protocol/UpdatePlayerGameTypePacket.php | 67 ++++++++++ .../mcpe/protocol/types/ContainerIds.php | 2 +- .../network/mcpe/protocol/types/Enchant.php | 53 ++++++++ .../mcpe/protocol/types/EnchantOption.php | 126 ++++++++++++++++++ .../mcpe/protocol/types/EntityLink.php | 5 +- .../protocol/types/NetworkInventoryAction.php | 15 ++- .../mcpe/protocol/types/PotionTypeRecipe.php | 39 ++++-- .../mcpe/protocol/types/SpawnSettings.php | 73 ++++++++++ .../mcpe/protocol/types/WindowTypes.php | 3 + .../types/inventory/CreativeContentEntry.php | 55 ++++++++ .../InventoryTransactionChangedSlotsHack.php | 65 +++++++++ .../types/inventory/ItemStackWrapper.php | 59 ++++++++ .../BeaconPaymentStackRequestAction.php | 59 ++++++++ .../CraftRecipeAutoStackRequestAction.php | 34 +++++ .../CraftRecipeStackRequestAction.php | 33 +++++ .../CraftRecipeStackRequestActionTrait.php | 47 +++++++ ...CraftingConsumeInputStackRequestAction.php | 33 +++++ ...gMarkSecondaryResultStackRequestAction.php | 53 ++++++++ .../CreativeCreateStackRequestAction.php | 52 ++++++++ ...aftingNonImplementedStackRequestAction.php | 45 +++++++ ...catedCraftingResultsStackRequestAction.php | 74 ++++++++++ .../DestroyStackRequestAction.php | 34 +++++ .../DisappearStackRequestActionTrait.php | 53 ++++++++ .../stackrequest/DropStackRequestAction.php | 66 +++++++++ .../stackrequest/ItemStackRequest.php | 87 ++++++++++++ .../stackrequest/ItemStackRequestAction.php | 33 +++++ .../ItemStackRequestActionType.php | 46 +++++++ .../stackrequest/ItemStackRequestSlotInfo.php | 61 +++++++++ .../LabTableCombineStackRequestAction.php | 43 ++++++ .../stackrequest/PlaceStackRequestAction.php | 33 +++++ .../stackrequest/SwapStackRequestAction.php | 59 ++++++++ .../TakeOrPlaceStackRequestActionTrait.php | 61 +++++++++ .../stackrequest/TakeStackRequestAction.php | 33 +++++ .../stackresponse/ItemStackResponse.php | 72 ++++++++++ .../ItemStackResponseContainerInfo.php | 65 +++++++++ .../ItemStackResponseSlotInfo.php | 68 ++++++++++ src/pocketmine/resources/vanilla | 2 +- .../check-explicit-mixed-baseline.neon | 15 ++- tests/phpstan/configs/l7-baseline.neon | 30 +++-- tests/phpstan/configs/l8-baseline.neon | 50 +++---- 81 files changed, 3026 insertions(+), 184 deletions(-) create mode 100644 src/pocketmine/network/mcpe/convert/R12ToCurrentBlockMapEntry.php rename src/pocketmine/network/mcpe/{protocol/types => convert}/RuntimeBlockMapping.php (82%) create mode 100644 src/pocketmine/network/mcpe/protocol/CodeBuilderPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/CreativeContentPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/DebugInfoPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/EmoteListPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/ItemStackRequestPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/ItemStackResponsePacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/PacketViolationWarningPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/PlayerArmorDamagePacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/PlayerEnchantOptionsPacket.php rename src/pocketmine/network/mcpe/protocol/{VideoStreamConnectPacket.php => PositionTrackingDBClientRequestPacket.php} (56%) create mode 100644 src/pocketmine/network/mcpe/protocol/PositionTrackingDBServerBroadcastPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/Enchant.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/EnchantOption.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/SpawnSettings.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/CreativeContentEntry.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/InventoryTransactionChangedSlotsHack.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/ItemStackWrapper.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/BeaconPaymentStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftRecipeAutoStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftRecipeStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftRecipeStackRequestActionTrait.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftingConsumeInputStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftingMarkSecondaryResultStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CreativeCreateStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingNonImplementedStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingResultsStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DestroyStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DisappearStackRequestActionTrait.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DropStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequest.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequestActionType.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequestSlotInfo.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/LabTableCombineStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/PlaceStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/SwapStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeOrPlaceStackRequestActionTrait.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeStackRequestAction.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponse.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseContainerInfo.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseSlotInfo.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 35ef7cffc..1f60f3717 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -113,6 +113,7 @@ use pocketmine\network\mcpe\protocol\BlockPickRequestPacket; use pocketmine\network\mcpe\protocol\BookEditPacket; use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; +use pocketmine\network\mcpe\protocol\ContainerOpenPacket; use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; use pocketmine\network\mcpe\protocol\InteractPacket; @@ -156,6 +157,8 @@ use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton; use pocketmine\network\mcpe\protocol\types\SkinAnimation; use pocketmine\network\mcpe\protocol\types\SkinData; use pocketmine\network\mcpe\protocol\types\SkinImage; +use pocketmine\network\mcpe\protocol\types\SpawnSettings; +use pocketmine\network\mcpe\protocol\types\WindowTypes; use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; use pocketmine\network\mcpe\protocol\UpdateBlockPacket; use pocketmine\network\mcpe\VerifyLoginTask; @@ -225,6 +228,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ private const RESOURCE_PACK_CHUNK_SIZE = 128 * 1024; //128KB + //TODO: HACK! + //these IDs are used for 1.16 to restore 1.14ish crafting & inventory behaviour; since they don't seem to have any + //effect on the behaviour of inventory transactions I don't currently plan to integrate these into the main system. + private const RESERVED_WINDOW_ID_RANGE_START = ContainerIds::LAST - 10; + private const RESERVED_WINDOW_ID_RANGE_END = ContainerIds::LAST; + public const HARDCODED_CRAFTING_GRID_WINDOW_ID = self::RESERVED_WINDOW_ID_RANGE_START + 1; + public const HARDCODED_INVENTORY_WINDOW_ID = self::RESERVED_WINDOW_ID_RANGE_START + 2; + /** * Validates the given username. */ @@ -1230,11 +1241,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } $this->spawnPosition = new Position($pos->x, $pos->y, $pos->z, $level); $pk = new SetSpawnPositionPacket(); - $pk->x = $this->spawnPosition->getFloorX(); - $pk->y = $this->spawnPosition->getFloorY(); - $pk->z = $this->spawnPosition->getFloorZ(); + $pk->x = $pk->x2 = $this->spawnPosition->getFloorX(); + $pk->y = $pk->y2 = $this->spawnPosition->getFloorY(); + $pk->z = $pk->z2 = $this->spawnPosition->getFloorZ(); + $pk->dimension = DimensionIds::OVERWORLD; $pk->spawnType = SetSpawnPositionPacket::TYPE_PLAYER_SPAWN; - $pk->spawnForced = false; + $this->dataPacket($pk); } @@ -2202,7 +2214,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $pk->pitch = $this->pitch; $pk->yaw = $this->yaw; $pk->seed = -1; - $pk->dimension = DimensionIds::OVERWORLD; //TODO: implement this properly + $pk->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly $pk->worldGamemode = Player::getClientFriendlyGamemode($this->server->getGamemode()); $pk->difficulty = $this->level->getDifficulty(); $pk->spawnX = $spawnPosition->getFloorX(); @@ -2373,22 +2385,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $isFinalCraftingPart = false; foreach($packet->actions as $networkInventoryAction){ if( - $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER and - $networkInventoryAction->windowId === ContainerIds::UI and - $networkInventoryAction->inventorySlot === 50 and - !$networkInventoryAction->oldItem->equalsExact($networkInventoryAction->newItem) - ){ - $isCraftingPart = true; - if(!$networkInventoryAction->oldItem->isNull() and $networkInventoryAction->newItem->isNull()){ - $isFinalCraftingPart = true; - } - }elseif( $networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and ( $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or $networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT ) ){ $isCraftingPart = true; + if($networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT){ + $isFinalCraftingPart = true; + } } try{ $action = $networkInventoryAction->createInventoryAction($this); @@ -2789,6 +2794,20 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ case InteractPacket::ACTION_LEAVE_VEHICLE: case InteractPacket::ACTION_MOUSEOVER: break; //TODO: handle these + case InteractPacket::ACTION_OPEN_INVENTORY: + if($target === $this){ + //TODO: HACK! this restores 1.14ish behaviour, but this should be able to be listened to and + //controlled by plugins. However, the player is always a subscriber to their own inventory so it + //doesn't integrate well with the regular container system right now. + $pk = new ContainerOpenPacket(); + $pk->windowId = self::HARDCODED_INVENTORY_WINDOW_ID; + $pk->type = WindowTypes::INVENTORY; + $pk->x = $pk->y = $pk->z = 0; + $pk->entityUniqueId = $this->getId(); + $this->sendDataPacket($pk); + break; + } + return false; default: $this->server->getLogger()->debug("Unhandled/unknown interaction type " . $packet->action . " received from " . $this->getName()); @@ -3002,18 +3021,22 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } public function handleContainerClose(ContainerClosePacket $packet) : bool{ - if(!$this->spawned or $packet->windowId === 0){ + if(!$this->spawned){ return true; } $this->doCloseInventory(); + if($packet->windowId >= self::RESERVED_WINDOW_ID_RANGE_START and $packet->windowId <= self::RESERVED_WINDOW_ID_RANGE_END){ + $pk = new ContainerClosePacket(); + $pk->windowId = $packet->windowId; + $this->sendDataPacket($pk); + return true; + } if(isset($this->windowIndex[$packet->windowId])){ (new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this))->call(); $this->removeWindow($this->windowIndex[$packet->windowId]); - return true; - }elseif($packet->windowId === 255){ - //Closed a fake window + //removeWindow handles sending the appropriate return true; } @@ -3983,7 +4006,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if($forceId === null){ $cnt = $this->windowCnt; do{ - $cnt = max(ContainerIds::FIRST, ($cnt + 1) % ContainerIds::LAST); + $cnt = max(ContainerIds::FIRST, ($cnt + 1) % self::RESERVED_WINDOW_ID_RANGE_START); if($cnt === $this->windowCnt){ //wraparound, no free slots throw new \InvalidStateException("No free window IDs found"); } @@ -3991,7 +4014,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->windowCnt = $cnt; }else{ $cnt = $forceId; - if(isset($this->windowIndex[$cnt])){ + if(isset($this->windowIndex[$cnt]) or ($cnt >= self::RESERVED_WINDOW_ID_RANGE_START && $cnt <= self::RESERVED_WINDOW_ID_RANGE_END)){ throw new \InvalidArgumentException("Requested force ID $forceId already in use"); } } diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index f5583753e..d35abc935 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -36,7 +36,7 @@ use pocketmine\math\RayTraceResult; use pocketmine\math\Vector3; use pocketmine\metadata\Metadatable; use pocketmine\metadata\MetadataValue; -use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; +use pocketmine\network\mcpe\convert\RuntimeBlockMapping; use pocketmine\Player; use pocketmine\plugin\Plugin; use function array_merge; diff --git a/src/pocketmine/block/BlockFactory.php b/src/pocketmine/block/BlockFactory.php index 3bc389c8a..e8c2d232b 100644 --- a/src/pocketmine/block/BlockFactory.php +++ b/src/pocketmine/block/BlockFactory.php @@ -25,7 +25,7 @@ namespace pocketmine\block; use pocketmine\item\Item; use pocketmine\level\Position; -use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; +use pocketmine\network\mcpe\convert\RuntimeBlockMapping; use function min; /** diff --git a/src/pocketmine/block/CraftingTable.php b/src/pocketmine/block/CraftingTable.php index 05b52fd6a..bb2fe5c39 100644 --- a/src/pocketmine/block/CraftingTable.php +++ b/src/pocketmine/block/CraftingTable.php @@ -25,6 +25,8 @@ namespace pocketmine\block; use pocketmine\inventory\CraftingGrid; use pocketmine\item\Item; +use pocketmine\network\mcpe\protocol\ContainerOpenPacket; +use pocketmine\network\mcpe\protocol\types\WindowTypes; use pocketmine\Player; class CraftingTable extends Solid{ @@ -50,6 +52,16 @@ class CraftingTable extends Solid{ public function onActivate(Item $item, Player $player = null) : bool{ if($player instanceof Player){ $player->setCraftingGrid(new CraftingGrid($player, CraftingGrid::SIZE_BIG)); + + //TODO: HACK! crafting grid doesn't fit very well into the current PM container system, so this hack allows + //it to carry on working approximately the same way as it did in 1.14 + $pk = new ContainerOpenPacket(); + $pk->windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID; + $pk->type = WindowTypes::WORKBENCH; + $pk->x = $this->getFloorX(); + $pk->y = $this->getFloorY(); + $pk->z = $this->getFloorZ(); + $player->sendDataPacket($pk); } return true; diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 97ac40b4b..594eaf7bc 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -311,12 +311,15 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ public const DATA_FLAG_ROARING = 83; public const DATA_FLAG_DELAYED_ATTACKING = 84; public const DATA_FLAG_AVOIDING_MOBS = 85; - public const DATA_FLAG_FACING_TARGET_TO_RANGE_ATTACK = 86; - public const DATA_FLAG_HIDDEN_WHEN_INVISIBLE = 87; //?????????????????? - public const DATA_FLAG_IS_IN_UI = 88; - public const DATA_FLAG_STALKING = 89; - public const DATA_FLAG_EMOTING = 90; - public const DATA_FLAG_CELEBRATING = 91; + public const DATA_FLAG_AVOIDING_BLOCK = 86; + public const DATA_FLAG_FACING_TARGET_TO_RANGE_ATTACK = 87; + public const DATA_FLAG_HIDDEN_WHEN_INVISIBLE = 88; //?????????????????? + public const DATA_FLAG_IS_IN_UI = 89; + public const DATA_FLAG_STALKING = 90; + public const DATA_FLAG_EMOTING = 91; + public const DATA_FLAG_CELEBRATING = 92; + public const DATA_FLAG_ADMIRING = 93; + public const DATA_FLAG_CELEBRATING_SPECIAL = 94; public const DATA_PLAYER_FLAG_SLEEP = 1; public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK diff --git a/src/pocketmine/inventory/ArmorInventory.php b/src/pocketmine/inventory/ArmorInventory.php index b8aa855ab..9e204a157 100644 --- a/src/pocketmine/inventory/ArmorInventory.php +++ b/src/pocketmine/inventory/ArmorInventory.php @@ -28,7 +28,9 @@ use pocketmine\item\Item; use pocketmine\network\mcpe\protocol\InventoryContentPacket; use pocketmine\network\mcpe\protocol\InventorySlotPacket; use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\Player; +use function array_map; use function array_merge; class ArmorInventory extends BaseInventory{ @@ -109,7 +111,7 @@ class ArmorInventory extends BaseInventory{ $pk2 = new InventorySlotPacket(); $pk2->windowId = $player->getWindowId($this); $pk2->inventorySlot = $index; - $pk2->item = $this->getItem($index); + $pk2->item = ItemStackWrapper::legacy($this->getItem($index)); $player->dataPacket($pk2); }else{ $player->dataPacket($pk); @@ -134,7 +136,7 @@ class ArmorInventory extends BaseInventory{ if($player === $this->getHolder()){ $pk2 = new InventoryContentPacket(); $pk2->windowId = $player->getWindowId($this); - $pk2->items = $this->getContents(true); + $pk2->items = array_map([ItemStackWrapper::class, 'legacy'], $this->getContents(true)); $player->dataPacket($pk2); }else{ $player->dataPacket($pk); diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index dc6dc87e4..f1d8bb987 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -31,7 +31,9 @@ use pocketmine\math\Vector3; use pocketmine\network\mcpe\protocol\InventoryContentPacket; use pocketmine\network\mcpe\protocol\InventorySlotPacket; use pocketmine\network\mcpe\protocol\types\ContainerIds; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\Player; +use function array_map; use function array_slice; use function count; use function max; @@ -433,7 +435,7 @@ abstract class BaseInventory implements Inventory{ } $pk = new InventoryContentPacket(); - $pk->items = $this->getContents(true); + $pk->items = array_map([ItemStackWrapper::class, 'legacy'], $this->getContents(true)); foreach($target as $player){ if(($id = $player->getWindowId($this)) === ContainerIds::NONE){ @@ -455,7 +457,7 @@ abstract class BaseInventory implements Inventory{ $pk = new InventorySlotPacket(); $pk->inventorySlot = $index; - $pk->item = $this->getItem($index); + $pk->item = ItemStackWrapper::legacy($this->getItem($index)); foreach($target as $player){ if(($id = $player->getWindowId($this)) === ContainerIds::NONE){ diff --git a/src/pocketmine/inventory/PlayerInventory.php b/src/pocketmine/inventory/PlayerInventory.php index 7d7d159c6..ddf1dfe90 100644 --- a/src/pocketmine/inventory/PlayerInventory.php +++ b/src/pocketmine/inventory/PlayerInventory.php @@ -26,10 +26,12 @@ namespace pocketmine\inventory; use pocketmine\entity\Human; use pocketmine\event\player\PlayerItemHeldEvent; use pocketmine\item\Item; -use pocketmine\network\mcpe\protocol\InventoryContentPacket; +use pocketmine\network\mcpe\protocol\CreativeContentPacket; use pocketmine\network\mcpe\protocol\MobEquipmentPacket; use pocketmine\network\mcpe\protocol\types\ContainerIds; +use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry; use pocketmine\Player; +use function array_map; use function in_array; use function is_array; @@ -196,16 +198,11 @@ class PlayerInventory extends BaseInventory{ if(!($holder instanceof Player)){ throw new \LogicException("Cannot send creative inventory contents to non-player inventory holder"); } - $pk = new InventoryContentPacket(); - $pk->windowId = ContainerIds::CREATIVE; - if(!$holder->isSpectator()){ //fill it for all gamemodes except spectator - foreach(Item::getCreativeItems() as $i => $item){ - $pk->items[$i] = clone $item; - } - } - - $holder->dataPacket($pk); + $nextEntryId = 1; + $holder->sendDataPacket(CreativeContentPacket::create(array_map(function(Item $item) use (&$nextEntryId) : CreativeContentEntry{ + return new CreativeContentEntry($nextEntryId++, clone $item); + }, $holder->isSpectator() ? [] : Item::getCreativeItems()))); //fill it for all gamemodes except spectator } /** diff --git a/src/pocketmine/inventory/transaction/CraftingTransaction.php b/src/pocketmine/inventory/transaction/CraftingTransaction.php index 8787e32c1..a8b74dc4a 100644 --- a/src/pocketmine/inventory/transaction/CraftingTransaction.php +++ b/src/pocketmine/inventory/transaction/CraftingTransaction.php @@ -27,7 +27,7 @@ use pocketmine\event\inventory\CraftItemEvent; use pocketmine\inventory\CraftingRecipe; use pocketmine\item\Item; use pocketmine\network\mcpe\protocol\ContainerClosePacket; -use pocketmine\network\mcpe\protocol\types\ContainerIds; +use pocketmine\Player; use function array_pop; use function count; use function intdiv; @@ -165,7 +165,7 @@ class CraftingTransaction extends InventoryTransaction{ * transaction goes wrong. */ $pk = new ContainerClosePacket(); - $pk->windowId = ContainerIds::NONE; + $pk->windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID; $this->source->dataPacket($pk); } diff --git a/src/pocketmine/item/enchantment/Enchantment.php b/src/pocketmine/item/enchantment/Enchantment.php index 4460e595f..3775d7cfe 100644 --- a/src/pocketmine/item/enchantment/Enchantment.php +++ b/src/pocketmine/item/enchantment/Enchantment.php @@ -69,6 +69,7 @@ class Enchantment{ public const MULTISHOT = 33; public const PIERCING = 34; public const QUICK_CHARGE = 35; + public const SOUL_SPEED = 36; public const RARITY_COMMON = 10; public const RARITY_UNCOMMON = 5; diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 3284717b7..f2030141e 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -72,6 +72,7 @@ use pocketmine\metadata\Metadatable; use pocketmine\metadata\MetadataValue; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\StringTag; +use pocketmine\network\mcpe\convert\RuntimeBlockMapping; use pocketmine\network\mcpe\protocol\AddActorPacket; use pocketmine\network\mcpe\protocol\BatchPacket; use pocketmine\network\mcpe\protocol\DataPacket; @@ -79,7 +80,6 @@ use pocketmine\network\mcpe\protocol\LevelEventPacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\network\mcpe\protocol\SetDifficultyPacket; use pocketmine\network\mcpe\protocol\SetTimePacket; -use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use pocketmine\network\mcpe\protocol\UpdateBlockPacket; use pocketmine\Player; use pocketmine\plugin\Plugin; diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 585a2ae3a..1e8324212 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -471,7 +471,7 @@ class NetworkBinaryStream extends BinaryStream{ /** * Reads and returns an EntityUniqueID */ - public function getEntityUniqueId() : int{ + final public function getEntityUniqueId() : int{ return $this->getVarLong(); } @@ -485,7 +485,7 @@ class NetworkBinaryStream extends BinaryStream{ /** * Reads and returns an EntityRuntimeID */ - public function getEntityRuntimeId() : int{ + final public function getEntityRuntimeId() : int{ return $this->getUnsignedVarLong(); } @@ -648,7 +648,8 @@ class NetworkBinaryStream extends BinaryStream{ $toEntityUniqueId = $this->getEntityUniqueId(); $type = $this->getByte(); $immediate = $this->getBool(); - return new EntityLink($fromEntityUniqueId, $toEntityUniqueId, $type, $immediate); + $causedByRider = $this->getBool(); + return new EntityLink($fromEntityUniqueId, $toEntityUniqueId, $type, $immediate, $causedByRider); } protected function putEntityLink(EntityLink $link) : void{ @@ -656,6 +657,7 @@ class NetworkBinaryStream extends BinaryStream{ $this->putEntityUniqueId($link->toEntityUniqueId); $this->putByte($link->type); $this->putBool($link->immediate); + $this->putBool($link->causedByRider); } protected function getCommandOriginData() : CommandOriginData{ @@ -747,4 +749,12 @@ class NetworkBinaryStream extends BinaryStream{ $this->putStructureSettings($structureEditorData->structureSettings); $this->putVarInt($structureEditorData->structureRedstoneSaveMove); } + + public function readGenericTypeNetworkId() : int{ + return $this->getVarInt(); + } + + public function writeGenericTypeNetworkId(int $id) : void{ + $this->putVarInt($id); + } } diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index cb647212b..0ff494411 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -52,6 +52,7 @@ use pocketmine\network\mcpe\protocol\ClientCacheBlobStatusPacket; use pocketmine\network\mcpe\protocol\ClientCacheMissResponsePacket; use pocketmine\network\mcpe\protocol\ClientCacheStatusPacket; use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket; +use pocketmine\network\mcpe\protocol\CodeBuilderPacket; use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket; use pocketmine\network\mcpe\protocol\CommandOutputPacket; use pocketmine\network\mcpe\protocol\CommandRequestPacket; @@ -61,9 +62,12 @@ use pocketmine\network\mcpe\protocol\ContainerOpenPacket; use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; use pocketmine\network\mcpe\protocol\CraftingDataPacket; use pocketmine\network\mcpe\protocol\CraftingEventPacket; +use pocketmine\network\mcpe\protocol\CreativeContentPacket; use pocketmine\network\mcpe\protocol\DataPacket; +use pocketmine\network\mcpe\protocol\DebugInfoPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; use pocketmine\network\mcpe\protocol\EducationSettingsPacket; +use pocketmine\network\mcpe\protocol\EmoteListPacket; use pocketmine\network\mcpe\protocol\EmotePacket; use pocketmine\network\mcpe\protocol\EventPacket; use pocketmine\network\mcpe\protocol\GameRulesChangedPacket; @@ -74,6 +78,8 @@ use pocketmine\network\mcpe\protocol\InventoryContentPacket; use pocketmine\network\mcpe\protocol\InventorySlotPacket; use pocketmine\network\mcpe\protocol\InventoryTransactionPacket; use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket; +use pocketmine\network\mcpe\protocol\ItemStackRequestPacket; +use pocketmine\network\mcpe\protocol\ItemStackResponsePacket; use pocketmine\network\mcpe\protocol\LabTablePacket; use pocketmine\network\mcpe\protocol\LecternUpdatePacket; use pocketmine\network\mcpe\protocol\LevelChunkPacket; @@ -99,15 +105,20 @@ use pocketmine\network\mcpe\protocol\NetworkSettingsPacket; use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket; use pocketmine\network\mcpe\protocol\NpcRequestPacket; use pocketmine\network\mcpe\protocol\OnScreenTextureAnimationPacket; +use pocketmine\network\mcpe\protocol\PacketViolationWarningPacket; use pocketmine\network\mcpe\protocol\PhotoTransferPacket; use pocketmine\network\mcpe\protocol\PlayerActionPacket; +use pocketmine\network\mcpe\protocol\PlayerArmorDamagePacket; use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket; +use pocketmine\network\mcpe\protocol\PlayerEnchantOptionsPacket; use pocketmine\network\mcpe\protocol\PlayerHotbarPacket; use pocketmine\network\mcpe\protocol\PlayerInputPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\PlaySoundPacket; use pocketmine\network\mcpe\protocol\PlayStatusPacket; +use pocketmine\network\mcpe\protocol\PositionTrackingDBClientRequestPacket; +use pocketmine\network\mcpe\protocol\PositionTrackingDBServerBroadcastPacket; use pocketmine\network\mcpe\protocol\PurchaseReceiptPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\RemoveEntityPacket; @@ -163,9 +174,9 @@ use pocketmine\network\mcpe\protocol\UpdateBlockPacket; use pocketmine\network\mcpe\protocol\UpdateBlockPropertiesPacket; use pocketmine\network\mcpe\protocol\UpdateBlockSyncedPacket; use pocketmine\network\mcpe\protocol\UpdateEquipPacket; +use pocketmine\network\mcpe\protocol\UpdatePlayerGameTypePacket; use pocketmine\network\mcpe\protocol\UpdateSoftEnumPacket; use pocketmine\network\mcpe\protocol\UpdateTradePacket; -use pocketmine\network\mcpe\protocol\VideoStreamConnectPacket; abstract class NetworkSession{ @@ -666,10 +677,6 @@ abstract class NetworkSession{ return false; } - public function handleVideoStreamConnect(VideoStreamConnectPacket $packet) : bool{ - return false; - } - public function handleAddEntity(AddEntityPacket $packet) : bool{ return false; } @@ -741,4 +748,52 @@ abstract class NetworkSession{ public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{ return false; } + + public function handleCreativeContent(CreativeContentPacket $packet) : bool{ + return false; + } + + public function handlePlayerEnchantOptions(PlayerEnchantOptionsPacket $packet) : bool{ + return false; + } + + public function handleItemStackRequest(ItemStackRequestPacket $packet) : bool{ + return false; + } + + public function handleItemStackResponse(ItemStackResponsePacket $packet) : bool{ + return false; + } + + public function handlePlayerArmorDamage(PlayerArmorDamagePacket $packet) : bool{ + return false; + } + + public function handleCodeBuilder(CodeBuilderPacket $packet) : bool{ + return false; + } + + public function handleUpdatePlayerGameType(UpdatePlayerGameTypePacket $packet) : bool{ + return false; + } + + public function handleEmoteList(EmoteListPacket $packet) : bool{ + return false; + } + + public function handlePositionTrackingDBServerBroadcast(PositionTrackingDBServerBroadcastPacket $packet) : bool{ + return false; + } + + public function handlePositionTrackingDBClientRequest(PositionTrackingDBClientRequestPacket $packet) : bool{ + return false; + } + + public function handleDebugInfo(DebugInfoPacket $packet) : bool{ + return false; + } + + public function handlePacketViolationWarning(PacketViolationWarningPacket $packet) : bool{ + return false; + } } diff --git a/src/pocketmine/network/mcpe/RakLibInterface.php b/src/pocketmine/network/mcpe/RakLibInterface.php index d8a67bfdf..5f3ec921a 100644 --- a/src/pocketmine/network/mcpe/RakLibInterface.php +++ b/src/pocketmine/network/mcpe/RakLibInterface.php @@ -53,7 +53,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ * Sometimes this gets changed when the MCPE-layer protocol gets broken to the point where old and new can't * communicate. It's important that we check this to avoid catastrophes. */ - private const MCPE_RAKNET_PROTOCOL_VERSION = 9; + private const MCPE_RAKNET_PROTOCOL_VERSION = 10; /** @var Server */ private $server; diff --git a/src/pocketmine/network/mcpe/convert/R12ToCurrentBlockMapEntry.php b/src/pocketmine/network/mcpe/convert/R12ToCurrentBlockMapEntry.php new file mode 100644 index 000000000..1244d2745 --- /dev/null +++ b/src/pocketmine/network/mcpe/convert/R12ToCurrentBlockMapEntry.php @@ -0,0 +1,58 @@ +id = $id; + $this->meta = $meta; + $this->blockState = $blockState; + } + + public function getId() : string{ + return $this->id; + } + + public function getMeta() : int{ + return $this->meta; + } + + public function getBlockState() : CompoundTag{ + return $this->blockState; + } + + public function __toString(){ + return "id=$this->id, meta=$this->meta, nbt=$this->blockState"; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php similarity index 82% rename from src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php rename to src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php index e9b5dc41c..501a12d29 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php @@ -21,13 +21,14 @@ declare(strict_types=1); -namespace pocketmine\network\mcpe\protocol\types; +namespace pocketmine\network\mcpe\convert; use pocketmine\block\BlockIds; use pocketmine\nbt\NBT; use pocketmine\nbt\NetworkLittleEndianNBTStream; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\ListTag; +use pocketmine\network\mcpe\NetworkBinaryStream; use function file_get_contents; use function getmypid; use function json_decode; @@ -66,9 +67,22 @@ final class RuntimeBlockMapping{ private static function setupLegacyMappings() : void{ $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); - $legacyStateMap = (new NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.nbt")); - if(!($legacyStateMap instanceof ListTag) or $legacyStateMap->getTagType() !== NBT::TAG_Compound){ - throw new \RuntimeException("Invalid legacy states mapping table, expected TAG_List root"); + + /** @var R12ToCurrentBlockMapEntry[] $legacyStateMap */ + $legacyStateMap = []; + $legacyStateMapReader = new NetworkBinaryStream(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.bin")); + $nbtReader = new NetworkLittleEndianNBTStream(); + while(!$legacyStateMapReader->feof()){ + $id = $legacyStateMapReader->getString(); + $meta = $legacyStateMapReader->getLShort(); + + $offset = $legacyStateMapReader->getOffset(); + $state = $nbtReader->read($legacyStateMapReader->getBuffer(), false, $offset); + $legacyStateMapReader->setOffset($offset); + if(!($state instanceof CompoundTag)){ + throw new \RuntimeException("Blockstate should be a TAG_Compound"); + } + $legacyStateMap[] = new R12ToCurrentBlockMapEntry($id, $meta, $state); } /** @@ -78,16 +92,17 @@ final class RuntimeBlockMapping{ foreach(self::$bedrockKnownStates as $k => $state){ $idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k; } - /** @var CompoundTag $pair */ foreach($legacyStateMap as $pair){ - $oldState = $pair->getCompoundTag("old"); - $id = $legacyIdMap[$oldState->getString("name")]; - $data = $oldState->getShort("val"); + $id = $legacyIdMap[$pair->getId()] ?? null; + if($id === null){ + throw new \RuntimeException("No legacy ID matches " . $pair->getId()); + } + $data = $pair->getMeta(); if($data > 15){ //we can't handle metadata with more than 4 bits continue; } - $mappedState = $pair->getCompoundTag("new"); + $mappedState = $pair->getBlockState(); //TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values $mappedState->setName("block"); diff --git a/src/pocketmine/network/mcpe/protocol/ActorEventPacket.php b/src/pocketmine/network/mcpe/protocol/ActorEventPacket.php index fbb6887fa..b49e47a35 100644 --- a/src/pocketmine/network/mcpe/protocol/ActorEventPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ActorEventPacket.php @@ -87,6 +87,7 @@ class ActorEventPacket extends DataPacket{ public const TREASURE_HUNT = 72; public const AGENT_SUMMON = 73; public const CHARGED_CROSSBOW = 74; + public const FALL = 75; //TODO: add more events diff --git a/src/pocketmine/network/mcpe/protocol/BatchPacket.php b/src/pocketmine/network/mcpe/protocol/BatchPacket.php index b9e715586..4e345eb3f 100644 --- a/src/pocketmine/network/mcpe/protocol/BatchPacket.php +++ b/src/pocketmine/network/mcpe/protocol/BatchPacket.php @@ -32,6 +32,7 @@ use function get_class; use function strlen; use function zlib_decode; use function zlib_encode; +use const ZLIB_ENCODING_RAW; #ifndef COMPILE use pocketmine\utils\Binary; #endif @@ -71,7 +72,7 @@ class BatchPacket extends DataPacket{ } protected function encodePayload(){ - $this->put(zlib_encode($this->payload, ZLIB_ENCODING_DEFLATE, $this->compressionLevel)); + $this->put(zlib_encode($this->payload, ZLIB_ENCODING_RAW, $this->compressionLevel)); } /** diff --git a/src/pocketmine/network/mcpe/protocol/CodeBuilderPacket.php b/src/pocketmine/network/mcpe/protocol/CodeBuilderPacket.php new file mode 100644 index 000000000..a05b07765 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/CodeBuilderPacket.php @@ -0,0 +1,66 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class CodeBuilderPacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::CODE_BUILDER_PACKET; + + /** @var string */ + private $url; + /** @var bool */ + private $openCodeBuilder; + + public static function create(string $url, bool $openCodeBuilder) : self{ + $result = new self; + $result->url = $url; + $result->openCodeBuilder = $openCodeBuilder; + return $result; + } + + public function getUrl() : string{ + return $this->url; + } + + public function openCodeBuilder() : bool{ + return $this->openCodeBuilder; + } + + protected function decodePayload() : void{ + $this->url = $this->getString(); + $this->openCodeBuilder = $this->getBool(); + } + + protected function encodePayload() : void{ + $this->putString($this->url); + $this->putBool($this->openCodeBuilder); + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleCodeBuilder($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php index 2b3e1d98e..c035d1467 100644 --- a/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php @@ -97,6 +97,7 @@ class CraftingDataPacket extends DataPacket{ $entry["uuid"] = $this->getUUID()->toString(); $entry["block"] = $this->getString(); $entry["priority"] = $this->getVarInt(); + $entry["net_id"] = $this->readGenericTypeNetworkId(); break; case self::ENTRY_SHAPED: @@ -118,6 +119,7 @@ class CraftingDataPacket extends DataPacket{ $entry["uuid"] = $this->getUUID()->toString(); $entry["block"] = $this->getString(); $entry["priority"] = $this->getVarInt(); + $entry["net_id"] = $this->readGenericTypeNetworkId(); break; case self::ENTRY_FURNACE: @@ -140,6 +142,7 @@ class CraftingDataPacket extends DataPacket{ break; case self::ENTRY_MULTI: $entry["uuid"] = $this->getUUID()->toString(); + $entry["net_id"] = $this->readGenericTypeNetworkId(); break; default: throw new \UnexpectedValueException("Unhandled recipe type $recipeType!"); //do not continue attempting to decode @@ -148,9 +151,12 @@ class CraftingDataPacket extends DataPacket{ } for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ $input = $this->getVarInt(); + $inputMeta = $this->getVarInt(); $ingredient = $this->getVarInt(); + $ingredientMeta = $this->getVarInt(); $output = $this->getVarInt(); - $this->potionTypeRecipes[] = new PotionTypeRecipe($input, $ingredient, $output); + $outputMeta = $this->getVarInt(); + $this->potionTypeRecipes[] = new PotionTypeRecipe($input, $inputMeta, $ingredient, $ingredientMeta, $output, $outputMeta); } for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ $input = $this->getVarInt(); @@ -193,6 +199,7 @@ class CraftingDataPacket extends DataPacket{ $stream->put(str_repeat("\x00", 16)); //Null UUID $stream->putString("crafting_table"); //TODO: blocktype (no prefix) (this might require internal API breaks) $stream->putVarInt(50); //TODO: priority + $stream->writeGenericTypeNetworkId($pos); //TODO: ANOTHER recipe ID, only used on the network return CraftingDataPacket::ENTRY_SHAPELESS; } @@ -217,6 +224,7 @@ class CraftingDataPacket extends DataPacket{ $stream->put(str_repeat("\x00", 16)); //Null UUID $stream->putString("crafting_table"); //TODO: blocktype (no prefix) (this might require internal API breaks) $stream->putVarInt(50); //TODO: priority + $stream->writeGenericTypeNetworkId($pos); //TODO: ANOTHER recipe ID, only used on the network return CraftingDataPacket::ENTRY_SHAPED; } @@ -272,9 +280,12 @@ class CraftingDataPacket extends DataPacket{ } $this->putUnsignedVarInt(count($this->potionTypeRecipes)); foreach($this->potionTypeRecipes as $recipe){ - $this->putVarInt($recipe->getInputPotionType()); + $this->putVarInt($recipe->getInputItemId()); + $this->putVarInt($recipe->getInputItemMeta()); $this->putVarInt($recipe->getIngredientItemId()); - $this->putVarInt($recipe->getOutputPotionType()); + $this->putVarInt($recipe->getIngredientItemMeta()); + $this->putVarInt($recipe->getOutputItemId()); + $this->putVarInt($recipe->getOutputItemMeta()); } $this->putUnsignedVarInt(count($this->potionContainerRecipes)); foreach($this->potionContainerRecipes as $recipe){ diff --git a/src/pocketmine/network/mcpe/protocol/CreativeContentPacket.php b/src/pocketmine/network/mcpe/protocol/CreativeContentPacket.php new file mode 100644 index 000000000..0bb599f6a --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/CreativeContentPacket.php @@ -0,0 +1,67 @@ + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry; +use function count; + +class CreativeContentPacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::CREATIVE_CONTENT_PACKET; + + /** @var CreativeContentEntry[] */ + private $entries; + + /** + * @param CreativeContentEntry[] $entries + */ + public static function create(array $entries) : self{ + $result = new self; + $result->entries = $entries; + return $result; + } + + /** @return CreativeContentEntry[] */ + public function getEntries() : array{ return $this->entries; } + + protected function decodePayload() : void{ + $this->entries = []; + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->entries[] = CreativeContentEntry::read($this); + } + } + + protected function encodePayload() : void{ + $this->putUnsignedVarInt(count($this->entries)); + foreach($this->entries as $entry){ + $entry->write($this); + } + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleCreativeContent($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/DebugInfoPacket.php b/src/pocketmine/network/mcpe/protocol/DebugInfoPacket.php new file mode 100644 index 000000000..531d00f99 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/DebugInfoPacket.php @@ -0,0 +1,65 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class DebugInfoPacket extends DataPacket/* implements ClientboundPacket, ServerboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::DEBUG_INFO_PACKET; + + /** @var int */ + private $entityUniqueId; + /** @var string */ + private $data; + + public static function create(int $entityUniqueId, string $data) : self{ + $result = new self; + $result->entityUniqueId = $entityUniqueId; + $result->data = $data; + return $result; + } + + /** + * TODO: we can't call this getEntityRuntimeId() because of base class collision (crap architecture, thanks Shoghi) + */ + public function getEntityUniqueIdField() : int{ return $this->entityUniqueId; } + + public function getData() : string{ return $this->data; } + + protected function decodePayload() : void{ + $this->entityUniqueId = $this->getEntityUniqueId(); + $this->data = $this->getString(); + } + + protected function encodePayload() : void{ + $this->putEntityUniqueId($this->entityUniqueId); + $this->putString($this->data); + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleDebugInfo($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php index e8f5eea4e..3ca1bbf93 100644 --- a/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php @@ -32,12 +32,21 @@ class EducationSettingsPacket extends DataPacket{ /** @var string */ private $codeBuilderDefaultUri; + /** @var string */ + private $codeBuilderTitle; + /** @var bool */ + private $canResizeCodeBuilder; + /** @var string|null */ + private $codeBuilderOverrideUri; /** @var bool */ private $hasQuiz; - public static function create(string $codeBuilderDefaultUri, bool $hasQuiz) : self{ + public static function create(string $codeBuilderDefaultUri, string $codeBuilderTitle, bool $canResizeCodeBuilder, ?string $codeBuilderOverrideUri, bool $hasQuiz) : self{ $result = new self; $result->codeBuilderDefaultUri = $codeBuilderDefaultUri; + $result->codeBuilderTitle = $codeBuilderTitle; + $result->canResizeCodeBuilder = $canResizeCodeBuilder; + $result->codeBuilderOverrideUri = $codeBuilderOverrideUri; $result->hasQuiz = $hasQuiz; return $result; } @@ -46,17 +55,42 @@ class EducationSettingsPacket extends DataPacket{ return $this->codeBuilderDefaultUri; } + public function getCodeBuilderTitle() : string{ + return $this->codeBuilderTitle; + } + + public function canResizeCodeBuilder() : bool{ + return $this->canResizeCodeBuilder; + } + + public function getCodeBuilderOverrideUri() : ?string{ + return $this->codeBuilderOverrideUri; + } + public function getHasQuiz() : bool{ return $this->hasQuiz; } protected function decodePayload() : void{ $this->codeBuilderDefaultUri = $this->getString(); + $this->codeBuilderTitle = $this->getString(); + $this->canResizeCodeBuilder = $this->getBool(); + if($this->getBool()){ + $this->codeBuilderOverrideUri = $this->getString(); + }else{ + $this->codeBuilderOverrideUri = null; + } $this->hasQuiz = $this->getBool(); } protected function encodePayload() : void{ $this->putString($this->codeBuilderDefaultUri); + $this->putString($this->codeBuilderTitle); + $this->putBool($this->canResizeCodeBuilder); + $this->putBool($this->codeBuilderOverrideUri !== null); + if($this->codeBuilderOverrideUri !== null){ + $this->putString($this->codeBuilderOverrideUri); + } $this->putBool($this->hasQuiz); } diff --git a/src/pocketmine/network/mcpe/protocol/EmoteListPacket.php b/src/pocketmine/network/mcpe/protocol/EmoteListPacket.php new file mode 100644 index 000000000..d900012c9 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/EmoteListPacket.php @@ -0,0 +1,74 @@ + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\utils\UUID; +use function count; + +class EmoteListPacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::EMOTE_LIST_PACKET; + + /** @var int */ + private $playerEntityRuntimeId; + /** @var UUID[] */ + private $emoteIds; + + /** + * @param UUID[] $emoteIds + */ + public static function create(int $playerEntityRuntimeId, array $emoteIds) : self{ + $result = new self; + $result->playerEntityRuntimeId = $playerEntityRuntimeId; + $result->emoteIds = $emoteIds; + return $result; + } + + public function getPlayerEntityRuntimeId() : int{ return $this->playerEntityRuntimeId; } + + /** @return UUID[] */ + public function getEmoteIds() : array{ return $this->emoteIds; } + + protected function decodePayload() : void{ + $this->playerEntityRuntimeId = $this->getEntityRuntimeId(); + $this->emoteIds = []; + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->emoteIds[] = $this->getUUID(); + } + } + + protected function encodePayload() : void{ + $this->putEntityRuntimeId($this->playerEntityRuntimeId); + $this->putUnsignedVarInt(count($this->emoteIds)); + foreach($this->emoteIds as $emoteId){ + $this->putUUID($emoteId); + } + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleEmoteList($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/HurtArmorPacket.php b/src/pocketmine/network/mcpe/protocol/HurtArmorPacket.php index 9205e046e..e098cf22a 100644 --- a/src/pocketmine/network/mcpe/protocol/HurtArmorPacket.php +++ b/src/pocketmine/network/mcpe/protocol/HurtArmorPacket.php @@ -30,14 +30,18 @@ use pocketmine\network\mcpe\NetworkSession; class HurtArmorPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::HURT_ARMOR_PACKET; + /** @var int */ + public $cause; /** @var int */ public $health; protected function decodePayload(){ + $this->cause = $this->getVarInt(); $this->health = $this->getVarInt(); } protected function encodePayload(){ + $this->putVarInt($this->cause); $this->putVarInt($this->health); } diff --git a/src/pocketmine/network/mcpe/protocol/InventoryContentPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryContentPacket.php index 33c66e94f..f7142cfe3 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryContentPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryContentPacket.php @@ -25,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol; #include -use pocketmine\item\Item; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use function count; class InventoryContentPacket extends DataPacket{ @@ -34,14 +34,14 @@ class InventoryContentPacket extends DataPacket{ /** @var int */ public $windowId; - /** @var Item[] */ + /** @var ItemStackWrapper[] */ public $items = []; protected function decodePayload(){ $this->windowId = $this->getUnsignedVarInt(); $count = $this->getUnsignedVarInt(); for($i = 0; $i < $count; ++$i){ - $this->items[] = $this->getSlot(); + $this->items[] = ItemStackWrapper::read($this); } } @@ -49,7 +49,7 @@ class InventoryContentPacket extends DataPacket{ $this->putUnsignedVarInt($this->windowId); $this->putUnsignedVarInt(count($this->items)); foreach($this->items as $item){ - $this->putSlot($item); + $item->write($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/InventorySlotPacket.php b/src/pocketmine/network/mcpe/protocol/InventorySlotPacket.php index 1973e690b..905148205 100644 --- a/src/pocketmine/network/mcpe/protocol/InventorySlotPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventorySlotPacket.php @@ -25,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol; #include -use pocketmine\item\Item; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; class InventorySlotPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::INVENTORY_SLOT_PACKET; @@ -35,19 +35,19 @@ class InventorySlotPacket extends DataPacket{ public $windowId; /** @var int */ public $inventorySlot; - /** @var Item */ + /** @var ItemStackWrapper */ public $item; protected function decodePayload(){ $this->windowId = $this->getUnsignedVarInt(); $this->inventorySlot = $this->getUnsignedVarInt(); - $this->item = $this->getSlot(); + $this->item = ItemStackWrapper::read($this); } protected function encodePayload(){ $this->putUnsignedVarInt($this->windowId); $this->putUnsignedVarInt($this->inventorySlot); - $this->putSlot($this->item); + $this->item->write($this); } public function handle(NetworkSession $session) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index 277054c00..22a04037c 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php @@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol; #include use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\inventory\InventoryTransactionChangedSlotsHack; use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use function count; @@ -48,8 +49,15 @@ class InventoryTransactionPacket extends DataPacket{ 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 NetworkInventoryAction[] */ public $actions = []; @@ -58,10 +66,20 @@ class InventoryTransactionPacket extends DataPacket{ public $trData; protected function decodePayload(){ + $this->requestId = $this->readGenericTypeNetworkId(); + $this->requestChangedSlots = []; + if($this->requestId !== 0){ + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($this); + } + } + $this->transactionType = $this->getUnsignedVarInt(); + $this->hasItemStackIds = $this->getBool(); + for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ - $this->actions[] = $action = (new NetworkInventoryAction())->read($this); + $this->actions[] = $action = (new NetworkInventoryAction())->read($this, $this->hasItemStackIds); } $this->trData = new \stdClass(); @@ -101,11 +119,19 @@ class InventoryTransactionPacket extends DataPacket{ } protected function encodePayload(){ + $this->writeGenericTypeNetworkId($this->requestId); + if($this->requestId !== 0){ + $this->putUnsignedVarInt(count($this->requestChangedSlots)); + foreach($this->requestChangedSlots as $changedSlots){ + $changedSlots->write($this); + } + } + $this->putUnsignedVarInt($this->transactionType); $this->putUnsignedVarInt(count($this->actions)); foreach($this->actions as $action){ - $action->write($this); + $action->write($this, $this->hasItemStackIds); } switch($this->transactionType){ diff --git a/src/pocketmine/network/mcpe/protocol/ItemStackRequestPacket.php b/src/pocketmine/network/mcpe/protocol/ItemStackRequestPacket.php new file mode 100644 index 000000000..11b827d7c --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ItemStackRequestPacket.php @@ -0,0 +1,67 @@ + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\inventory\stackrequest\ItemStackRequest; +use function count; + +class ItemStackRequestPacket extends DataPacket/* implements ServerboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::ITEM_STACK_REQUEST_PACKET; + + /** @var ItemStackRequest[] */ + private $requests; + + /** + * @param ItemStackRequest[] $requests + */ + public static function create(array $requests) : self{ + $result = new self; + $result->requests = $requests; + return $result; + } + + /** @return ItemStackRequest[] */ + public function getRequests() : array{ return $this->requests; } + + protected function decodePayload() : void{ + $this->requests = []; + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->requests[] = ItemStackRequest::read($this); + } + } + + protected function encodePayload() : void{ + $this->putUnsignedVarInt(count($this->requests)); + foreach($this->requests as $request){ + $request->write($this); + } + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleItemStackRequest($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/ItemStackResponsePacket.php b/src/pocketmine/network/mcpe/protocol/ItemStackResponsePacket.php new file mode 100644 index 000000000..c5bfcb69a --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/ItemStackResponsePacket.php @@ -0,0 +1,67 @@ + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResponse; +use function count; + +class ItemStackResponsePacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::ITEM_STACK_RESPONSE_PACKET; + + /** @var ItemStackResponse[] */ + private $responses; + + /** + * @param ItemStackResponse[] $responses + */ + public static function create(array $responses) : self{ + $result = new self; + $result->responses = $responses; + return $result; + } + + /** @return ItemStackResponse[] */ + public function getResponses() : array{ return $this->responses; } + + protected function decodePayload() : void{ + $this->responses = []; + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->responses[] = ItemStackResponse::read($this); + } + } + + protected function encodePayload() : void{ + $this->putUnsignedVarInt(count($this->responses)); + foreach($this->responses as $response){ + $response->write($this); + } + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleItemStackResponse($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/PacketPool.php b/src/pocketmine/network/mcpe/protocol/PacketPool.php index d6383dded..f42a2bc57 100644 --- a/src/pocketmine/network/mcpe/protocol/PacketPool.php +++ b/src/pocketmine/network/mcpe/protocol/PacketPool.php @@ -159,7 +159,6 @@ class PacketPool{ static::registerPacket(new LevelSoundEventPacket()); static::registerPacket(new LevelEventGenericPacket()); static::registerPacket(new LecternUpdatePacket()); - static::registerPacket(new VideoStreamConnectPacket()); static::registerPacket(new AddEntityPacket()); static::registerPacket(new RemoveEntityPacket()); static::registerPacket(new ClientCacheStatusPacket()); @@ -178,6 +177,18 @@ class PacketPool{ static::registerPacket(new CompletedUsingItemPacket()); static::registerPacket(new NetworkSettingsPacket()); static::registerPacket(new PlayerAuthInputPacket()); + static::registerPacket(new CreativeContentPacket()); + static::registerPacket(new PlayerEnchantOptionsPacket()); + static::registerPacket(new ItemStackRequestPacket()); + static::registerPacket(new ItemStackResponsePacket()); + static::registerPacket(new PlayerArmorDamagePacket()); + static::registerPacket(new CodeBuilderPacket()); + static::registerPacket(new UpdatePlayerGameTypePacket()); + static::registerPacket(new EmoteListPacket()); + static::registerPacket(new PositionTrackingDBServerBroadcastPacket()); + static::registerPacket(new PositionTrackingDBClientRequestPacket()); + static::registerPacket(new DebugInfoPacket()); + static::registerPacket(new PacketViolationWarningPacket()); } /** diff --git a/src/pocketmine/network/mcpe/protocol/PacketViolationWarningPacket.php b/src/pocketmine/network/mcpe/protocol/PacketViolationWarningPacket.php new file mode 100644 index 000000000..51167bc0c --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/PacketViolationWarningPacket.php @@ -0,0 +1,84 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class PacketViolationWarningPacket extends DataPacket/* implements ServerboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::PACKET_VIOLATION_WARNING_PACKET; + + public const TYPE_MALFORMED = 0; + + public const SEVERITY_WARNING = 0; + public const SEVERITY_FINAL_WARNING = 1; + public const SEVERITY_TERMINATING_CONNECTION = 2; + + /** @var int */ + private $type; + /** @var int */ + private $severity; + /** @var int */ + private $packetId; + /** @var string */ + private $message; + + public static function create(int $type, int $severity, int $packetId, string $message) : self{ + $result = new self; + + $result->type = $type; + $result->severity = $severity; + $result->packetId = $packetId; + $result->message = $message; + + return $result; + } + + public function getType() : int{ return $this->type; } + + public function getSeverity() : int{ return $this->severity; } + + public function getPacketId() : int{ return $this->packetId; } + + public function getMessage() : string{ return $this->message; } + + protected function decodePayload() : void{ + $this->type = $this->getVarInt(); + $this->severity = $this->getVarInt(); + $this->packetId = $this->getVarInt(); + $this->message = $this->getString(); + } + + protected function encodePayload() : void{ + $this->putVarInt($this->type); + $this->putVarInt($this->severity); + $this->putVarInt($this->packetId); + $this->putString($this->message); + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handlePacketViolationWarning($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/PlayerActionPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerActionPacket.php index 48eae374a..12734cc4e 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerActionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerActionPacket.php @@ -50,7 +50,7 @@ class PlayerActionPacket extends DataPacket{ public const ACTION_BUILD_DENIED = 17; public const ACTION_CONTINUE_BREAK = 18; public const ACTION_CHANGE_SKIN = 19; - public const ACTION_SET_ENCHANTMENT_SEED = 20; + public const ACTION_SET_ENCHANTMENT_SEED = 20; //no longer used public const ACTION_START_SWIMMING = 21; public const ACTION_STOP_SWIMMING = 22; public const ACTION_START_SPIN_ATTACK = 23; diff --git a/src/pocketmine/network/mcpe/protocol/PlayerArmorDamagePacket.php b/src/pocketmine/network/mcpe/protocol/PlayerArmorDamagePacket.php new file mode 100644 index 000000000..4554c1666 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/PlayerArmorDamagePacket.php @@ -0,0 +1,108 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class PlayerArmorDamagePacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::PLAYER_ARMOR_DAMAGE_PACKET; + + private const FLAG_HEAD = 0; + private const FLAG_CHEST = 1; + private const FLAG_LEGS = 2; + private const FLAG_FEET = 3; + + /** @var int|null */ + private $headSlotDamage; + /** @var int|null */ + private $chestSlotDamage; + /** @var int|null */ + private $legsSlotDamage; + /** @var int|null */ + private $feetSlotDamage; + + public static function create(?int $headSlotDamage, ?int $chestSlotDamage, ?int $legsSlotDamage, ?int $feetSlotDamage) : self{ + $result = new self; + $result->headSlotDamage = $headSlotDamage; + $result->chestSlotDamage = $chestSlotDamage; + $result->legsSlotDamage = $legsSlotDamage; + $result->feetSlotDamage = $feetSlotDamage; + + return $result; + } + + public function getHeadSlotDamage() : ?int{ return $this->headSlotDamage; } + + public function getChestSlotDamage() : ?int{ return $this->chestSlotDamage; } + + public function getLegsSlotDamage() : ?int{ return $this->legsSlotDamage; } + + public function getFeetSlotDamage() : ?int{ return $this->feetSlotDamage; } + + private function maybeReadDamage(int $flags, int $flag) : ?int{ + if(($flags & (1 << $flag)) !== 0){ + return $this->getVarInt(); + } + return null; + } + + protected function decodePayload() : void{ + $flags = $this->getByte(); + + $this->headSlotDamage = $this->maybeReadDamage($flags, self::FLAG_HEAD); + $this->chestSlotDamage = $this->maybeReadDamage($flags, self::FLAG_CHEST); + $this->legsSlotDamage = $this->maybeReadDamage($flags, self::FLAG_LEGS); + $this->feetSlotDamage = $this->maybeReadDamage($flags, self::FLAG_FEET); + } + + private function composeFlag(?int $field, int $flag) : int{ + return $field !== null ? (1 << $flag) : 0; + } + + private function maybeWriteDamage(?int $field) : void{ + if($field !== null){ + $this->putVarInt($field); + } + } + + protected function encodePayload() : void{ + $this->putByte( + $this->composeFlag($this->headSlotDamage, self::FLAG_HEAD) | + $this->composeFlag($this->chestSlotDamage, self::FLAG_CHEST) | + $this->composeFlag($this->legsSlotDamage, self::FLAG_LEGS) | + $this->composeFlag($this->feetSlotDamage, self::FLAG_FEET) + ); + + $this->maybeWriteDamage($this->headSlotDamage); + $this->maybeWriteDamage($this->chestSlotDamage); + $this->maybeWriteDamage($this->legsSlotDamage); + $this->maybeWriteDamage($this->feetSlotDamage); + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handlePlayerArmorDamage($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/PlayerEnchantOptionsPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerEnchantOptionsPacket.php new file mode 100644 index 000000000..c31f7bb5b --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/PlayerEnchantOptionsPacket.php @@ -0,0 +1,69 @@ + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\EnchantOption; +use function count; + +class PlayerEnchantOptionsPacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::PLAYER_ENCHANT_OPTIONS_PACKET; + + /** @var EnchantOption[] */ + private $options; + + /** + * @param EnchantOption[] $options + */ + public static function create(array $options) : self{ + $result = new self; + $result->options = $options; + return $result; + } + + /** + * @return EnchantOption[] + */ + public function getOptions() : array{ return $this->options; } + + protected function decodePayload() : void{ + $this->options = []; + for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){ + $this->options[] = EnchantOption::read($this); + } + } + + protected function encodePayload() : void{ + $this->putUnsignedVarInt(count($this->options)); + foreach($this->options as $option){ + $option->write($this); + } + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handlePlayerEnchantOptions($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/VideoStreamConnectPacket.php b/src/pocketmine/network/mcpe/protocol/PositionTrackingDBClientRequestPacket.php similarity index 56% rename from src/pocketmine/network/mcpe/protocol/VideoStreamConnectPacket.php rename to src/pocketmine/network/mcpe/protocol/PositionTrackingDBClientRequestPacket.php index aed7d1c54..2a83e0eb1 100644 --- a/src/pocketmine/network/mcpe/protocol/VideoStreamConnectPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PositionTrackingDBClientRequestPacket.php @@ -27,40 +27,38 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class VideoStreamConnectPacket extends DataPacket/* implements ClientboundPacket*/{ - public const NETWORK_ID = ProtocolInfo::VIDEO_STREAM_CONNECT_PACKET; +class PositionTrackingDBClientRequestPacket extends DataPacket/* implements ServerboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::POSITION_TRACKING_D_B_CLIENT_REQUEST_PACKET; - public const ACTION_CONNECT = 0; - public const ACTION_DISCONNECT = 1; + public const ACTION_QUERY = 0; - /** @var string */ - public $serverUri; - /** @var float */ - public $frameSendFrequency; /** @var int */ - public $action; + private $action; /** @var int */ - public $resolutionX; - /** @var int */ - public $resolutionY; + private $trackingId; + + public static function create(int $action, int $trackingId) : self{ + $result = new self; + $result->action = $action; + $result->trackingId = $trackingId; + return $result; + } + + public function getAction() : int{ return $this->action; } + + public function getTrackingId() : int{ return $this->trackingId; } protected function decodePayload() : void{ - $this->serverUri = $this->getString(); - $this->frameSendFrequency = $this->getLFloat(); $this->action = $this->getByte(); - $this->resolutionX = $this->getLInt(); - $this->resolutionY = $this->getLInt(); + $this->trackingId = $this->getVarInt(); } protected function encodePayload() : void{ - $this->putString($this->serverUri); - $this->putLFloat($this->frameSendFrequency); $this->putByte($this->action); - $this->putLInt($this->resolutionX); - $this->putLInt($this->resolutionY); + $this->putVarInt($this->trackingId); } - public function handle(NetworkSession $session) : bool{ - return $session->handleVideoStreamConnect($this); + public function handle(NetworkSession $handler) : bool{ + return $handler->handlePositionTrackingDBClientRequest($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/PositionTrackingDBServerBroadcastPacket.php b/src/pocketmine/network/mcpe/protocol/PositionTrackingDBServerBroadcastPacket.php new file mode 100644 index 000000000..2131a8001 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/PositionTrackingDBServerBroadcastPacket.php @@ -0,0 +1,81 @@ + + +use pocketmine\nbt\NetworkLittleEndianNBTStream; +use pocketmine\nbt\tag\CompoundTag; +use pocketmine\network\mcpe\NetworkSession; + +class PositionTrackingDBServerBroadcastPacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::POSITION_TRACKING_D_B_SERVER_BROADCAST_PACKET; + + public const ACTION_UPDATE = 0; + public const ACTION_DESTROY = 1; + public const ACTION_NOT_FOUND = 2; + + /** @var int */ + private $action; + /** @var int */ + private $trackingId; + /** @var CompoundTag */ + private $nbt; + + public static function create(int $action, int $trackingId, CompoundTag $nbt) : self{ + $result = new self; + $result->action = $action; + $result->trackingId = $trackingId; + $result->nbt = $nbt; + return $result; + } + + public function getAction() : int{ return $this->action; } + + public function getTrackingId() : int{ return $this->trackingId; } + + public function getNbt() : CompoundTag{ return $this->nbt; } + + protected function decodePayload() : void{ + $this->action = $this->getByte(); + $this->trackingId = $this->getVarInt(); + $offset = $this->getOffset(); + $nbt = (new NetworkLittleEndianNBTStream())->read($this->getBuffer(), false, $offset); + $this->setOffset($offset); + if(!($nbt instanceof CompoundTag)){ + throw new \UnexpectedValueException("Expected TAG_Compound"); + } + $this->nbt = $nbt; + } + + protected function encodePayload() : void{ + $this->putByte($this->action); + $this->putVarInt($this->trackingId); + $this->put((new NetworkLittleEndianNBTStream())->write($this->nbt)); + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handlePositionTrackingDBServerBroadcast($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php index a020149b8..983f23a6b 100644 --- a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php +++ b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php @@ -37,11 +37,11 @@ interface ProtocolInfo{ */ /** Actual Minecraft: PE protocol version */ - public const CURRENT_PROTOCOL = 390; + public const CURRENT_PROTOCOL = 407; /** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */ - public const MINECRAFT_VERSION = 'v1.14.60'; + public const MINECRAFT_VERSION = 'v1.16.0.67 beta'; /** Version number sent to clients in ping responses. */ - public const MINECRAFT_VERSION_NETWORK = '1.14.60'; + public const MINECRAFT_VERSION_NETWORK = '1.16.0.67'; public const LOGIN_PACKET = 0x01; public const PLAY_STATUS_PACKET = 0x02; @@ -168,7 +168,7 @@ interface ProtocolInfo{ public const LEVEL_SOUND_EVENT_PACKET = 0x7b; public const LEVEL_EVENT_GENERIC_PACKET = 0x7c; public const LECTERN_UPDATE_PACKET = 0x7d; - public const VIDEO_STREAM_CONNECT_PACKET = 0x7e; + public const ADD_ENTITY_PACKET = 0x7f; public const REMOVE_ENTITY_PACKET = 0x80; public const CLIENT_CACHE_STATUS_PACKET = 0x81; @@ -187,5 +187,17 @@ interface ProtocolInfo{ public const COMPLETED_USING_ITEM_PACKET = 0x8e; public const NETWORK_SETTINGS_PACKET = 0x8f; public const PLAYER_AUTH_INPUT_PACKET = 0x90; + public const CREATIVE_CONTENT_PACKET = 0x91; + public const PLAYER_ENCHANT_OPTIONS_PACKET = 0x92; + public const ITEM_STACK_REQUEST_PACKET = 0x93; + public const ITEM_STACK_RESPONSE_PACKET = 0x94; + public const PLAYER_ARMOR_DAMAGE_PACKET = 0x95; + public const CODE_BUILDER_PACKET = 0x96; + public const UPDATE_PLAYER_GAME_TYPE_PACKET = 0x97; + public const EMOTE_LIST_PACKET = 0x98; + public const POSITION_TRACKING_D_B_SERVER_BROADCAST_PACKET = 0x99; + public const POSITION_TRACKING_D_B_CLIENT_REQUEST_PACKET = 0x9a; + public const DEBUG_INFO_PACKET = 0x9b; + public const PACKET_VIOLATION_WARNING_PACKET = 0x9c; } diff --git a/src/pocketmine/network/mcpe/protocol/SetSpawnPositionPacket.php b/src/pocketmine/network/mcpe/protocol/SetSpawnPositionPacket.php index 9919f20a2..d45ca3553 100644 --- a/src/pocketmine/network/mcpe/protocol/SetSpawnPositionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SetSpawnPositionPacket.php @@ -41,19 +41,27 @@ class SetSpawnPositionPacket extends DataPacket{ public $y; /** @var int */ public $z; - /** @var bool */ - public $spawnForced; + /** @var int */ + public $dimension; + /** @var int */ + public $x2; + /** @var int */ + public $y2; + /** @var int */ + public $z2; protected function decodePayload(){ $this->spawnType = $this->getVarInt(); $this->getBlockPosition($this->x, $this->y, $this->z); - $this->spawnForced = $this->getBool(); + $this->dimension = $this->getVarInt(); + $this->getBlockPosition($this->x2, $this->y2, $this->z2); } protected function encodePayload(){ $this->putVarInt($this->spawnType); $this->putBlockPosition($this->x, $this->y, $this->z); - $this->putBool($this->spawnForced); + $this->putVarInt($this->dimension); + $this->putBlockPosition($this->x2, $this->y2, $this->z2); } public function handle(NetworkSession $session) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php index 33c900083..c457d77e0 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\math\Vector3; use pocketmine\nbt\NetworkLittleEndianNBTStream; use pocketmine\nbt\tag\ListTag; +use pocketmine\network\mcpe\convert\RuntimeBlockMapping; use pocketmine\network\mcpe\NetworkBinaryStream; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\EducationEditionOffer; @@ -35,7 +36,7 @@ use pocketmine\network\mcpe\protocol\types\GameRuleType; use pocketmine\network\mcpe\protocol\types\GeneratorType; use pocketmine\network\mcpe\protocol\types\MultiplayerGameVisibility; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; -use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; +use pocketmine\network\mcpe\protocol\types\SpawnSettings; use function count; use function file_get_contents; use function json_decode; @@ -66,8 +67,8 @@ class StartGamePacket extends DataPacket{ /** @var int */ public $seed; - /** @var int */ - public $dimension; + /** @var SpawnSettings */ + public $spawnSettings; /** @var int */ public $generator = GeneratorType::OVERWORLD; /** @var int */ @@ -88,6 +89,8 @@ class StartGamePacket extends DataPacket{ public $eduEditionOffer = EducationEditionOffer::NONE; /** @var bool */ public $hasEduFeaturesEnabled = false; + /** @var string */ + public $eduProductUUID = ""; /** @var float */ public $rainLevel; /** @var float */ @@ -137,9 +140,17 @@ class StartGamePacket extends DataPacket{ public $isWorldTemplateOptionLocked = false; /** @var bool */ public $onlySpawnV1Villagers = false; - /** @var string */ public $vanillaVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK; + /** @var int */ + public $limitedWorldWidth = 0; + /** @var int */ + public $limitedWorldLength = 0; + /** @var bool */ + public $isNewNether = true; + /** @var bool|null */ + public $experimentalGameplayOverride = null; + /** @var string */ public $levelId = ""; //base64 string, usually the same as world folder name in vanilla /** @var string */ @@ -164,6 +175,8 @@ class StartGamePacket extends DataPacket{ * @phpstan-var array|null */ public $itemTable = null; + /** @var bool */ + public $enableNewInventorySystem = false; //TODO protected function decodePayload(){ $this->entityUniqueId = $this->getEntityUniqueId(); @@ -177,7 +190,7 @@ class StartGamePacket extends DataPacket{ //Level settings $this->seed = $this->getVarInt(); - $this->dimension = $this->getVarInt(); + $this->spawnSettings = SpawnSettings::read($this); $this->generator = $this->getVarInt(); $this->worldGamemode = $this->getVarInt(); $this->difficulty = $this->getVarInt(); @@ -186,6 +199,7 @@ class StartGamePacket extends DataPacket{ $this->time = $this->getVarInt(); $this->eduEditionOffer = $this->getVarInt(); $this->hasEduFeaturesEnabled = $this->getBool(); + $this->eduProductUUID = $this->getString(); $this->rainLevel = $this->getLFloat(); $this->lightningLevel = $this->getLFloat(); $this->hasConfirmedPlatformLockedContent = $this->getBool(); @@ -207,8 +221,16 @@ class StartGamePacket extends DataPacket{ $this->isFromWorldTemplate = $this->getBool(); $this->isWorldTemplateOptionLocked = $this->getBool(); $this->onlySpawnV1Villagers = $this->getBool(); - $this->vanillaVersion = $this->getString(); + $this->limitedWorldWidth = $this->getLInt(); + $this->limitedWorldLength = $this->getLInt(); + $this->isNewNether = $this->getBool(); + if($this->getBool()){ + $this->experimentalGameplayOverride = $this->getBool(); + }else{ + $this->experimentalGameplayOverride = null; + } + $this->levelId = $this->getString(); $this->worldName = $this->getString(); $this->premiumWorldTemplateId = $this->getString(); @@ -233,6 +255,7 @@ class StartGamePacket extends DataPacket{ } $this->multiplayerCorrelationId = $this->getString(); + $this->enableNewInventorySystem = $this->getBool(); } protected function encodePayload(){ @@ -247,7 +270,7 @@ class StartGamePacket extends DataPacket{ //Level settings $this->putVarInt($this->seed); - $this->putVarInt($this->dimension); + $this->spawnSettings->write($this); $this->putVarInt($this->generator); $this->putVarInt($this->worldGamemode); $this->putVarInt($this->difficulty); @@ -256,6 +279,7 @@ class StartGamePacket extends DataPacket{ $this->putVarInt($this->time); $this->putVarInt($this->eduEditionOffer); $this->putBool($this->hasEduFeaturesEnabled); + $this->putString($this->eduProductUUID); $this->putLFloat($this->rainLevel); $this->putLFloat($this->lightningLevel); $this->putBool($this->hasConfirmedPlatformLockedContent); @@ -277,8 +301,15 @@ class StartGamePacket extends DataPacket{ $this->putBool($this->isFromWorldTemplate); $this->putBool($this->isWorldTemplateOptionLocked); $this->putBool($this->onlySpawnV1Villagers); - $this->putString($this->vanillaVersion); + $this->putLInt($this->limitedWorldWidth); + $this->putLInt($this->limitedWorldLength); + $this->putBool($this->isNewNether); + $this->putBool($this->experimentalGameplayOverride !== null); + if($this->experimentalGameplayOverride !== null){ + $this->putBool($this->experimentalGameplayOverride); + } + $this->putString($this->levelId); $this->putString($this->worldName); $this->putString($this->premiumWorldTemplateId); @@ -307,6 +338,7 @@ class StartGamePacket extends DataPacket{ } $this->putString($this->multiplayerCorrelationId); + $this->putBool($this->enableNewInventorySystem); } /** diff --git a/src/pocketmine/network/mcpe/protocol/TextPacket.php b/src/pocketmine/network/mcpe/protocol/TextPacket.php index 799e8afc2..6b35b1f09 100644 --- a/src/pocketmine/network/mcpe/protocol/TextPacket.php +++ b/src/pocketmine/network/mcpe/protocol/TextPacket.php @@ -40,7 +40,8 @@ class TextPacket extends DataPacket{ public const TYPE_SYSTEM = 6; public const TYPE_WHISPER = 7; public const TYPE_ANNOUNCEMENT = 8; - public const TYPE_JSON = 9; + public const TYPE_JSON_WHISPER = 9; + public const TYPE_JSON = 10; /** @var int */ public $type; @@ -69,6 +70,7 @@ class TextPacket extends DataPacket{ case self::TYPE_RAW: case self::TYPE_TIP: case self::TYPE_SYSTEM: + case self::TYPE_JSON_WHISPER: case self::TYPE_JSON: $this->message = $this->getString(); break; @@ -100,6 +102,7 @@ class TextPacket extends DataPacket{ case self::TYPE_RAW: case self::TYPE_TIP: case self::TYPE_SYSTEM: + case self::TYPE_JSON_WHISPER: case self::TYPE_JSON: $this->putString($this->message); break; diff --git a/src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php b/src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php new file mode 100644 index 000000000..d5070f620 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php @@ -0,0 +1,67 @@ + + +use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\GameMode; + +class UpdatePlayerGameTypePacket extends DataPacket/* implements ClientboundPacket*/{ + public const NETWORK_ID = ProtocolInfo::UPDATE_PLAYER_GAME_TYPE_PACKET; + + /** + * @var int + * @see GameMode + */ + private $gameMode; + + /** @var int */ + private $playerEntityUniqueId; + + public static function create(int $gameMode, int $playerEntityUniqueId) : self{ + $result = new self; + $result->gameMode = $gameMode; + $result->playerEntityUniqueId = $playerEntityUniqueId; + return $result; + } + + public function getGameMode() : int{ return $this->gameMode; } + + public function getPlayerEntityUniqueId() : int{ return $this->playerEntityUniqueId; } + + protected function decodePayload() : void{ + $this->gameMode = $this->getVarInt(); + $this->playerEntityUniqueId = $this->getEntityUniqueId(); + } + + protected function encodePayload() : void{ + $this->putVarInt($this->gameMode); + $this->putEntityUniqueId($this->playerEntityUniqueId); + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleUpdatePlayerGameType($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php b/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php index 0c9769f9f..23c82c5e7 100644 --- a/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php +++ b/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php @@ -31,7 +31,7 @@ interface ContainerIds{ public const LAST = 100; public const OFFHAND = 119; public const ARMOR = 120; - public const CREATIVE = 121; + public const HOTBAR = 122; public const FIXED_INVENTORY = 123; public const UI = 124; diff --git a/src/pocketmine/network/mcpe/protocol/types/Enchant.php b/src/pocketmine/network/mcpe/protocol/types/Enchant.php new file mode 100644 index 000000000..e9e2f1dcc --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/Enchant.php @@ -0,0 +1,53 @@ +id = $id; + $this->level = $level; + } + + public function getId() : int{ return $this->id; } + + public function getLevel() : int{ return $this->level; } + + public static function read(NetworkBinaryStream $in) : self{ + $id = $in->getByte(); + $level = $in->getByte(); + return new self($id, $level); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->id); + $out->putByte($this->level); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/EnchantOption.php b/src/pocketmine/network/mcpe/protocol/types/EnchantOption.php new file mode 100644 index 000000000..c066b78e2 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/EnchantOption.php @@ -0,0 +1,126 @@ +cost = $cost; + $this->slotFlags = $slotFlags; + $this->equipActivatedEnchantments = $equipActivatedEnchantments; + $this->heldActivatedEnchantments = $heldActivatedEnchantments; + $this->selfActivatedEnchantments = $selfActivatedEnchantments; + $this->name = $name; + $this->optionId = $optionId; + } + + public function getCost() : int{ return $this->cost; } + + public function getSlotFlags() : int{ return $this->slotFlags; } + + /** @return Enchant[] */ + public function getEquipActivatedEnchantments() : array{ return $this->equipActivatedEnchantments; } + + /** @return Enchant[] */ + public function getHeldActivatedEnchantments() : array{ return $this->heldActivatedEnchantments; } + + /** @return Enchant[] */ + public function getSelfActivatedEnchantments() : array{ return $this->selfActivatedEnchantments; } + + public function getName() : string{ return $this->name; } + + public function getOptionId() : int{ return $this->optionId; } + + /** + * @return Enchant[] + */ + private static function readEnchantList(NetworkBinaryStream $in) : array{ + $result = []; + for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ + $result[] = Enchant::read($in); + } + return $result; + } + + /** + * @param Enchant[] $list + */ + private static function writeEnchantList(NetworkBinaryStream $out, array $list) : void{ + $out->putUnsignedVarInt(count($list)); + foreach($list as $item){ + $item->write($out); + } + } + + public static function read(NetworkBinaryStream $in) : self{ + $cost = $in->getUnsignedVarInt(); + + $slotFlags = $in->getLInt(); + $equipActivatedEnchants = self::readEnchantList($in); + $heldActivatedEnchants = self::readEnchantList($in); + $selfActivatedEnchants = self::readEnchantList($in); + + $name = $in->getString(); + $optionId = $in->readGenericTypeNetworkId(); + + return new self($cost, $slotFlags, $equipActivatedEnchants, $heldActivatedEnchants, $selfActivatedEnchants, $name, $optionId); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putUnsignedVarInt($this->cost); + + $out->putLInt($this->slotFlags); + self::writeEnchantList($out, $this->equipActivatedEnchantments); + self::writeEnchantList($out, $this->heldActivatedEnchantments); + self::writeEnchantList($out, $this->selfActivatedEnchantments); + + $out->putString($this->name); + $out->writeGenericTypeNetworkId($this->optionId); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/EntityLink.php b/src/pocketmine/network/mcpe/protocol/types/EntityLink.php index 97bd8c50f..bbd5a478f 100644 --- a/src/pocketmine/network/mcpe/protocol/types/EntityLink.php +++ b/src/pocketmine/network/mcpe/protocol/types/EntityLink.php @@ -37,11 +37,14 @@ class EntityLink{ public $type; /** @var bool */ public $immediate; //for dismounting on mount death + /** @var bool */ + public $causedByRider; - public function __construct(int $fromEntityUniqueId, int $toEntityUniqueId, int $type, bool $immediate){ + public function __construct(int $fromEntityUniqueId, int $toEntityUniqueId, int $type, bool $immediate, bool $causedByRider){ $this->fromEntityUniqueId = $fromEntityUniqueId; $this->toEntityUniqueId = $toEntityUniqueId; $this->type = $type; $this->immediate = $immediate; + $this->causedByRider = $causedByRider; } } diff --git a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php index 50cc70916..e29e2fd2a 100644 --- a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php +++ b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php @@ -81,11 +81,13 @@ class NetworkInventoryAction{ public $oldItem; /** @var Item */ public $newItem; + /** @var int|null */ + public $newItemStackId = null; /** * @return $this */ - public function read(NetworkBinaryStream $packet){ + public function read(NetworkBinaryStream $packet, bool $hasItemStackIds){ $this->sourceType = $packet->getUnsignedVarInt(); switch($this->sourceType){ @@ -107,6 +109,9 @@ class NetworkInventoryAction{ $this->inventorySlot = $packet->getUnsignedVarInt(); $this->oldItem = $packet->getSlot(); $this->newItem = $packet->getSlot(); + if($hasItemStackIds){ + $this->newItemStackId = $packet->readGenericTypeNetworkId(); + } return $this; } @@ -114,7 +119,7 @@ class NetworkInventoryAction{ /** * @return void */ - public function write(NetworkBinaryStream $packet){ + public function write(NetworkBinaryStream $packet, bool $hasItemStackIds){ $packet->putUnsignedVarInt($this->sourceType); switch($this->sourceType){ @@ -136,6 +141,12 @@ 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); + } } /** diff --git a/src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php b/src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php index 642e156a9..6ad42cc6f 100644 --- a/src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php +++ b/src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php @@ -25,27 +25,48 @@ namespace pocketmine\network\mcpe\protocol\types; class PotionTypeRecipe{ /** @var int */ - private $inputPotionType; + private $inputItemId; + /** @var int */ + private $inputItemMeta; /** @var int */ private $ingredientItemId; /** @var int */ - private $outputPotionType; + private $ingredientItemMeta; + /** @var int */ + private $outputItemId; + /** @var int */ + private $outputItemMeta; - public function __construct(int $inputPotionType, int $ingredientItemId, int $outputPotionType){ - $this->inputPotionType = $inputPotionType; + public function __construct(int $inputItemId, int $inputItemMeta, int $ingredientItemId, int $ingredientItemMeta, int $outputItemId, int $outputItemMeta){ + $this->inputItemId = $inputItemId; + $this->inputItemMeta = $inputItemMeta; $this->ingredientItemId = $ingredientItemId; - $this->outputPotionType = $outputPotionType; + $this->ingredientItemMeta = $ingredientItemMeta; + $this->outputItemId = $outputItemId; + $this->outputItemMeta = $outputItemMeta; } - public function getInputPotionType() : int{ - return $this->inputPotionType; + public function getInputItemId() : int{ + return $this->inputItemId; + } + + public function getInputItemMeta() : int{ + return $this->inputItemMeta; } public function getIngredientItemId() : int{ return $this->ingredientItemId; } - public function getOutputPotionType() : int{ - return $this->outputPotionType; + public function getIngredientItemMeta() : int{ + return $this->ingredientItemMeta; + } + + public function getOutputItemId() : int{ + return $this->outputItemId; + } + + public function getOutputItemMeta() : int{ + return $this->outputItemMeta; } } diff --git a/src/pocketmine/network/mcpe/protocol/types/SpawnSettings.php b/src/pocketmine/network/mcpe/protocol/types/SpawnSettings.php new file mode 100644 index 000000000..f592d4053 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/SpawnSettings.php @@ -0,0 +1,73 @@ +biomeType = $biomeType; + $this->biomeName = $biomeName; + $this->dimension = $dimension; + } + + public function getBiomeType() : int{ + return $this->biomeType; + } + + public function getBiomeName() : string{ + return $this->biomeName; + } + + /** + * @see DimensionIds + */ + public function getDimension() : int{ + return $this->dimension; + } + + public static function read(NetworkBinaryStream $in) : self{ + $biomeType = $in->getLShort(); + $biomeName = $in->getString(); + $dimension = $in->getVarInt(); + + return new self($biomeType, $biomeName, $dimension); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putLShort($this->biomeType); + $out->putString($this->biomeName); + $out->putVarInt($this->dimension); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php index 6394e76ea..583a3384c 100644 --- a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php +++ b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php @@ -59,5 +59,8 @@ interface WindowTypes{ public const SMOKER = 28; public const STONECUTTER = 29; public const CARTOGRAPHY = 30; + public const HUD = 31; + public const JIGSAW_EDITOR = 32; + public const SMITHING_TABLE = 33; } diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/CreativeContentEntry.php b/src/pocketmine/network/mcpe/protocol/types/inventory/CreativeContentEntry.php new file mode 100644 index 000000000..ce66c6786 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/CreativeContentEntry.php @@ -0,0 +1,55 @@ +entryId = $entryId; + $this->item = $item; + } + + public function getEntryId() : int{ return $this->entryId; } + + public function getItem() : Item{ return $this->item; } + + public static function read(NetworkBinaryStream $in) : self{ + $entryId = $in->readGenericTypeNetworkId(); + $item = $in->getSlot(); + return new self($entryId, $item); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->writeGenericTypeNetworkId($this->entryId); + $out->putSlot($this->item); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/InventoryTransactionChangedSlotsHack.php b/src/pocketmine/network/mcpe/protocol/types/inventory/InventoryTransactionChangedSlotsHack.php new file mode 100644 index 000000000..b69c44fde --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/InventoryTransactionChangedSlotsHack.php @@ -0,0 +1,65 @@ +containerId = $containerId; + $this->changedSlotIndexes = $changedSlotIndexes; + } + + public function getContainerId() : int{ return $this->containerId; } + + /** @return int[] */ + public function getChangedSlotIndexes() : array{ return $this->changedSlotIndexes; } + + public static function read(NetworkBinaryStream $in) : self{ + $containerId = $in->getByte(); + $changedSlots = []; + for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ + $changedSlots[] = $in->getByte(); + } + return new self($containerId, $changedSlots); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->containerId); + $out->putUnsignedVarInt(count($this->changedSlotIndexes)); + foreach($this->changedSlotIndexes as $index){ + $out->putByte($index); + } + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/ItemStackWrapper.php b/src/pocketmine/network/mcpe/protocol/types/inventory/ItemStackWrapper.php new file mode 100644 index 000000000..44a552f39 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/ItemStackWrapper.php @@ -0,0 +1,59 @@ +stackId = $stackId; + $this->itemStack = $itemStack; + } + + public static function legacy(Item $itemStack) : self{ + return new self($itemStack->isNull() ? 0 : 1, $itemStack); + } + + public function getStackId() : int{ return $this->stackId; } + + public function getItemStack() : Item{ return $this->itemStack; } + + public static function read(NetworkBinaryStream $in) : self{ + $stackId = $in->readGenericTypeNetworkId(); + $stack = $in->getSlot(); + return new self($stackId, $stack); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->writeGenericTypeNetworkId($this->stackId); + $out->putSlot($this->itemStack); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/BeaconPaymentStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/BeaconPaymentStackRequestAction.php new file mode 100644 index 000000000..c698073bd --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/BeaconPaymentStackRequestAction.php @@ -0,0 +1,59 @@ +primaryEffectId = $primaryEffectId; + $this->secondaryEffectId = $secondaryEffectId; + } + + public function getPrimaryEffectId() : int{ return $this->primaryEffectId; } + + public function getSecondaryEffectId() : int{ return $this->secondaryEffectId; } + + public static function getTypeId() : int{ return ItemStackRequestActionType::BEACON_PAYMENT; } + + public static function read(NetworkBinaryStream $in) : self{ + $primary = $in->getVarInt(); + $secondary = $in->getVarInt(); + return new self($primary, $secondary); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putVarInt($this->primaryEffectId); + $out->putVarInt($this->secondaryEffectId); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftRecipeAutoStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftRecipeAutoStackRequestAction.php new file mode 100644 index 000000000..d5f10ee1f --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftRecipeAutoStackRequestAction.php @@ -0,0 +1,34 @@ +recipeId = $recipeId; + } + + public function getRecipeId() : int{ return $this->recipeId; } + + public static function read(NetworkBinaryStream $in) : self{ + $recipeId = $in->readGenericTypeNetworkId(); + return new self($recipeId); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->writeGenericTypeNetworkId($this->recipeId); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftingConsumeInputStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftingConsumeInputStackRequestAction.php new file mode 100644 index 000000000..26f85f7a7 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CraftingConsumeInputStackRequestAction.php @@ -0,0 +1,33 @@ +craftingGridSlot = $craftingGridSlot; + } + + public function getCraftingGridSlot() : int{ return $this->craftingGridSlot; } + + public static function getTypeId() : int{ return ItemStackRequestActionType::CRAFTING_MARK_SECONDARY_RESULT_SLOT; } + + public static function read(NetworkBinaryStream $in) : self{ + $slot = $in->getByte(); + return new self($slot); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->craftingGridSlot); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CreativeCreateStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CreativeCreateStackRequestAction.php new file mode 100644 index 000000000..64b4aa7df --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/CreativeCreateStackRequestAction.php @@ -0,0 +1,52 @@ +creativeItemId = $creativeItemId; + } + + public function getCreativeItemId() : int{ return $this->creativeItemId; } + + public static function getTypeId() : int{ return ItemStackRequestActionType::CREATIVE_CREATE; } + + public static function read(NetworkBinaryStream $in) : self{ + $creativeItemId = $in->readGenericTypeNetworkId(); + return new self($creativeItemId); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->writeGenericTypeNetworkId($this->creativeItemId); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingNonImplementedStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingNonImplementedStackRequestAction.php new file mode 100644 index 000000000..031b410d8 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DeprecatedCraftingNonImplementedStackRequestAction.php @@ -0,0 +1,45 @@ +results = $results; + $this->iterations = $iterations; + } + + /** @return Item[] */ + public function getResults() : array{ return $this->results; } + + public function getIterations() : int{ return $this->iterations; } + + public static function getTypeId() : int{ + return ItemStackRequestActionType::CRAFTING_RESULTS_DEPRECATED_ASK_TY_LAING; + } + + public static function read(NetworkBinaryStream $in) : self{ + $results = []; + for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ + $results[] = $in->getSlot(); + } + $iterations = $in->getByte(); + return new self($results, $iterations); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putUnsignedVarInt(count($this->results)); + foreach($this->results as $result){ + $out->putSlot($result); + } + $out->putByte($this->iterations); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DestroyStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DestroyStackRequestAction.php new file mode 100644 index 000000000..36f55e9a4 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DestroyStackRequestAction.php @@ -0,0 +1,34 @@ +count = $count; + $this->source = $source; + } + + final public function getCount() : int{ return $this->count; } + + final public function getSource() : ItemStackRequestSlotInfo{ return $this->source; } + + public static function read(NetworkBinaryStream $in) : self{ + $count = $in->getByte(); + $source = ItemStackRequestSlotInfo::read($in); + return new self($count, $source); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->count); + $this->source->write($out); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DropStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DropStackRequestAction.php new file mode 100644 index 000000000..329b4ec98 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/DropStackRequestAction.php @@ -0,0 +1,66 @@ +count = $count; + $this->source = $source; + $this->randomly = $randomly; + } + + public function getCount() : int{ return $this->count; } + + public function getSource() : ItemStackRequestSlotInfo{ return $this->source; } + + public function isRandomly() : bool{ return $this->randomly; } + + public static function getTypeId() : int{ return ItemStackRequestActionType::DROP; } + + public static function read(NetworkBinaryStream $in) : self{ + $count = $in->getByte(); + $source = ItemStackRequestSlotInfo::read($in); + $random = $in->getBool(); + return new self($count, $source, $random); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->count); + $this->source->write($out); + $out->putBool($this->randomly); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequest.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequest.php new file mode 100644 index 000000000..648eb295a --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequest.php @@ -0,0 +1,87 @@ +requestId = $requestId; + $this->actions = $actions; + } + + public function getRequestId() : int{ return $this->requestId; } + + /** @return ItemStackRequestAction[] */ + public function getActions() : array{ return $this->actions; } + + private static function readAction(NetworkBinaryStream $in, int $typeId) : ItemStackRequestAction{ + switch($typeId){ + case TakeStackRequestAction::getTypeId(): return TakeStackRequestAction::read($in); + case PlaceStackRequestAction::getTypeId(): return PlaceStackRequestAction::read($in); + case SwapStackRequestAction::getTypeId(): return SwapStackRequestAction::read($in); + case DropStackRequestAction::getTypeId(): return DropStackRequestAction::read($in); + case DestroyStackRequestAction::getTypeId(): return DestroyStackRequestAction::read($in); + case CraftingConsumeInputStackRequestAction::getTypeId(): return CraftingConsumeInputStackRequestAction::read($in); + case CraftingMarkSecondaryResultStackRequestAction::getTypeId(): return CraftingMarkSecondaryResultStackRequestAction::read($in); + case LabTableCombineStackRequestAction::getTypeId(): return LabTableCombineStackRequestAction::read($in); + case BeaconPaymentStackRequestAction::getTypeId(): return BeaconPaymentStackRequestAction::read($in); + case CraftRecipeStackRequestAction::getTypeId(): return CraftRecipeStackRequestAction::read($in); + case CraftRecipeAutoStackRequestAction::getTypeId(): return CraftRecipeAutoStackRequestAction::read($in); + case CreativeCreateStackRequestAction::getTypeId(): return CreativeCreateStackRequestAction::read($in); + case DeprecatedCraftingNonImplementedStackRequestAction::getTypeId(): return DeprecatedCraftingNonImplementedStackRequestAction::read($in); + case DeprecatedCraftingResultsStackRequestAction::getTypeId(): return DeprecatedCraftingResultsStackRequestAction::read($in); + } + throw new \UnexpectedValueException("Unhandled item stack request action type $typeId"); + } + + public static function read(NetworkBinaryStream $in) : self{ + $requestId = $in->readGenericTypeNetworkId(); + $actions = []; + for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ + $typeId = $in->getByte(); + $actions[] = self::readAction($in, $typeId); + } + return new self($requestId, $actions); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->writeGenericTypeNetworkId($this->requestId); + $out->putUnsignedVarInt(count($this->actions)); + foreach($this->actions as $action){ + $out->putByte($action::getTypeId()); + $action->write($out); + } + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequestAction.php new file mode 100644 index 000000000..26437fdd6 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/ItemStackRequestAction.php @@ -0,0 +1,33 @@ +containerId = $containerId; + $this->slotId = $slotId; + $this->stackId = $stackId; + } + + public function getContainerId() : int{ return $this->containerId; } + + public function getSlotId() : int{ return $this->slotId; } + + public function getStackId() : int{ return $this->stackId; } + + public static function read(NetworkBinaryStream $in) : self{ + $containerId = $in->getByte(); + $slotId = $in->getByte(); + $stackId = $in->readGenericTypeNetworkId(); + return new self($containerId, $slotId, $stackId); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->containerId); + $out->putByte($this->slotId); + $out->writeGenericTypeNetworkId($this->stackId); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/LabTableCombineStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/LabTableCombineStackRequestAction.php new file mode 100644 index 000000000..501bc3159 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/LabTableCombineStackRequestAction.php @@ -0,0 +1,43 @@ +slot1 = $slot1; + $this->slot2 = $slot2; + } + + public function getSlot1() : ItemStackRequestSlotInfo{ return $this->slot1; } + + public function getSlot2() : ItemStackRequestSlotInfo{ return $this->slot2; } + + public static function getTypeId() : int{ return ItemStackRequestActionType::SWAP; } + + public static function read(NetworkBinaryStream $in) : self{ + $slot1 = ItemStackRequestSlotInfo::read($in); + $slot2 = ItemStackRequestSlotInfo::read($in); + return new self($slot1, $slot2); + } + + public function write(NetworkBinaryStream $out) : void{ + $this->slot1->write($out); + $this->slot2->write($out); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeOrPlaceStackRequestActionTrait.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeOrPlaceStackRequestActionTrait.php new file mode 100644 index 000000000..63388d792 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeOrPlaceStackRequestActionTrait.php @@ -0,0 +1,61 @@ +count = $count; + $this->source = $source; + $this->destination = $destination; + } + + final public function getCount() : int{ return $this->count; } + + final public function getSource() : ItemStackRequestSlotInfo{ return $this->source; } + + final public function getDestination() : ItemStackRequestSlotInfo{ return $this->destination; } + + public static function read(NetworkBinaryStream $in) : self{ + $count = $in->getByte(); + $src = ItemStackRequestSlotInfo::read($in); + $dst = ItemStackRequestSlotInfo::read($in); + return new self($count, $src, $dst); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->count); + $this->source->write($out); + $this->destination->write($out); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeStackRequestAction.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeStackRequestAction.php new file mode 100644 index 000000000..f2946def2 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackrequest/TakeStackRequestAction.php @@ -0,0 +1,33 @@ +ok = $ok; + $this->requestId = $requestId; + $this->containerInfos = $containerInfos; + } + + public function isOk() : bool{ return $this->ok; } + + public function getRequestId() : int{ return $this->requestId; } + + /** @return ItemStackResponseContainerInfo[] */ + public function getContainerInfos() : array{ return $this->containerInfos; } + + public static function read(NetworkBinaryStream $in) : self{ + $ok = $in->getBool(); + $requestId = $in->readGenericTypeNetworkId(); + $containerInfos = []; + for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ + $containerInfos[] = ItemStackResponseContainerInfo::read($in); + } + return new self($ok, $requestId, $containerInfos); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putBool($this->ok); + $out->writeGenericTypeNetworkId($this->requestId); + $out->putUnsignedVarInt(count($this->containerInfos)); + foreach($this->containerInfos as $containerInfo){ + $containerInfo->write($out); + } + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseContainerInfo.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseContainerInfo.php new file mode 100644 index 000000000..50c956596 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseContainerInfo.php @@ -0,0 +1,65 @@ +containerId = $containerId; + $this->slots = $slots; + } + + public function getContainerId() : int{ return $this->containerId; } + + /** @return ItemStackResponseSlotInfo[] */ + public function getSlots() : array{ return $this->slots; } + + public static function read(NetworkBinaryStream $in) : self{ + $containerId = $in->getByte(); + $slots = []; + for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){ + $slots[] = ItemStackResponseSlotInfo::read($in); + } + return new self($containerId, $slots); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->containerId); + $out->putUnsignedVarInt(count($this->slots)); + foreach($this->slots as $slot){ + $slot->write($out); + } + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseSlotInfo.php b/src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseSlotInfo.php new file mode 100644 index 000000000..f8ac0a781 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/inventory/stackresponse/ItemStackResponseSlotInfo.php @@ -0,0 +1,68 @@ +slot = $slot; + $this->hotbarSlot = $hotbarSlot; + $this->count = $count; + $this->itemStackId = $itemStackId; + } + + public function getSlot() : int{ return $this->slot; } + + public function getHotbarSlot() : int{ return $this->hotbarSlot; } + + public function getCount() : int{ return $this->count; } + + public function getItemStackId() : int{ return $this->itemStackId; } + + public static function read(NetworkBinaryStream $in) : self{ + $slot = $in->getByte(); + $hotbarSlot = $in->getByte(); + $count = $in->getByte(); + $itemStackId = $in->readGenericTypeNetworkId(); + return new self($slot, $hotbarSlot, $count, $itemStackId); + } + + public function write(NetworkBinaryStream $out) : void{ + $out->putByte($this->slot); + $out->putByte($this->hotbarSlot); + $out->putByte($this->count); + $out->writeGenericTypeNetworkId($this->itemStackId); + } +} diff --git a/src/pocketmine/resources/vanilla b/src/pocketmine/resources/vanilla index cc132c80d..43edcfde6 160000 --- a/src/pocketmine/resources/vanilla +++ b/src/pocketmine/resources/vanilla @@ -1 +1 @@ -Subproject commit cc132c80dd9d76a44e4b0a360e85e8e28bba8956 +Subproject commit 43edcfde6b9611b7c7d643be52332707cc10cd1b diff --git a/tests/phpstan/configs/check-explicit-mixed-baseline.neon b/tests/phpstan/configs/check-explicit-mixed-baseline.neon index c82bdac38..ac5ec53fd 100644 --- a/tests/phpstan/configs/check-explicit-mixed-baseline.neon +++ b/tests/phpstan/configs/check-explicit-mixed-baseline.neon @@ -385,6 +385,16 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/VerifyLoginTask.php + - + message: "#^Cannot access offset string on mixed\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + + - + message: "#^Offset mixed does not exist on array\\\\|null\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + - message: "#^Property pocketmine\\\\network\\\\mcpe\\\\protocol\\\\AddActorPacket\\:\\:\\$metadata \\(array\\\\) does not accept array\\\\.$#" count: 1 @@ -455,11 +465,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/StartGamePacket.php - - - message: "#^Cannot access offset string on mixed\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - message: "#^Parameter \\#1 \\$value of static method pocketmine\\\\permission\\\\Permission\\:\\:getByName\\(\\) expects bool\\|string, mixed given\\.$#" count: 1 diff --git a/tests/phpstan/configs/l7-baseline.neon b/tests/phpstan/configs/l7-baseline.neon index 6aee260f6..bb844e3a3 100644 --- a/tests/phpstan/configs/l7-baseline.neon +++ b/tests/phpstan/configs/l7-baseline.neon @@ -840,6 +840,21 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/VerifyLoginTask.php + - + message: "#^Parameter \\#1 \\$buffer of method pocketmine\\\\nbt\\\\NBTStream\\:\\:read\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + + - + message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + + - + message: "#^Parameter \\#1 \\$buffer of class pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream constructor expects string, string\\|false given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + - message: "#^Parameter \\#1 \\$str of method pocketmine\\\\utils\\\\BinaryStream\\:\\:put\\(\\) expects string, string\\|false given\\.$#" count: 1 @@ -870,6 +885,11 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/LevelEventGenericPacket.php + - + message: "#^Parameter \\#1 \\$str of method pocketmine\\\\utils\\\\BinaryStream\\:\\:put\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/protocol/PositionTrackingDBServerBroadcastPacket.php + - message: "#^Static property pocketmine\\\\network\\\\mcpe\\\\protocol\\\\StartGamePacket\\:\\:\\$blockTableCache \\(string\\|null\\) does not accept string\\|false\\.$#" count: 1 @@ -895,16 +915,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php - - - message: "#^Parameter \\#1 \\$buffer of method pocketmine\\\\nbt\\\\NBTStream\\:\\:read\\(\\) expects string, string\\|false given\\.$#" - count: 2 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - message: "#^Call to an undefined method object\\:\\:Add\\(\\)\\.$#" count: 1 diff --git a/tests/phpstan/configs/l8-baseline.neon b/tests/phpstan/configs/l8-baseline.neon index 6c82adc4b..3956d0103 100644 --- a/tests/phpstan/configs/l8-baseline.neon +++ b/tests/phpstan/configs/l8-baseline.neon @@ -1515,6 +1515,21 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/NetworkBinaryStream.php + - + message: "#^Argument of an invalid type array\\\\|null supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + + - + message: "#^Parameter \\#1 \\$that of method pocketmine\\\\nbt\\\\tag\\\\NamedTag\\:\\:equals\\(\\) expects pocketmine\\\\nbt\\\\tag\\\\NamedTag, pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null given\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + + - + message: "#^Method pocketmine\\\\network\\\\mcpe\\\\convert\\\\RuntimeBlockMapping\\:\\:getBedrockKnownStates\\(\\) should return array\\ but returns array\\\\|null\\.$#" + count: 1 + path: ../../../src/pocketmine/network/mcpe/convert/RuntimeBlockMapping.php + - message: "#^Parameter \\#1 \\$eid of method pocketmine\\\\network\\\\mcpe\\\\NetworkBinaryStream\\:\\:putEntityUniqueId\\(\\) expects int, int\\|null given\\.$#" count: 1 @@ -1530,41 +1545,6 @@ parameters: count: 1 path: ../../../src/pocketmine/network/mcpe/protocol/SetScoreboardIdentityPacket.php - - - message: "#^Argument of an invalid type array\\\\|null supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - - message: "#^Cannot call method getString\\(\\) on pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null\\.$#" - count: 2 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - - message: "#^Cannot call method getShort\\(\\) on pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - - message: "#^Cannot call method setName\\(\\) on pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - - message: "#^Offset mixed does not exist on array\\\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - - message: "#^Cannot call method equals\\(\\) on pocketmine\\\\nbt\\\\tag\\\\CompoundTag\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - - - message: "#^Method pocketmine\\\\network\\\\mcpe\\\\protocol\\\\types\\\\RuntimeBlockMapping\\:\\:getBedrockKnownStates\\(\\) should return array\\ but returns array\\\\|null\\.$#" - count: 1 - path: ../../../src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php - - message: "#^Cannot call method wakeupSleeper\\(\\) on pocketmine\\\\snooze\\\\SleeperNotifier\\|null\\.$#" count: 1