From cd2b60a860bc4fb412a7b08bbcdfa438e5d2d0c0 Mon Sep 17 00:00:00 2001 From: Drew Date: Wed, 30 Oct 2019 21:36:08 -0400 Subject: [PATCH 01/95] Initial update to 1.13 --- src/pocketmine/Player.php | 28 +++-- src/pocketmine/entity/Skin.php | 102 +++++++++++++++--- src/pocketmine/level/Explosion.php | 7 -- .../network/mcpe/NetworkBinaryStream.php | 14 +++ .../network/mcpe/NetworkSession.php | 4 - .../mcpe/PlayerNetworkSessionAdapter.php | 2 +- .../network/mcpe/protocol/ExplodePacket.php | 73 ------------- .../network/mcpe/protocol/PacketPool.php | 1 - .../mcpe/protocol/PlayerListPacket.php | 67 ++++++++---- .../mcpe/protocol/PlayerSkinPacket.php | 54 ++++++---- .../network/mcpe/protocol/ProtocolInfo.php | 16 ++- .../protocol/ResourcePackChunkDataPacket.php | 5 +- .../network/mcpe/protocol/RespawnPacket.php | 8 ++ .../network/mcpe/protocol/StartGamePacket.php | 29 ++++- .../mcpe/protocol/types/PlayerListEntry.php | 15 ++- .../mcpe/protocol/types/ResourcePackType.php | 1 + .../protocol/types/RuntimeBlockMapping.php | 27 ++++- .../resources/runtime_block_states.dat | Bin 0 -> 208936 bytes src/pocketmine/utils/SerializedImage.php | 67 ++++++++++++ src/pocketmine/utils/SkinAnimation.php | 52 +++++++++ 20 files changed, 408 insertions(+), 164 deletions(-) delete mode 100644 src/pocketmine/network/mcpe/protocol/ExplodePacket.php create mode 100644 src/pocketmine/resources/runtime_block_states.dat create mode 100644 src/pocketmine/utils/SerializedImage.php create mode 100644 src/pocketmine/utils/SkinAnimation.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 1969fc1da..11fac9fb5 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -160,6 +160,8 @@ use pocketmine\tile\ItemFrame; use pocketmine\tile\Spawnable; use pocketmine\tile\Tile; use pocketmine\timings\Timings; +use pocketmine\utils\SerializedImage; +use pocketmine\utils\SkinAnimation; use pocketmine\utils\TextFormat; use pocketmine\utils\UUID; use function abs; @@ -834,12 +836,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ * Plugin developers should not use this, use setSkin() and sendSkin() instead. * * @param Skin $skin - * @param string $newSkinName - * @param string $oldSkinName * * @return bool */ - public function changeSkin(Skin $skin, string $newSkinName, string $oldSkinName) : bool{ + public function changeSkin(Skin $skin) : bool{ if(!$skin->isValid()){ return false; } @@ -1112,9 +1112,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - protected function sendRespawnPacket(Vector3 $pos){ + protected function sendRespawnPacket(Vector3 $pos, int $respawnState = 1){ $pk = new RespawnPacket(); $pk->position = $pos->add(0, $this->baseOffset, 0); + $pk->respawnState = $respawnState; + $pk->entityRuntimeId = $this->getId(); $this->dataPacket($pk); } @@ -1913,12 +1915,22 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->uuid = UUID::fromString($packet->clientUUID); $this->rawUUID = $this->uuid->toBinary(); + $animations = []; + foreach($packet->clientData["AnimatedImageData"] as $animatedData){ + $animations[] = new SkinAnimation(new SerializedImage($animatedData["ImageWidth"], $animatedData["ImageHeight"], base64_decode($animatedData["Image"])), $animatedData["Type"], $animatedData["Frames"]); + } $skin = new Skin( $packet->clientData["SkinId"], - base64_decode($packet->clientData["SkinData"] ?? ""), - base64_decode($packet->clientData["CapeData"] ?? ""), - $packet->clientData["SkinGeometryName"] ?? "", - base64_decode($packet->clientData["SkinGeometry"] ?? "") + base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), + new SerializedImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData" ?? ""])), //SerializedImage + $animations, + new SerializedImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")), + base64_decode($packet->clientData["SkinGeometryData"] ?? ""), + base64_decode($packet->clientData["SkinAnimationData"] ?? ""), + (bool) $packet->clientData["PremiumSkin"] ?? false, + (bool) $packet->clientData["PersonaSkin"] ?? false, + (bool) $packet->clientData["CapeOnClassicSkin"] ?? false, + $packet->clientData["CapeId"] ?? "" ); if(!$skin->isValid()){ diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index d5a1477ea..497154094 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -24,6 +24,8 @@ declare(strict_types=1); namespace pocketmine\entity; use Ahc\Json\Comment as CommentedJsonDecoder; +use pocketmine\utils\SerializedImage; +use pocketmine\utils\SkinAnimation; use function implode; use function in_array; use function json_encode; @@ -39,20 +41,38 @@ class Skin{ /** @var string */ private $skinId; /** @var string */ + private $skinResourcePatch; + /** @var SerializedImage */ private $skinData; - /** @var string */ + /** @var SkinAnimation[] */ + private $animations = []; + /** @var SerializedImage */ private $capeData; /** @var string */ - private $geometryName; - /** @var string */ private $geometryData; + /** @var string */ + private $animationData; + /** @var bool */ + private $premium; + /** @var bool */ + private $persona; + /** @var bool */ + private $capeOnClassic; + /** @var string */ + private $capeId; - public function __construct(string $skinId, string $skinData, string $capeData = "", string $geometryName = "", string $geometryData = ""){ + public function __construct(string $skinId, string $skinResourcePatch, SerializedImage $skinData, array $animations = [], SerializedImage $capeData = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $capeOnClassic = false, string $capeId = ""){ $this->skinId = $skinId; + $this->skinResourcePatch = $skinResourcePatch; $this->skinData = $skinData; + $this->animations = $animations; $this->capeData = $capeData; - $this->geometryName = $geometryName; $this->geometryData = $geometryData; + $this->animationData = $animationData; + $this->premium = $premium; + $this->persona = $persona; + $this->capeOnClassic = $capeOnClassic; + $this->capeId = $capeId; } /** @@ -75,13 +95,14 @@ class Skin{ if($this->skinId === ""){ throw new \InvalidArgumentException("Skin ID must not be empty"); } - $len = strlen($this->skinData); + //Broken with Persona skins + /*$len = strlen($this->skinData->getData()); if(!in_array($len, self::ACCEPTED_SKIN_SIZES, true)){ throw new \InvalidArgumentException("Invalid skin data size $len bytes (allowed sizes: " . implode(", ", self::ACCEPTED_SKIN_SIZES) . ")"); } - if($this->capeData !== "" and strlen($this->capeData) !== 8192){ - throw new \InvalidArgumentException("Invalid cape data size " . strlen($this->capeData) . " bytes (must be exactly 8192 bytes)"); - } + if($this->capeData->getData() !== "" and strlen($this->capeData->getData()) !== 8192){ + throw new \InvalidArgumentException("Invalid cape data size " . strlen($this->capeData->getData()) . " bytes (must be exactly 8192 bytes)"); + }*/ //TODO: validate geometry } @@ -95,22 +116,29 @@ class Skin{ /** * @return string */ - public function getSkinData() : string{ + public function getSkinResourcePatch() : string{ + return $this->skinResourcePatch; + } + + /** + * @return SerializedImage + */ + public function getSkinData() : SerializedImage{ return $this->skinData; } /** - * @return string + * @return SkinAnimation[] */ - public function getCapeData() : string{ - return $this->capeData; + public function getAnimations() : array{ + return $this->animations; } /** - * @return string + * @return SerializedImage */ - public function getGeometryName() : string{ - return $this->geometryName; + public function getCapeData() : SerializedImage{ + return $this->capeData; } /** @@ -120,6 +148,48 @@ class Skin{ return $this->geometryData; } + /** + * @return string + */ + public function getAnimationData() : string{ + return $this->animationData; + } + + /** + * @return bool + */ + public function getPremium() : bool{ + return $this->premium; + } + + /** + * @return bool + */ + public function getPersona() : bool{ + return $this->persona; + } + + /** + * @return bool + */ + public function getCapeOnClassic() : bool{ + return $this->capeOnClassic; + } + + /** + * @return string + */ + public function getCapeId() : string{ + return $this->capeId; + } + + /** + * @return string + */ + public function getFullSkinId() : string{ + return $this->skinId . "_" . $this->capeId; + } + /** * Hack to cut down on network overhead due to skins, by un-pretty-printing geometry JSON. * diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index e234fdd3e..d3906790e 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -38,7 +38,6 @@ use pocketmine\level\particle\HugeExplodeSeedParticle; use pocketmine\level\utils\SubChunkIteratorManager; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; -use pocketmine\network\mcpe\protocol\ExplodePacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\tile\Chest; use pocketmine\tile\Container; @@ -260,12 +259,6 @@ class Explosion{ $send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z); } - $pk = new ExplodePacket(); - $pk->position = $this->source->asVector3(); - $pk->radius = $this->size; - $pk->records = $send; - $this->level->broadcastPacketToViewers($source, $pk); - $this->level->addParticle(new HugeExplodeSeedParticle($source)); $this->level->broadcastLevelSoundEvent($source, LevelSoundEventPacket::SOUND_EXPLODE); diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index ba48a129b..398357f55 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -39,6 +39,7 @@ use pocketmine\network\mcpe\protocol\types\CommandOriginData; use pocketmine\network\mcpe\protocol\types\EntityLink; use pocketmine\network\mcpe\protocol\types\StructureSettings; use pocketmine\utils\BinaryStream; +use pocketmine\utils\SerializedImage; use pocketmine\utils\UUID; use function count; use function strlen; @@ -74,6 +75,19 @@ class NetworkBinaryStream extends BinaryStream{ $this->putLInt($uuid->getPart(2)); } + public function putImage(SerializedImage $image) : void{ + $this->putLInt($image->getWidth()); + $this->putLInt($image->getHeight()); + $this->putString($image->getData()); + } + + public function getImage() : SerializedImage{ + $width = $this->getLInt(); + $height = $this->getLInt(); + $data = $this->getString(); + return new SerializedImage($height, $width, $data); + } + public function getSlot() : Item{ $id = $this->getVarInt(); if($id === 0){ diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index 4de360f3a..338034558 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -247,10 +247,6 @@ abstract class NetworkSession{ return false; } - public function handleExplode(ExplodePacket $packet) : bool{ - return false; - } - public function handleLevelSoundEventPacketV1(LevelSoundEventPacketV1 $packet) : bool{ return false; } diff --git a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php index 7e546647c..914b8becc 100644 --- a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php +++ b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php @@ -248,7 +248,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{ } public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{ - return $this->player->changeSkin($packet->skin, $packet->newSkinName, $packet->oldSkinName); + return $this->player->changeSkin($packet->skin); } public function handleBookEdit(BookEditPacket $packet) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/ExplodePacket.php b/src/pocketmine/network/mcpe/protocol/ExplodePacket.php deleted file mode 100644 index 174d8a6bf..000000000 --- a/src/pocketmine/network/mcpe/protocol/ExplodePacket.php +++ /dev/null @@ -1,73 +0,0 @@ - - - -use pocketmine\math\Vector3; -use pocketmine\network\mcpe\NetworkSession; -use function count; - -class ExplodePacket extends DataPacket{ - public const NETWORK_ID = ProtocolInfo::EXPLODE_PACKET; - - /** @var Vector3 */ - public $position; - /** @var float */ - public $radius; - /** @var Vector3[] */ - public $records = []; - - public function clean(){ - $this->records = []; - return parent::clean(); - } - - protected function decodePayload(){ - $this->position = $this->getVector3(); - $this->radius = (float) ($this->getVarInt() / 32); - $count = $this->getUnsignedVarInt(); - for($i = 0; $i < $count; ++$i){ - $x = $y = $z = null; - $this->getSignedBlockPosition($x, $y, $z); - $this->records[$i] = new Vector3($x, $y, $z); - } - } - - protected function encodePayload(){ - $this->putVector3($this->position); - $this->putVarInt((int) ($this->radius * 32)); - $this->putUnsignedVarInt(count($this->records)); - if(count($this->records) > 0){ - foreach($this->records as $record){ - $this->putSignedBlockPosition((int) $record->x, (int) $record->y, (int) $record->z); - } - } - } - - public function handle(NetworkSession $session) : bool{ - return $session->handleExplode($this); - } -} diff --git a/src/pocketmine/network/mcpe/protocol/PacketPool.php b/src/pocketmine/network/mcpe/protocol/PacketPool.php index 05c532d60..c67e031dc 100644 --- a/src/pocketmine/network/mcpe/protocol/PacketPool.php +++ b/src/pocketmine/network/mcpe/protocol/PacketPool.php @@ -54,7 +54,6 @@ class PacketPool{ static::registerPacket(new RiderJumpPacket()); static::registerPacket(new UpdateBlockPacket()); static::registerPacket(new AddPaintingPacket()); - static::registerPacket(new ExplodePacket()); static::registerPacket(new LevelSoundEventPacketV1()); static::registerPacket(new LevelEventPacket()); static::registerPacket(new BlockEventPacket()); diff --git a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php index 9d8bd8f1b..160154c77 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php @@ -29,6 +29,8 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\entity\Skin; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; +use pocketmine\utils\SerializedImage; +use pocketmine\utils\SkinAnimation; use function count; class PlayerListPacket extends DataPacket{ @@ -57,22 +59,32 @@ class PlayerListPacket extends DataPacket{ $entry->uuid = $this->getUUID(); $entry->entityUniqueId = $this->getEntityUniqueId(); $entry->username = $this->getString(); - - $skinId = $this->getString(); - $skinData = $this->getString(); - $capeData = $this->getString(); - $geometryName = $this->getString(); - $geometryData = $this->getString(); - - $entry->skin = new Skin( - $skinId, - $skinData, - $capeData, - $geometryName, - $geometryData - ); $entry->xboxUserId = $this->getString(); $entry->platformChatId = $this->getString(); + $entry->buildPlatform = $this->getLInt(); + + $skinId = $this->getString(); + $skinResourcePatch = $this->getString(); + $skinData = $this->getImage(); + $animations = []; + for($i = 0; $i < $this->getLInt(); ++$i){ + $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); + } + $capeData = $this->getImage(); + $geometryData = $this->getString(); + $animationData = $this->getString(); + $premium = $this->getBool(); + $persona = $this->getBool(); + $capeOnClassic = $this->getBool(); + $capeId = $this->getString(); + $fullSkinId = $this->getString(); + + $entry->skin = new Skin( + $skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId + ); + + $entry->isTeacher = $this->getBool(); + $entry->isHost = $this->getBool(); }else{ $entry->uuid = $this->getUUID(); } @@ -89,13 +101,30 @@ class PlayerListPacket extends DataPacket{ $this->putUUID($entry->uuid); $this->putEntityUniqueId($entry->entityUniqueId); $this->putString($entry->username); - $this->putString($entry->skin->getSkinId()); - $this->putString($entry->skin->getSkinData()); - $this->putString($entry->skin->getCapeData()); - $this->putString($entry->skin->getGeometryName()); - $this->putString($entry->skin->getGeometryData()); $this->putString($entry->xboxUserId); $this->putString($entry->platformChatId); + $this->putLInt($entry->buildPlatform); + + $this->putString($entry->skin->getSkinId()); + $this->putString($entry->skin->getSkinResourcePatch()); + $this->putImage($entry->skin->getSkinData()); + $this->putLInt(count($entry->skin->getAnimations())); + foreach($entry->skin->getAnimations() as $animation){ + $this->putImage($animation->getImage()); + $this->putLInt($animation->getType()); + $this->putLFloat($animation->getFrames()); + } + $this->putImage($entry->skin->getCapeData()); + $this->putString($entry->skin->getGeometryData()); + $this->putString($entry->skin->getAnimationData()); + $this->putBool($entry->skin->getPremium()); + $this->putBool($entry->skin->getPersona()); + $this->putBool($entry->skin->getCapeOnClassic()); + $this->putString($entry->skin->getCapeId()); + $this->putString($entry->skin->getFullSkinId()); + + $this->putBool($entry->isTeacher); + $this->putBool($entry->isHost); }else{ $this->putUUID($entry->uuid); } diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index ea56e12cb..a6da88129 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -27,6 +27,8 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\entity\Skin; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\utils\SerializedImage; +use pocketmine\utils\SkinAnimation; use pocketmine\utils\UUID; class PlayerSkinPacket extends DataPacket{ @@ -34,43 +36,53 @@ class PlayerSkinPacket extends DataPacket{ /** @var UUID */ public $uuid; - /** @var string */ - public $oldSkinName = ""; - /** @var string */ - public $newSkinName = ""; /** @var Skin */ public $skin; - /** @var bool */ - public $premiumSkin = false; protected function decodePayload(){ $this->uuid = $this->getUUID(); $skinId = $this->getString(); - $this->newSkinName = $this->getString(); - $this->oldSkinName = $this->getString(); - $skinData = $this->getString(); - $capeData = $this->getString(); - $geometryModel = $this->getString(); + $skinResourcePatch = $this->getString(); + $skinData = $this->getImage(); + $animations = []; + for($i = 0; $i < $this->getLInt(); ++$i){ + $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); + } + $capeData = $this->getImage(); $geometryData = $this->getString(); + $animationData = $this->getString(); + $premium = $this->getBool(); + $persona = $this->getBool(); + $capeOnClassic = $this->getBool(); + $capeId = $this->getString(); + $fullSkinId = $this->getString(); - $this->skin = new Skin($skinId, $skinData, $capeData, $geometryModel, $geometryData); - - $this->premiumSkin = $this->getBool(); + $this->skin = new Skin( + $skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId + ); } protected function encodePayload(){ $this->putUUID($this->uuid); $this->putString($this->skin->getSkinId()); - $this->putString($this->newSkinName); - $this->putString($this->oldSkinName); - $this->putString($this->skin->getSkinData()); - $this->putString($this->skin->getCapeData()); - $this->putString($this->skin->getGeometryName()); + $this->putString($this->skin->getSkinResourcePatch()); + $this->putImage($this->skin->getSkinData()); + $this->putLInt(count($this->skin->getAnimations())); + foreach($this->skin->getAnimations() as $animation){ + $this->putImage($animation->getImage()); + $this->putLInt($animation->getType()); + $this->putLFloat($animation->getFrames()); + } + $this->putImage($this->skin->getCapeData()); $this->putString($this->skin->getGeometryData()); - - $this->putBool($this->premiumSkin); + $this->putString($this->skin->getAnimationData()); + $this->putBool($this->skin->getPremium()); + $this->putBool($this->skin->getPersona()); + $this->putBool($this->skin->getCapeOnClassic()); + $this->putString($this->skin->getCapeId()); + $this->putString($this->skin->getFullSkinId()); } public function handle(NetworkSession $session) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php index 152dd4463..a0b459a55 100644 --- a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php +++ b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php @@ -39,15 +39,15 @@ interface ProtocolInfo{ /** * Actual Minecraft: PE protocol version */ - public const CURRENT_PROTOCOL = 361; + public const CURRENT_PROTOCOL = 388; /** * Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */ - public const MINECRAFT_VERSION = 'v1.12.0'; + public const MINECRAFT_VERSION = 'v1.13.0'; /** * Version number sent to clients in ping responses. */ - public const MINECRAFT_VERSION_NETWORK = '1.12.0'; + public const MINECRAFT_VERSION_NETWORK = '1.13.0'; public const LOGIN_PACKET = 0x01; public const PLAY_STATUS_PACKET = 0x02; @@ -71,7 +71,7 @@ interface ProtocolInfo{ public const RIDER_JUMP_PACKET = 0x14; public const UPDATE_BLOCK_PACKET = 0x15; public const ADD_PAINTING_PACKET = 0x16; - public const EXPLODE_PACKET = 0x17; + public const TICK_SYNC_PACKET = 0x17; public const LEVEL_SOUND_EVENT_PACKET_V1 = 0x18; public const LEVEL_EVENT_PACKET = 0x19; public const BLOCK_EVENT_PACKET = 0x1a; @@ -185,5 +185,13 @@ interface ProtocolInfo{ public const UPDATE_BLOCK_PROPERTIES_PACKET = 0x86; public const CLIENT_CACHE_BLOB_STATUS_PACKET = 0x87; public const CLIENT_CACHE_MISS_RESPONSE_PACKET = 0x88; + public const EDUCATION_SETTINGS_PACKET = 0x89; + public const EMOTE_PACKET = 0x8a; + public const MULTIPLAYER_SETTINGS_PACKET = 0x8b; + public const SETTINGS_COMMAND_PACKET = 0x8c; + public const ANVIL_DAMAGE_PACKET = 0x8d; + public const COMPLETED_USING_ITEM_PACKET = 0x8e; + public const NETWORK_SETTINGS_PACKET = 0x8f; + public const PLAYER_AUTH_INPUT_PACKET = 0x90; } diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackChunkDataPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackChunkDataPacket.php index 93b86e810..07d34affd 100644 --- a/src/pocketmine/network/mcpe/protocol/ResourcePackChunkDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ResourcePackChunkDataPacket.php @@ -46,15 +46,14 @@ class ResourcePackChunkDataPacket extends DataPacket{ $this->packId = $this->getString(); $this->chunkIndex = $this->getLInt(); $this->progress = $this->getLLong(); - $this->data = $this->get($this->getLInt()); + $this->data = $this->getString(); } protected function encodePayload(){ $this->putString($this->packId); $this->putLInt($this->chunkIndex); $this->putLLong($this->progress); - $this->putLInt(strlen($this->data)); - $this->put($this->data); + $this->putString($this->data); } public function handle(NetworkSession $session) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/RespawnPacket.php b/src/pocketmine/network/mcpe/protocol/RespawnPacket.php index b13e7bcb7..1f8cb4091 100644 --- a/src/pocketmine/network/mcpe/protocol/RespawnPacket.php +++ b/src/pocketmine/network/mcpe/protocol/RespawnPacket.php @@ -34,13 +34,21 @@ class RespawnPacket extends DataPacket{ /** @var Vector3 */ public $position; + /** @var int */ + public $respawnState = 1; //TODO: Add + /** @var int */ + public $entityRuntimeId; protected function decodePayload(){ $this->position = $this->getVector3(); + $this->respawnState = $this->getInt(); + $this->entityRuntimeId = $this->getEntityRuntimeId(); } protected function encodePayload(){ $this->putVector3($this->position); + $this->putInt($this->respawnState); + $this->putEntityRuntimeId($this->entityRuntimeId); } 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 557dee7ca..af0f47541 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -27,6 +27,10 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\math\Vector3; +use pocketmine\nbt\NetworkLittleEndianNBTStream; +use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\tag\ListTag; +use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\NetworkBinaryStream; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; @@ -130,6 +134,8 @@ class StartGamePacket extends DataPacket{ /** @var bool */ public $onlySpawnV1Villagers = false; + /** @var string */ + public $vanillaVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK; /** @var string */ public $levelId = ""; //base64 string, usually the same as world folder name in vanilla /** @var string */ @@ -138,6 +144,8 @@ class StartGamePacket extends DataPacket{ public $premiumWorldTemplateId = ""; /** @var bool */ public $isTrial = false; + /** @var bool */ + public $isMovementServerAuthoritative = false; /** @var int */ public $currentTick = 0; //only used if isTrial is true /** @var int */ @@ -193,10 +201,12 @@ class StartGamePacket extends DataPacket{ $this->isWorldTemplateOptionLocked = $this->getBool(); $this->onlySpawnV1Villagers = $this->getBool(); + $this->vanillaVersion = $this->getString(); $this->levelId = $this->getString(); $this->worldName = $this->getString(); $this->premiumWorldTemplateId = $this->getString(); $this->isTrial = $this->getBool(); + $this->isMovementServerAuthoritative = $this->getBool(); $this->currentTick = $this->getLLong(); $this->enchantmentSeed = $this->getVarInt(); @@ -263,10 +273,12 @@ class StartGamePacket extends DataPacket{ $this->putBool($this->isWorldTemplateOptionLocked); $this->putBool($this->onlySpawnV1Villagers); + $this->putString($this->vanillaVersion); $this->putString($this->levelId); $this->putString($this->worldName); $this->putString($this->premiumWorldTemplateId); $this->putBool($this->isTrial); + $this->putBool($this->isMovementServerAuthoritative); $this->putLLong($this->currentTick); $this->putVarInt($this->enchantmentSeed); @@ -293,14 +305,27 @@ class StartGamePacket extends DataPacket{ } private static function serializeBlockTable(array $table) : string{ - $stream = new NetworkBinaryStream(); + $states = new ListTag(); + foreach($table as $v){ + $state = new CompoundTag(); + $state->setTag(new CompoundTag("block", [ + new StringTag("name", $v["name"]), + $v["states"] + ])); + $state->setShort("id", $v["legacy_id"]); + $states->push($state); + } + ($stream = new NetworkLittleEndianNBTStream())->writeTag($states); + return $stream->buffer; + + /*$stream = new NetworkBinaryStream(); $stream->putUnsignedVarInt(count($table)); foreach($table as $v){ $stream->putString($v["name"]); $stream->putLShort($v["data"]); $stream->putLShort($v["legacy_id"]); } - return $stream->getBuffer(); + return $stream->getBuffer();*/ } private static function serializeItemTable(array $table) : string{ diff --git a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php index b8b14f0d8..95bc19c9e 100644 --- a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php +++ b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php @@ -34,12 +34,18 @@ class PlayerListEntry{ public $entityUniqueId; /** @var string */ public $username; - /** @var Skin */ - public $skin; /** @var string */ public $xboxUserId; /** @var string */ public $platformChatId = ""; + /** @var int */ + public $buildPlatform = 0; //Unknown + /** @var Skin */ + public $skin; + /** @var bool */ + public $isTeacher = false; + /** @var bool */ + public $isHost = false; public static function createRemovalEntry(UUID $uuid) : PlayerListEntry{ $entry = new PlayerListEntry(); @@ -48,7 +54,7 @@ class PlayerListEntry{ return $entry; } - public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "") : PlayerListEntry{ + public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = 0, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{ $entry = new PlayerListEntry(); $entry->uuid = $uuid; $entry->entityUniqueId = $entityUniqueId; @@ -56,6 +62,9 @@ class PlayerListEntry{ $entry->skin = $skin; $entry->xboxUserId = $xboxUserId; $entry->platformChatId = $platformChatId; + $entry->buildPlatform = $buildPlatform; + $entry->isTeacher = $isTeacher; + $entry->isHost = $isHost; return $entry; } diff --git a/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php b/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php index 9efaced08..da4931b44 100644 --- a/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php +++ b/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php @@ -29,6 +29,7 @@ final class ResourcePackType{ //NOOP } + //Needs updated public const INVALID = 0; public const RESOURCES = 1; public const BEHAVIORS = 2; diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php index 606148d1b..956b1d922 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -24,6 +24,9 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol\types; use pocketmine\block\BlockIds; +use pocketmine\nbt\BigEndianNBTStream; +use pocketmine\nbt\tag\CompoundTag; +use pocketmine\utils\BinaryDataException; use function file_get_contents; use function getmypid; use function json_decode; @@ -51,8 +54,29 @@ final class RuntimeBlockMapping{ $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); $compressedTable = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.json"), true); + + try{ + /** @var CompoundTag $tag */ + $tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "runtime_block_states.dat")); + }catch(BinaryDataException $e){ + throw new \RuntimeException("", 0, $e); + } + $decompressed = []; + $states = $tag->getListTag("Palette"); + foreach($states as $state){ + /** @var CompoundTag $state */ + $block = $state->getCompoundTag("block"); + $decompressed[] = [ + "name" => $block->getString("name"), + "states" => $block->getCompoundTag("states"), + "data" => $state->getShort("meta"), + "legacy_id" => $state->getShort("id"), + ]; + } + + /* foreach($compressedTable as $prefix => $entries){ foreach($entries as $shortStringId => $states){ foreach($states as $state){ @@ -63,8 +87,7 @@ final class RuntimeBlockMapping{ "legacy_id" => $legacyIdMap[$name] ]; } - } - } + }*/ self::$bedrockKnownStates = self::randomizeTable($decompressed); foreach(self::$bedrockKnownStates as $k => $obj){ diff --git a/src/pocketmine/resources/runtime_block_states.dat b/src/pocketmine/resources/runtime_block_states.dat new file mode 100644 index 0000000000000000000000000000000000000000..32c993949174a90f34b2e4003f614796a4588f84 GIT binary patch literal 208936 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 literal 0 HcmV?d00001 diff --git a/src/pocketmine/utils/SerializedImage.php b/src/pocketmine/utils/SerializedImage.php new file mode 100644 index 000000000..6996e2208 --- /dev/null +++ b/src/pocketmine/utils/SerializedImage.php @@ -0,0 +1,67 @@ +height = $height; + $this->width = $width; + $this->data = $data; + } + + public static function fromLegacy(string $data) : SerializedImage{ + switch(strlen($data)){ + case 64 * 32 * 4: + return new self(64, 32, $data); + case 64 * 64 * 4: + return new self(64, 64, $data); + case 128 * 64 * 4: + return new self(128, 64, $data); + case 128 * 128 * 4: + return new self(128, 128, $data); + } + + throw new \InvalidArgumentException("Unknown size"); + } + + public function getHeight() : int{ + return $this->height; + } + + public function getWidth() : int{ + return $this->width; + } + + public function getData() : string{ + return $this->data; + } +} \ No newline at end of file diff --git a/src/pocketmine/utils/SkinAnimation.php b/src/pocketmine/utils/SkinAnimation.php new file mode 100644 index 000000000..025aee3d6 --- /dev/null +++ b/src/pocketmine/utils/SkinAnimation.php @@ -0,0 +1,52 @@ +image = $image; + $this->type = $type; + $this->frames = $frames; + } + + public function getImage() : SerializedImage{ + return $this->image; + } + + public function getType() : int{ + return $this->type; + } + + public function getFrames() : float{ + return $this->frames; + } +} \ No newline at end of file From 5ebe9859e9469c4d2110718d05c37018dce87bf8 Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 4 Nov 2019 21:47:44 -0500 Subject: [PATCH 02/95] Moved runtime_block_states to vanilla --- ...ock_states.dat => runtime_block_states.dat | Bin .../network/mcpe/protocol/StartGamePacket.php | 10 +------ .../protocol/types/RuntimeBlockMapping.php | 25 +++++------------- 3 files changed, 7 insertions(+), 28 deletions(-) rename src/pocketmine/resources/runtime_block_states.dat => runtime_block_states.dat (100%) diff --git a/src/pocketmine/resources/runtime_block_states.dat b/runtime_block_states.dat similarity index 100% rename from src/pocketmine/resources/runtime_block_states.dat rename to runtime_block_states.dat diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php index af0f47541..a251112aa 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -316,16 +316,8 @@ class StartGamePacket extends DataPacket{ $states->push($state); } ($stream = new NetworkLittleEndianNBTStream())->writeTag($states); - return $stream->buffer; - /*$stream = new NetworkBinaryStream(); - $stream->putUnsignedVarInt(count($table)); - foreach($table as $v){ - $stream->putString($v["name"]); - $stream->putLShort($v["data"]); - $stream->putLShort($v["legacy_id"]); - } - return $stream->getBuffer();*/ + return $stream->buffer; } private static function serializeItemTable(array $table) : string{ diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php index 956b1d922..bc8d7df41 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -53,11 +53,9 @@ final class RuntimeBlockMapping{ public static function init() : void{ $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); - $compressedTable = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.json"), true); - try{ /** @var CompoundTag $tag */ - $tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "runtime_block_states.dat")); + $tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/runtime_block_states.dat")); }catch(BinaryDataException $e){ throw new \RuntimeException("", 0, $e); } @@ -65,29 +63,18 @@ final class RuntimeBlockMapping{ $decompressed = []; $states = $tag->getListTag("Palette"); + /** @var CompoundTag $state */ foreach($states as $state){ - /** @var CompoundTag $state */ $block = $state->getCompoundTag("block"); + $name = $block->getString("name"); $decompressed[] = [ - "name" => $block->getString("name"), + "name" => $name, "states" => $block->getCompoundTag("states"), "data" => $state->getShort("meta"), - "legacy_id" => $state->getShort("id"), + "legacy_id" => $legacyIdMap[$name] ]; - } - /* - foreach($compressedTable as $prefix => $entries){ - foreach($entries as $shortStringId => $states){ - foreach($states as $state){ - $name = "$prefix:$shortStringId"; - $decompressed[] = [ - "name" => $name, - "data" => $state, - "legacy_id" => $legacyIdMap[$name] - ]; - } - }*/ + } self::$bedrockKnownStates = self::randomizeTable($decompressed); foreach(self::$bedrockKnownStates as $k => $obj){ From 3b7ded0ba3fe6ba96c9aa26c057ea0dd519b80f4 Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 20:25:45 -0500 Subject: [PATCH 03/95] Fixed changing skins crashing the server --- src/pocketmine/network/mcpe/protocol/PlayerListPacket.php | 3 ++- src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php index 160154c77..327fa4103 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php @@ -66,8 +66,9 @@ class PlayerListPacket extends DataPacket{ $skinId = $this->getString(); $skinResourcePatch = $this->getString(); $skinData = $this->getImage(); + $animationCount = $this->getLInt(); $animations = []; - for($i = 0; $i < $this->getLInt(); ++$i){ + for($i = 0; $i < $animationCount; ++$i){ $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); } $capeData = $this->getImage(); diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index a6da88129..0c8ac46b2 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -45,8 +45,9 @@ class PlayerSkinPacket extends DataPacket{ $skinId = $this->getString(); $skinResourcePatch = $this->getString(); $skinData = $this->getImage(); + $animationCount = $this->getLInt(); $animations = []; - for($i = 0; $i < $this->getLInt(); ++$i){ + for($i = 0; $i < $animationCount; ++$i){ $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); } $capeData = $this->getImage(); From 5b11ddee35fa366eb1f6908bcbb4097e4d7ad539 Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 20:30:41 -0500 Subject: [PATCH 04/95] Clean up duplicated skin entries --- .../network/mcpe/NetworkBinaryStream.php | 53 +++++++++++++++++-- .../mcpe/protocol/PlayerListPacket.php | 43 +-------------- .../mcpe/protocol/PlayerSkinPacket.php | 41 +------------- 3 files changed, 53 insertions(+), 84 deletions(-) diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 398357f55..5b87a2d26 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -27,6 +27,7 @@ namespace pocketmine\network\mcpe; use pocketmine\entity\Attribute; use pocketmine\entity\Entity; +use pocketmine\entity\Skin; use pocketmine\item\Durable; use pocketmine\item\Item; use pocketmine\item\ItemFactory; @@ -40,6 +41,7 @@ use pocketmine\network\mcpe\protocol\types\EntityLink; use pocketmine\network\mcpe\protocol\types\StructureSettings; use pocketmine\utils\BinaryStream; use pocketmine\utils\SerializedImage; +use pocketmine\utils\SkinAnimation; use pocketmine\utils\UUID; use function count; use function strlen; @@ -75,10 +77,47 @@ class NetworkBinaryStream extends BinaryStream{ $this->putLInt($uuid->getPart(2)); } - public function putImage(SerializedImage $image) : void{ - $this->putLInt($image->getWidth()); - $this->putLInt($image->getHeight()); - $this->putString($image->getData()); + public function getSkin() : Skin{ + $skinId = $this->getString(); + $skinResourcePatch = $this->getString(); + $skinData = $this->getImage(); + $animationCount = $this->getLInt(); + $animations = []; + for($i = 0; $i < $animationCount; ++$i){ + $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); + } + $capeData = $this->getImage(); + $geometryData = $this->getString(); + $animationData = $this->getString(); + $premium = $this->getBool(); + $persona = $this->getBool(); + $capeOnClassic = $this->getBool(); + $capeId = $this->getString(); + $fullSkinId = $this->getString(); + + return new Skin( + $skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId + ); + } + + public function putSkin(Skin $skin){ + $this->putString($skin->getSkinId()); + $this->putString($skin->getSkinResourcePatch()); + $this->putImage($skin->getSkinData()); + $this->putLInt(count($skin->getAnimations())); + foreach($skin->getAnimations() as $animation){ + $this->putImage($animation->getImage()); + $this->putLInt($animation->getType()); + $this->putLFloat($animation->getFrames()); + } + $this->putImage($skin->getCapeData()); + $this->putString($skin->getGeometryData()); + $this->putString($skin->getAnimationData()); + $this->putBool($skin->getPremium()); + $this->putBool($skin->getPersona()); + $this->putBool($skin->getCapeOnClassic()); + $this->putString($skin->getCapeId()); + $this->putString($skin->getFullSkinId()); } public function getImage() : SerializedImage{ @@ -88,6 +127,12 @@ class NetworkBinaryStream extends BinaryStream{ return new SerializedImage($height, $width, $data); } + public function putImage(SerializedImage $image) : void{ + $this->putLInt($image->getWidth()); + $this->putLInt($image->getHeight()); + $this->putString($image->getData()); + } + public function getSlot() : Item{ $id = $this->getVarInt(); if($id === 0){ diff --git a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php index 327fa4103..d3f765dd1 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php @@ -62,28 +62,7 @@ class PlayerListPacket extends DataPacket{ $entry->xboxUserId = $this->getString(); $entry->platformChatId = $this->getString(); $entry->buildPlatform = $this->getLInt(); - - $skinId = $this->getString(); - $skinResourcePatch = $this->getString(); - $skinData = $this->getImage(); - $animationCount = $this->getLInt(); - $animations = []; - for($i = 0; $i < $animationCount; ++$i){ - $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); - } - $capeData = $this->getImage(); - $geometryData = $this->getString(); - $animationData = $this->getString(); - $premium = $this->getBool(); - $persona = $this->getBool(); - $capeOnClassic = $this->getBool(); - $capeId = $this->getString(); - $fullSkinId = $this->getString(); - - $entry->skin = new Skin( - $skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId - ); - + $entry->skin = $this->getSkin(); $entry->isTeacher = $this->getBool(); $entry->isHost = $this->getBool(); }else{ @@ -105,25 +84,7 @@ class PlayerListPacket extends DataPacket{ $this->putString($entry->xboxUserId); $this->putString($entry->platformChatId); $this->putLInt($entry->buildPlatform); - - $this->putString($entry->skin->getSkinId()); - $this->putString($entry->skin->getSkinResourcePatch()); - $this->putImage($entry->skin->getSkinData()); - $this->putLInt(count($entry->skin->getAnimations())); - foreach($entry->skin->getAnimations() as $animation){ - $this->putImage($animation->getImage()); - $this->putLInt($animation->getType()); - $this->putLFloat($animation->getFrames()); - } - $this->putImage($entry->skin->getCapeData()); - $this->putString($entry->skin->getGeometryData()); - $this->putString($entry->skin->getAnimationData()); - $this->putBool($entry->skin->getPremium()); - $this->putBool($entry->skin->getPersona()); - $this->putBool($entry->skin->getCapeOnClassic()); - $this->putString($entry->skin->getCapeId()); - $this->putString($entry->skin->getFullSkinId()); - + $this->putSkin($entry->skin); $this->putBool($entry->isTeacher); $this->putBool($entry->isHost); }else{ diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index 0c8ac46b2..4da5deeef 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -41,49 +41,12 @@ class PlayerSkinPacket extends DataPacket{ protected function decodePayload(){ $this->uuid = $this->getUUID(); - - $skinId = $this->getString(); - $skinResourcePatch = $this->getString(); - $skinData = $this->getImage(); - $animationCount = $this->getLInt(); - $animations = []; - for($i = 0; $i < $animationCount; ++$i){ - $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); - } - $capeData = $this->getImage(); - $geometryData = $this->getString(); - $animationData = $this->getString(); - $premium = $this->getBool(); - $persona = $this->getBool(); - $capeOnClassic = $this->getBool(); - $capeId = $this->getString(); - $fullSkinId = $this->getString(); - - $this->skin = new Skin( - $skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId - ); + $this->skin = $this->getSkin(); } protected function encodePayload(){ $this->putUUID($this->uuid); - - $this->putString($this->skin->getSkinId()); - $this->putString($this->skin->getSkinResourcePatch()); - $this->putImage($this->skin->getSkinData()); - $this->putLInt(count($this->skin->getAnimations())); - foreach($this->skin->getAnimations() as $animation){ - $this->putImage($animation->getImage()); - $this->putLInt($animation->getType()); - $this->putLFloat($animation->getFrames()); - } - $this->putImage($this->skin->getCapeData()); - $this->putString($this->skin->getGeometryData()); - $this->putString($this->skin->getAnimationData()); - $this->putBool($this->skin->getPremium()); - $this->putBool($this->skin->getPersona()); - $this->putBool($this->skin->getCapeOnClassic()); - $this->putString($this->skin->getCapeId()); - $this->putString($this->skin->getFullSkinId()); + $this->putSkin($this->skin); } public function handle(NetworkSession $session) : bool{ From 080209c469202c4153ba8f2da4ce5fd9f2f31b71 Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 20:35:33 -0500 Subject: [PATCH 05/95] Fixed persona skins being messed up on join --- src/pocketmine/Player.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 11fac9fb5..498763309 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1917,19 +1917,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $animations = []; foreach($packet->clientData["AnimatedImageData"] as $animatedData){ - $animations[] = new SkinAnimation(new SerializedImage($animatedData["ImageWidth"], $animatedData["ImageHeight"], base64_decode($animatedData["Image"])), $animatedData["Type"], $animatedData["Frames"]); + $animations[] = new SkinAnimation(new SerializedImage($animatedData["ImageHeight"], $animatedData["ImageWidth"], base64_decode($animatedData["Image"])), $animatedData["Type"], $animatedData["Frames"]); } $skin = new Skin( $packet->clientData["SkinId"], base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), - new SerializedImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData" ?? ""])), //SerializedImage + new SerializedImage((int)$packet->clientData["SkinImageHeight"], (int)$packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"] ?? "")), $animations, new SerializedImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")), base64_decode($packet->clientData["SkinGeometryData"] ?? ""), - base64_decode($packet->clientData["SkinAnimationData"] ?? ""), - (bool) $packet->clientData["PremiumSkin"] ?? false, - (bool) $packet->clientData["PersonaSkin"] ?? false, - (bool) $packet->clientData["CapeOnClassicSkin"] ?? false, + base64_decode($packet->clientData["AnimationData"] ?? ""), + (bool)$packet->clientData["PremiumSkin"] ?? false, + (bool)$packet->clientData["PersonaSkin"] ?? false, + (bool)$packet->clientData["CapeOnClassicSkin"] ?? false, $packet->clientData["CapeId"] ?? "" ); From 8b912c136302c6ca69611ac82c9b641bdae4bed3 Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 20:59:48 -0500 Subject: [PATCH 06/95] Removed some useless casts --- src/pocketmine/Player.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 498763309..441b3b474 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1922,14 +1922,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $skin = new Skin( $packet->clientData["SkinId"], base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), - new SerializedImage((int)$packet->clientData["SkinImageHeight"], (int)$packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"] ?? "")), + new SerializedImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"] ?? "")), $animations, new SerializedImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")), base64_decode($packet->clientData["SkinGeometryData"] ?? ""), base64_decode($packet->clientData["AnimationData"] ?? ""), - (bool)$packet->clientData["PremiumSkin"] ?? false, - (bool)$packet->clientData["PersonaSkin"] ?? false, - (bool)$packet->clientData["CapeOnClassicSkin"] ?? false, + $packet->clientData["PremiumSkin"] ?? false, + $packet->clientData["PersonaSkin"] ?? false, + $packet->clientData["CapeOnClassicSkin"] ?? false, $packet->clientData["CapeId"] ?? "" ); From 4ea907ae1ace61438095fa0440de01b5a3d172d3 Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 21:01:01 -0500 Subject: [PATCH 07/95] Start saving new skin data --- src/pocketmine/entity/Human.php | 35 +++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 0a9def938..2955d1f88 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -42,6 +42,7 @@ use pocketmine\item\Totem; use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\ByteArrayTag; +use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; @@ -54,6 +55,8 @@ use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\Player; +use pocketmine\utils\SerializedImage; +use pocketmine\utils\SkinAnimation; use pocketmine\utils\UUID; use function array_filter; use function array_merge; @@ -117,10 +120,16 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ protected static function deserializeSkinNBT(CompoundTag $skinTag) : Skin{ $skin = new Skin( $skinTag->getString("Name"), - $skinTag->hasTag("Data", StringTag::class) ? $skinTag->getString("Data") : $skinTag->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM) - $skinTag->getByteArray("CapeData", ""), - $skinTag->getString("GeometryName", ""), - $skinTag->getByteArray("GeometryData", "") + $skinTag->getString("SkinResourcePatch", ""), + new SerializedImage($skinTag->getInt("SkinImageHeight"), $skinTag->getInt("SkinImageWidth"), $skinTag->getByteArray("Data")), + "", //TODO: animations + new SerializedImage($skinTag->getInt("CapeImageHeight"), $skinTag->getInt("CapeImageWidth"), $skinTag->getByteArray("CapeData")), + $skinTag->getByteArray("GeometryData", ""), + $skinTag->getByteArray("AnimationData", ""), + $skinTag->getByte("PremiumSkin") === 1, + $skinTag->getByte("PersonaSkin") === 1, + $skinTag->getByte("CapeOnClassic") === 1, + $skinTag->getString("CapeId", "") ); $skin->validate(); return $skin; @@ -826,13 +835,23 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ $this->namedtag->setTag(new ListTag("EnderChestInventory", $items, NBT::TAG_Compound)); } + //TODO: Save animations if($this->skin !== null){ $this->namedtag->setTag(new CompoundTag("Skin", [ new StringTag("Name", $this->skin->getSkinId()), - new ByteArrayTag("Data", $this->skin->getSkinData()), - new ByteArrayTag("CapeData", $this->skin->getCapeData()), - new StringTag("GeometryName", $this->skin->getGeometryName()), - new ByteArrayTag("GeometryData", $this->skin->getGeometryData()) + new StringTag("SkinResourcePatch", $this->skin->getSkinResourcePatch()), + new ByteArrayTag("Data", $this->skin->getSkinData()->getData()), + new IntTag("SkinImageHeight", $this->skin->getSkinData()->getHeight()), + new IntTag("SkinImageWidth", $this->skin->getSkinData()->getWidth()), + new ByteArrayTag("CapeData", $this->skin->getCapeData()->getData()), + new IntTag("CapeImageHeight", $this->skin->getCapeData()->getHeight()), + new IntTag("CapeImageWidth", $this->skin->getCapeData()->getWidth()), + new ByteArrayTag("GeometryData", $this->skin->getGeometryData()), + new ByteArrayTag("AnimationData", $this->skin->getAnimationData()), + new ByteTag("PremiumSkin", $this->skin->getPremium() ? 1 : 0), + new ByteTag("PersonaSkin", $this->skin->getPersona() ? 1 : 0), + new ByteTag("CapeOnClassic", $this->skin->getCapeOnClassic() ? 1 : 0), + new StringTag("CapeId", $this->skin->getCapeId()) ])); } } From 4e9a2b6d8ca6bed01b0cb9e6f0a0049a40c9a70c Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 21:19:49 -0500 Subject: [PATCH 08/95] Added CompletedUsingItemPacket --- .../network/mcpe/NetworkSession.php | 5 ++ .../protocol/CompletedUsingItemPacket.php | 68 +++++++++++++++++++ .../network/mcpe/protocol/PacketPool.php | 2 + 3 files changed, 75 insertions(+) create mode 100644 src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index 338034558..c20a53275 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; +use pocketmine\network\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\ActorEventPacket; use pocketmine\network\mcpe\protocol\ActorFallPacket; use pocketmine\network\mcpe\protocol\ActorPickRequestPacket; @@ -694,4 +695,8 @@ abstract class NetworkSession{ public function handleClientCacheMissResponse(ClientCacheMissResponsePacket $packet) : bool{ return false; } + + public function handleCompletedUsingItem(CompletedUsingItemPacket $packet) : bool{ + return false; + } } diff --git a/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php b/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php new file mode 100644 index 000000000..2ec976b75 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php @@ -0,0 +1,68 @@ +itemId = $this->getShort(); + $this->action = $this->getLInt(); + } + + public function encodePayload(){ + $this->putShort($this->itemId); + $this->putLInt($this->action); + } + + public function handle(NetworkSession $session) : bool{ + return $session->handleCompletedUsingItem($this); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/PacketPool.php b/src/pocketmine/network/mcpe/protocol/PacketPool.php index c67e031dc..a5436a085 100644 --- a/src/pocketmine/network/mcpe/protocol/PacketPool.php +++ b/src/pocketmine/network/mcpe/protocol/PacketPool.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol; +use pocketmine\network\CompletedUsingItemPacket; use pocketmine\utils\Binary; use pocketmine\utils\BinaryDataException; @@ -166,6 +167,7 @@ class PacketPool{ static::registerPacket(new UpdateBlockPropertiesPacket()); static::registerPacket(new ClientCacheBlobStatusPacket()); static::registerPacket(new ClientCacheMissResponsePacket()); + static::registerPacket(new CompletedUsingItemPacket()); } /** From 75742b487f09e332f6b829df993912fd3cf02016 Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 21:56:20 -0500 Subject: [PATCH 09/95] Fixed stupid mistake --- src/pocketmine/network/mcpe/NetworkSession.php | 2 +- .../network/mcpe/protocol/CompletedUsingItemPacket.php | 6 ++---- src/pocketmine/network/mcpe/protocol/PacketPool.php | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index c20a53275..777d55086 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; -use pocketmine\network\CompletedUsingItemPacket; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\ActorEventPacket; use pocketmine\network\mcpe\protocol\ActorFallPacket; use pocketmine\network\mcpe\protocol\ActorPickRequestPacket; diff --git a/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php b/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php index 2ec976b75..9a4f96d70 100644 --- a/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php @@ -21,11 +21,9 @@ declare(strict_types=1); -namespace pocketmine\network; +namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -use pocketmine\network\mcpe\protocol\DataPacket; -use pocketmine\network\mcpe\protocol\ProtocolInfo; class CompletedUsingItemPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::COMPLETED_USING_ITEM_PACKET; @@ -46,7 +44,7 @@ class CompletedUsingItemPacket extends DataPacket{ public const ACTION_RETRIEVED = 12; public const ACTION_DYED = 13; public const ACTION_TRADED = 14; - + /** @var int */ public $itemId; /** @var int */ diff --git a/src/pocketmine/network/mcpe/protocol/PacketPool.php b/src/pocketmine/network/mcpe/protocol/PacketPool.php index a5436a085..61fecb00a 100644 --- a/src/pocketmine/network/mcpe/protocol/PacketPool.php +++ b/src/pocketmine/network/mcpe/protocol/PacketPool.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol; -use pocketmine\network\CompletedUsingItemPacket; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\utils\Binary; use pocketmine\utils\BinaryDataException; From 17a17c31f32faecd55dd33ac977630beddb77f5b Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 22:25:11 -0500 Subject: [PATCH 10/95] Fixed items, but I'm not so sure how well this'll work out, it'll need improved in the future --- src/pocketmine/Player.php | 45 +++++++++++--------------- src/pocketmine/item/Bow.php | 5 +++ src/pocketmine/item/Bucket.php | 34 +++++++++++++++++++ src/pocketmine/item/Consumable.php | 1 + src/pocketmine/item/Food.php | 33 +++++++++++++++++++ src/pocketmine/item/Item.php | 9 ++++++ src/pocketmine/item/Potion.php | 28 ++++++++++++++++ src/pocketmine/item/ProjectileItem.php | 5 +++ 8 files changed, 134 insertions(+), 26 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 441b3b474..b7fa2dbb8 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -80,6 +80,7 @@ use pocketmine\item\Consumable; use pocketmine\item\Durable; use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\item\enchantment\MeleeWeaponEnchantment; +use pocketmine\item\Food; use pocketmine\item\Item; use pocketmine\item\WritableBook; use pocketmine\item\WrittenBook; @@ -98,6 +99,7 @@ use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\DoubleTag; use pocketmine\nbt\tag\ListTag; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\PlayerNetworkSessionAdapter; use pocketmine\network\mcpe\protocol\ActorEventPacket; use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; @@ -2532,7 +2534,18 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - $this->setUsingItem(true); + if($this->startAction === -1){ + $this->setUsingItem(true); + return true; + } + + $this->setUsingItem(false); + if($item->onUse($this)){ + $pk = new CompletedUsingItemPacket(); + $pk->itemId = $item->getId(); + $pk->action = $item->getCompletedAction(); + $this->sendDataPacket($pk); + } return true; default: @@ -2650,6 +2663,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if($item->onReleaseUsing($this)){ $this->resetItemCooldown($item); $this->inventory->setItemInHand($item); + + $pk = new CompletedUsingItemPacket(); + $pk->itemId = $item->getId(); + $pk->action = $item->getCompletedAction(); + $this->sendDataPacket($pk); } }else{ break; @@ -2657,31 +2675,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME: - $slot = $this->inventory->getItemInHand(); - - if($slot instanceof Consumable){ - $ev = new PlayerItemConsumeEvent($this, $slot); - if($this->hasItemCooldown($slot)){ - $ev->setCancelled(); - } - $ev->call(); - - if($ev->isCancelled() or !$this->consumeObject($slot)){ - $this->inventory->sendContents($this); - return true; - } - - $this->resetItemCooldown($slot); - - if($this->isSurvival()){ - $slot->pop(); - $this->inventory->setItemInHand($slot); - $this->inventory->addItem($slot->getResidue()); - } - - return true; - } - break; default: break; diff --git a/src/pocketmine/item/Bow.php b/src/pocketmine/item/Bow.php index 48428b576..dbce96828 100644 --- a/src/pocketmine/item/Bow.php +++ b/src/pocketmine/item/Bow.php @@ -29,6 +29,7 @@ use pocketmine\entity\projectile\Projectile; use pocketmine\event\entity\EntityShootBowEvent; use pocketmine\event\entity\ProjectileLaunchEvent; use pocketmine\item\enchantment\Enchantment; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\Player; use function intdiv; @@ -47,6 +48,10 @@ class Bow extends Tool{ return 385; } + public function getCompletedAction(){ + return CompletedUsingItemPacket::ACTION_SHOOT; + } + public function onReleaseUsing(Player $player) : bool{ if($player->isSurvival() and !$player->getInventory()->contains(ItemFactory::get(Item::ARROW, 0, 1))){ $player->getInventory()->sendContents($player); diff --git a/src/pocketmine/item/Bucket.php b/src/pocketmine/item/Bucket.php index 3c7834350..89612a438 100644 --- a/src/pocketmine/item/Bucket.php +++ b/src/pocketmine/item/Bucket.php @@ -30,7 +30,9 @@ use pocketmine\block\Liquid; use pocketmine\entity\Living; use pocketmine\event\player\PlayerBucketEmptyEvent; use pocketmine\event\player\PlayerBucketFillEvent; +use pocketmine\event\player\PlayerItemConsumeEvent; use pocketmine\math\Vector3; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\Player; class Bucket extends Item implements Consumable{ @@ -114,4 +116,36 @@ class Bucket extends Item implements Consumable{ public function onConsume(Living $consumer){ $consumer->removeAllEffects(); } + + public function getCompletedAction(){ + if($this->canBeConsumed()){ + return CompletedUsingItemPacket::ACTION_CONSUME; + }else{ + return CompletedUsingItemPacket::ACTION_POUR_BUCKET; + } + } + + public function onUse(Player $player) : bool{ + if($this->canBeConsumed()){ + $slot = $player->getInventory()->getItemInHand(); + + $ev = new PlayerItemConsumeEvent($player, $slot); + $ev->call(); + + /** @var $slot Consumable */ + if($ev->isCancelled() or !$player->consumeObject($slot)){ + $player->getInventory()->sendContents($player); + return true; + } + + if($player->isSurvival()){ + $slot->pop(); + $player->getInventory()->setItemInHand($slot); + $player->getInventory()->addItem($slot->getResidue()); + } + + return true; + } + return false; + } } diff --git a/src/pocketmine/item/Consumable.php b/src/pocketmine/item/Consumable.php index 5977c37f4..2e9606882 100644 --- a/src/pocketmine/item/Consumable.php +++ b/src/pocketmine/item/Consumable.php @@ -26,6 +26,7 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\entity\EffectInstance; use pocketmine\entity\Living; +use pocketmine\Player; /** * Interface implemented by objects that can be consumed by mobs. diff --git a/src/pocketmine/item/Food.php b/src/pocketmine/item/Food.php index 36f4cee4f..ac1ca4d2e 100644 --- a/src/pocketmine/item/Food.php +++ b/src/pocketmine/item/Food.php @@ -24,6 +24,10 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\entity\Living; +use pocketmine\event\player\PlayerItemConsumeEvent; +use pocketmine\math\Vector3; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; +use pocketmine\Player; abstract class Food extends Item implements FoodSource{ public function requiresHunger() : bool{ @@ -41,7 +45,36 @@ abstract class Food extends Item implements FoodSource{ return []; } + public function getCompletedAction(){ + return CompletedUsingItemPacket::ACTION_EAT; + } + + public function onUse(Player $player) : bool{ + $slot = $player->getInventory()->getItemInHand(); + + $ev = new PlayerItemConsumeEvent($player, $slot); + $ev->call(); + + /** @var $slot Consumable */ + if($ev->isCancelled() or !$player->consumeObject($slot)){ + $player->getInventory()->sendContents($player); + return true; + } + + if($player->isSurvival()){ + $slot->pop(); + $player->getInventory()->setItemInHand($slot); + $player->getInventory()->addItem($slot->getResidue()); + } + + return true; + } + public function onConsume(Living $consumer){ } + + public function onClickAir(Player $player, Vector3 $directionVector) : bool{ + return true; + } } diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index 664e07bba..bc02fd9f1 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -41,6 +41,7 @@ use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\NamedTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\Player; use pocketmine\utils\Binary; use function array_map; @@ -848,6 +849,14 @@ class Item implements ItemIds, \JsonSerializable{ return 0; } + public function getCompletedAction(){ + return CompletedUsingItemPacket::ACTION_UNKNOWN; + } + + public function onUse(Player $player) : bool{ + return false; + } + /** * Compares an Item to this Item and check if they match. * diff --git a/src/pocketmine/item/Potion.php b/src/pocketmine/item/Potion.php index b2b927d54..64a0246c4 100644 --- a/src/pocketmine/item/Potion.php +++ b/src/pocketmine/item/Potion.php @@ -26,6 +26,9 @@ namespace pocketmine\item; use pocketmine\entity\Effect; use pocketmine\entity\EffectInstance; use pocketmine\entity\Living; +use pocketmine\event\player\PlayerItemConsumeEvent; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; +use pocketmine\Player; class Potion extends Item implements Consumable{ @@ -235,4 +238,29 @@ class Potion extends Item implements Consumable{ public function getResidue(){ return ItemFactory::get(Item::GLASS_BOTTLE); } + + public function getCompletedAction(){ + return CompletedUsingItemPacket::ACTION_CONSUME; + } + + public function onUse(Player $player) : bool{ + $slot = $player->getInventory()->getItemInHand(); + + $ev = new PlayerItemConsumeEvent($player, $slot); + $ev->call(); + + /** @var $slot Consumable */ + if($ev->isCancelled() or !$player->consumeObject($slot)){ + $player->getInventory()->sendContents($player); + return true; + } + + if($player->isSurvival()){ + $slot->pop(); + $player->getInventory()->setItemInHand($slot); + $player->getInventory()->addItem($slot->getResidue()); + } + + return true; + } } diff --git a/src/pocketmine/item/ProjectileItem.php b/src/pocketmine/item/ProjectileItem.php index 88bebf3e1..3e824316c 100644 --- a/src/pocketmine/item/ProjectileItem.php +++ b/src/pocketmine/item/ProjectileItem.php @@ -29,6 +29,7 @@ use pocketmine\entity\projectile\Projectile; use pocketmine\event\entity\ProjectileLaunchEvent; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\Player; @@ -47,6 +48,10 @@ abstract class ProjectileItem extends Item{ } + public function getCompletedAction(){ + return CompletedUsingItemPacket::ACTION_THROW; + } + public function onClickAir(Player $player, Vector3 $directionVector) : bool{ $nbt = Entity::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch); $this->addExtraTags($nbt); From 07f19dd4a101abfadac58b1db8f08ff6c72d4b55 Mon Sep 17 00:00:00 2001 From: Stephen Date: Tue, 5 Nov 2019 22:33:12 -0500 Subject: [PATCH 11/95] Fixed respawning --- src/pocketmine/Player.php | 10 +++++++++- .../network/mcpe/PlayerNetworkSessionAdapter.php | 5 +++++ src/pocketmine/network/mcpe/protocol/RespawnPacket.php | 10 +++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index b7fa2dbb8..94cd39bc4 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1114,7 +1114,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - protected function sendRespawnPacket(Vector3 $pos, int $respawnState = 1){ + protected function sendRespawnPacket(Vector3 $pos, int $respawnState = RespawnPacket::SEARCHING_FOR_SPAWN){ $pk = new RespawnPacket(); $pk->position = $pos->add(0, $this->baseOffset, 0); $pk->respawnState = $respawnState; @@ -2918,6 +2918,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } + public function handleRespawn(RespawnPacket $packet) : bool{ + if(!$this->isAlive() && $packet->respawnState === RespawnPacket::CLIENT_READY_TO_SPAWN){ + $this->sendRespawnPacket($this, RespawnPacket::READY_TO_SPAWN); + } + + return true; + } + /** * Drops an item on the ground in front of the player. Returns if the item drop was successful. * diff --git a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php index 914b8becc..7a41eb194 100644 --- a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php +++ b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php @@ -59,6 +59,7 @@ use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket; use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket; use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket; +use pocketmine\network\mcpe\protocol\RespawnPacket; use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket; use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket; use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket; @@ -181,6 +182,10 @@ class PlayerNetworkSessionAdapter extends NetworkSession{ return $this->player->handleAnimate($packet); } + public function handleRespawn(RespawnPacket $packet) : bool{ + return $this->player->handleRespawn($packet); + } + public function handleContainerClose(ContainerClosePacket $packet) : bool{ return $this->player->handleContainerClose($packet); } diff --git a/src/pocketmine/network/mcpe/protocol/RespawnPacket.php b/src/pocketmine/network/mcpe/protocol/RespawnPacket.php index 1f8cb4091..6705877e8 100644 --- a/src/pocketmine/network/mcpe/protocol/RespawnPacket.php +++ b/src/pocketmine/network/mcpe/protocol/RespawnPacket.php @@ -32,22 +32,26 @@ use pocketmine\network\mcpe\NetworkSession; class RespawnPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::RESPAWN_PACKET; + public const SEARCHING_FOR_SPAWN = 0; + public const READY_TO_SPAWN = 1; + public const CLIENT_READY_TO_SPAWN = 2; + /** @var Vector3 */ public $position; /** @var int */ - public $respawnState = 1; //TODO: Add + public $respawnState = self::SEARCHING_FOR_SPAWN; /** @var int */ public $entityRuntimeId; protected function decodePayload(){ $this->position = $this->getVector3(); - $this->respawnState = $this->getInt(); + $this->respawnState = $this->getByte(); $this->entityRuntimeId = $this->getEntityRuntimeId(); } protected function encodePayload(){ $this->putVector3($this->position); - $this->putInt($this->respawnState); + $this->putByte($this->respawnState); $this->putEntityRuntimeId($this->entityRuntimeId); } From 3511ac010dc1ef174a90738b679ae81991fd583b Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 6 Nov 2019 20:42:53 -0500 Subject: [PATCH 12/95] Fixed crafting --- src/pocketmine/Player.php | 25 ++-- src/pocketmine/block/CraftingTable.php | 2 +- src/pocketmine/inventory/BaseUIInventory.php | 116 ++++++++++++++++ src/pocketmine/inventory/CraftingGrid.php | 129 ++---------------- .../inventory/PlayerCursorInventory.php | 31 +---- .../inventory/PlayerUIInventory.php | 89 ++++++++++++ .../mcpe/protocol/types/ContainerIds.php | 2 +- 7 files changed, 234 insertions(+), 160 deletions(-) create mode 100644 src/pocketmine/inventory/BaseUIInventory.php create mode 100644 src/pocketmine/inventory/PlayerUIInventory.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 94cd39bc4..33d2d4d44 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -72,6 +72,7 @@ use pocketmine\form\FormValidationException; use pocketmine\inventory\CraftingGrid; use pocketmine\inventory\Inventory; use pocketmine\inventory\PlayerCursorInventory; +use pocketmine\inventory\PlayerUIInventory; use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\CraftingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; @@ -289,8 +290,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ protected $windowIndex = []; /** @var bool[] */ protected $permanentWindows = []; - /** @var PlayerCursorInventory */ - protected $cursorInventory; + /** @var PlayerUIInventory */ + protected $playerUIInventory; /** @var CraftingGrid */ protected $craftingGrid = null; /** @var CraftingTransaction|null */ @@ -2718,6 +2719,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if(!$this->spawned or !$this->isAlive()){ return true; } + if($packet->action === InteractPacket::ACTION_MOUSEOVER and $packet->target === 0){ //TODO HACK: silence useless spam (MCPE 1.8) //this packet is EXPECTED to only be sent when interacting with an entity, but due to some messy Mojang @@ -3552,7 +3554,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->removeAllWindows(true); $this->windows = []; $this->windowIndex = []; - $this->cursorInventory = null; + $this->playerUIInventory = null; $this->craftingGrid = null; if($this->constructed){ @@ -3817,16 +3819,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->addWindow($this->getArmorInventory(), ContainerIds::ARMOR, true); - $this->cursorInventory = new PlayerCursorInventory($this); - $this->addWindow($this->cursorInventory, ContainerIds::CURSOR, true); - - $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); + $this->playerUIInventory = new PlayerUIInventory($this); + $this->addWindow($this->playerUIInventory, ContainerIds::UI, true); + $this->craftingGrid = new CraftingGrid($this->playerUIInventory, CraftingGrid::SIZE_SMALL); //TODO: more windows } + public function getPlayerUIInventory() : PlayerUIInventory{ + return $this->playerUIInventory; + } + public function getCursorInventory() : PlayerCursorInventory{ - return $this->cursorInventory; + return $this->playerUIInventory->getCursorInventory(); } public function getCraftingGrid() : CraftingGrid{ @@ -3842,7 +3847,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public function doCloseInventory() : void{ /** @var Inventory[] $inventories */ - $inventories = [$this->craftingGrid, $this->cursorInventory]; + $inventories = [$this->craftingGrid, $this->getCursorInventory()]; foreach($inventories as $inventory){ $contents = $inventory->getContents(); if(count($contents) > 0){ @@ -3856,7 +3861,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){ - $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); + $this->craftingGrid = new CraftingGrid($this->playerUIInventory, CraftingGrid::SIZE_SMALL); } } diff --git a/src/pocketmine/block/CraftingTable.php b/src/pocketmine/block/CraftingTable.php index 05b52fd6a..9e416e872 100644 --- a/src/pocketmine/block/CraftingTable.php +++ b/src/pocketmine/block/CraftingTable.php @@ -49,7 +49,7 @@ 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)); + $player->setCraftingGrid(new CraftingGrid($player->getPlayerUIInventory(), CraftingGrid::SIZE_BIG)); } return true; diff --git a/src/pocketmine/inventory/BaseUIInventory.php b/src/pocketmine/inventory/BaseUIInventory.php new file mode 100644 index 000000000..3636e3ab7 --- /dev/null +++ b/src/pocketmine/inventory/BaseUIInventory.php @@ -0,0 +1,116 @@ +inventory = $inventory; + $this->size = $size; + $this->offset = $offset; + parent::__construct([], $size); + } + + public function getName() : string{ + return "UI"; + } + + public function getDefaultSize() : int{ + return 1; + } + + public function getSize() : int{ + return $this->size; + } + + public function getMaxStackSize() : int{ + return Inventory::MAX_STACK; + } + + public function setMaxStackSize(int $size) : void{ + } + + public function getTitle() : string{ + return ""; + } + + public function getItem(int $index) : Item{ + return $this->inventory->getItem($index + $this->offset); + } + + public function setItem(int $index, Item $item, bool $send = true) : bool{ + return $this->inventory->setItem($index + $this->offset, $item, $send); + } + + public function getContents(bool $includeEmpty = false) : array{ + $contents = []; + $air = null; + foreach($this->slots as $i => $slot){ + if($i < $this->offset || $i > $this->offset + $this->size){ + continue; + } + if($slot !== null){ + $contents[$i] = clone $slot; + }elseif($includeEmpty){ + $contents[$i] = $air ?? ($air = ItemFactory::get(Item::AIR, 0, 0)); + } + } + + return $contents; + } + + public function sendContents($target) : void{ + $this->inventory->sendContents($target); + } + + public function sendSlot(int $index, $target) : void{ + $this->inventory->sendSlot($index + $this->offset, $target); + } + + public function getViewers() : array{ + return $this->inventory->viewers; + } + + public function onOpen(Player $who) : void{} + + public function onClose(Player $who) : void{} + + public function open(Player $who) : bool{ + return false; + } + + public function close(Player $who) : void{} + + public function onSlotChange(int $index, Item $before, bool $send) : void{ + $this->inventory->onSlotChange($index + $this->offset, $before, $send); + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingGrid.php b/src/pocketmine/inventory/CraftingGrid.php index 4beb9ed71..b54068f4e 100644 --- a/src/pocketmine/inventory/CraftingGrid.php +++ b/src/pocketmine/inventory/CraftingGrid.php @@ -29,134 +29,23 @@ use function max; use function min; use const PHP_INT_MAX; -class CraftingGrid extends BaseInventory{ - public const SIZE_SMALL = 2; - public const SIZE_BIG = 3; +class CraftingGrid extends BaseUIInventory{ + public const SIZE_SMALL = 4; + public const SIZE_BIG = 9; - /** @var Player */ - protected $holder; /** @var int */ private $gridWidth; - /** @var int|null */ - private $startX; - /** @var int|null */ - private $xLen; - /** @var int|null */ - private $startY; - /** @var int|null */ - private $yLen; - - public function __construct(Player $holder, int $gridWidth){ - $this->holder = $holder; + public function __construct(PlayerUIInventory $inventory, int $gridWidth){ $this->gridWidth = $gridWidth; - parent::__construct(); + if($gridWidth === self::SIZE_SMALL){ + parent::__construct($inventory, $gridWidth, 28); + }elseif($gridWidth === self::SIZE_BIG){ + parent::__construct($inventory, $gridWidth, 32); + } } public function getGridWidth() : int{ return $this->gridWidth; } - - public function getDefaultSize() : int{ - return $this->getGridWidth() ** 2; - } - - public function setSize(int $size){ - throw new \BadMethodCallException("Cannot change the size of a crafting grid"); - } - - public function getName() : string{ - return "Crafting"; - } - - public function setItem(int $index, Item $item, bool $send = true) : bool{ - if(parent::setItem($index, $item, $send)){ - $this->seekRecipeBounds(); - - return true; - } - - return false; - } - - public function sendSlot(int $index, $target) : void{ - //we can't send a slot of a client-sided inventory window - } - - public function sendContents($target) : void{ - //no way to do this - } - - /** - * @return Player - */ - public function getHolder(){ - return $this->holder; - } - - private function seekRecipeBounds() : void{ - $minX = PHP_INT_MAX; - $maxX = 0; - - $minY = PHP_INT_MAX; - $maxY = 0; - - $empty = true; - - for($y = 0; $y < $this->gridWidth; ++$y){ - for($x = 0; $x < $this->gridWidth; ++$x){ - if(!$this->isSlotEmpty($y * $this->gridWidth + $x)){ - $minX = min($minX, $x); - $maxX = max($maxX, $x); - - $minY = min($minY, $y); - $maxY = max($maxY, $y); - - $empty = false; - } - } - } - - if(!$empty){ - $this->startX = $minX; - $this->xLen = $maxX - $minX + 1; - $this->startY = $minY; - $this->yLen = $maxY - $minY + 1; - }else{ - $this->startX = $this->xLen = $this->startY = $this->yLen = null; - } - } - - /** - * Returns the item at offset x,y, offset by where the starts of the recipe rectangle are. - * - * @param int $x - * @param int $y - * - * @return Item - */ - public function getIngredient(int $x, int $y) : Item{ - if($this->startX !== null and $this->startY !== null){ - return $this->getItem(($y + $this->startY) * $this->gridWidth + ($x + $this->startX)); - } - - throw new \InvalidStateException("No ingredients found in grid"); - } - - /** - * Returns the width of the recipe we're trying to craft, based on items currently in the grid. - * - * @return int - */ - public function getRecipeWidth() : int{ - return $this->xLen ?? 0; - } - - /** - * Returns the height of the recipe we're trying to craft, based on items currently in the grid. - * @return int - */ - public function getRecipeHeight() : int{ - return $this->yLen ?? 0; - } } diff --git a/src/pocketmine/inventory/PlayerCursorInventory.php b/src/pocketmine/inventory/PlayerCursorInventory.php index b967139f5..32939f7f7 100644 --- a/src/pocketmine/inventory/PlayerCursorInventory.php +++ b/src/pocketmine/inventory/PlayerCursorInventory.php @@ -23,34 +23,9 @@ declare(strict_types=1); namespace pocketmine\inventory; -use pocketmine\Player; +class PlayerCursorInventory extends BaseUIInventory{ -class PlayerCursorInventory extends BaseInventory{ - /** @var Player */ - protected $holder; - - public function __construct(Player $holder){ - $this->holder = $holder; - parent::__construct(); - } - - public function getName() : string{ - return "Cursor"; - } - - public function getDefaultSize() : int{ - return 1; - } - - public function setSize(int $size){ - throw new \BadMethodCallException("Cursor can only carry one item at a time"); - } - - /** - * This override is here for documentation and code completion purposes only. - * @return Player - */ - public function getHolder(){ - return $this->holder; + public function __construct(PlayerUIInventory $inventory){ + parent::__construct($inventory, 1, 0); } } diff --git a/src/pocketmine/inventory/PlayerUIInventory.php b/src/pocketmine/inventory/PlayerUIInventory.php new file mode 100644 index 000000000..a3e9e6454 --- /dev/null +++ b/src/pocketmine/inventory/PlayerUIInventory.php @@ -0,0 +1,89 @@ +holder = $holder; + $this->cursorInventory = new PlayerCursorInventory($this); + parent::__construct(); + } + + public function getCursorInventory(){ + return $this->cursorInventory; + } + + public function getName() : string{ + return "UI"; + } + + public function getDefaultSize() : int{ + return 51; + } + + public function getSize() : int{ + return 51; + } + + public function getHolder(){ + return $this->holder; + } + + public function setSize(int $size){} + + public function sendSlot(int $index, $target) : void{ + if($target instanceof Player){ + $target = [$target]; + } + $pk = new InventorySlotPacket(); + $pk->inventorySlot = $index; + $pk->item = $this->getItem($index); + foreach($target as $player){ + if($player === $this->getHolder()){ + $pk->windowId = ContainerIds::UI; + $player->dataPacket($pk); + }else{ + if(($id = $player->getWindowId($this)) === ContainerIds::NONE){ + $this->close($player); + continue; + } + $pk->windowId = $id; + $player->dataPacket($pk); + } + } + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php b/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php index 3b55065e5..0c9769f9f 100644 --- a/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php +++ b/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php @@ -34,6 +34,6 @@ interface ContainerIds{ public const CREATIVE = 121; public const HOTBAR = 122; public const FIXED_INVENTORY = 123; - public const CURSOR = 124; + public const UI = 124; } From af3c7b7c760e3e3c0baf4521c84be8d1d17ba2dd Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 6 Nov 2019 21:57:09 -0500 Subject: [PATCH 13/95] Updated ResourcePackType --- .../mcpe/protocol/types/ResourcePackType.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php b/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php index da4931b44..fd0a8e931 100644 --- a/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php +++ b/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php @@ -29,11 +29,14 @@ final class ResourcePackType{ //NOOP } - //Needs updated public const INVALID = 0; - public const RESOURCES = 1; - public const BEHAVIORS = 2; - public const WORLD_TEMPLATE = 3; - public const ADDON = 4; //scripts? - public const SKINS = 5; + public const ADDON = 1; + public const CACHED = 2; + public const COPY_PROTECTED = 3; + public const BEHAVIOR = 4; + public const PERSONA_PIECE = 5; + public const RESOURCES = 6; + public const SKINS = 7; + public const WORLD_TEMPLATE = 8; + public const COUNT = 9; } From 40b4166a6e41e3bc557db2cf29676028425a7100 Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 6 Nov 2019 22:30:05 -0500 Subject: [PATCH 14/95] created method sendCompletedUsingItemPacket for less duplication --- src/pocketmine/Player.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 33d2d4d44..82c0a6b80 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1115,6 +1115,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } + protected function sendCompletedUsingItemPacket(int $itemId, int $action){ + $pk = new CompletedUsingItemPacket(); + $pk->itemId = $itemId; + $pk->action = $action; + $this->sendDataPacket($pk); + } + protected function sendRespawnPacket(Vector3 $pos, int $respawnState = RespawnPacket::SEARCHING_FOR_SPAWN){ $pk = new RespawnPacket(); $pk->position = $pos->add(0, $this->baseOffset, 0); @@ -2542,10 +2549,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->setUsingItem(false); if($item->onUse($this)){ - $pk = new CompletedUsingItemPacket(); - $pk->itemId = $item->getId(); - $pk->action = $item->getCompletedAction(); - $this->sendDataPacket($pk); + $this->sendCompletedUsingItemPacket($item->getId(), $item->getCompletedAction()); } return true; @@ -2664,11 +2668,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if($item->onReleaseUsing($this)){ $this->resetItemCooldown($item); $this->inventory->setItemInHand($item); - - $pk = new CompletedUsingItemPacket(); - $pk->itemId = $item->getId(); - $pk->action = $item->getCompletedAction(); - $this->sendDataPacket($pk); + $this->sendCompletedUsingItemPacket($item->getId(), $item->getCompletedAction()); } }else{ break; From dbd36d66aec2a6092c346d1d3faceb2523d8bfba Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 6 Nov 2019 23:01:06 -0500 Subject: [PATCH 15/95] Cape data can be null --- src/pocketmine/entity/Skin.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index 497154094..7ba5f264d 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -138,6 +138,10 @@ class Skin{ * @return SerializedImage */ public function getCapeData() : SerializedImage{ + if($this->capeData === null){ + return new SerializedImage(0, 0, ""); + } + return $this->capeData; } From 7dec912d15af813e2f1e195f789309ea10f924ae Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 6 Nov 2019 23:22:41 -0500 Subject: [PATCH 16/95] Fixed FloatingTextParticles --- src/pocketmine/entity/Skin.php | 4 ++++ src/pocketmine/level/particle/FloatingTextParticle.php | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index 7ba5f264d..f06d4d0c5 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -75,6 +75,10 @@ class Skin{ $this->capeId = $capeId; } + public static function convertToLegacyName(string $name){ + return '{"geometry" : {"default" : "geometry.humanoid.custom"}}'; + } + /** * @deprecated * @return bool diff --git a/src/pocketmine/level/particle/FloatingTextParticle.php b/src/pocketmine/level/particle/FloatingTextParticle.php index d12282874..a8b859e20 100644 --- a/src/pocketmine/level/particle/FloatingTextParticle.php +++ b/src/pocketmine/level/particle/FloatingTextParticle.php @@ -32,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; +use pocketmine\utils\SerializedImage; use pocketmine\utils\UUID; use function str_repeat; @@ -96,7 +97,7 @@ class FloatingTextParticle extends Particle{ $add = new PlayerListPacket(); $add->type = PlayerListPacket::TYPE_ADD; - $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192)))]; + $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", Skin::convertToLegacyName("geometry.humanoid.custom"), SerializedImage::fromLegacy(str_repeat("\x00", 8192))))]; $p[] = $add; $pk = new AddPlayerPacket(); From 8c6189775bd8d404185951941192fde6fd310cd2 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:33:53 -0500 Subject: [PATCH 17/95] Removed root directory runtime states --- runtime_block_states.dat | Bin 208936 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 runtime_block_states.dat diff --git a/runtime_block_states.dat b/runtime_block_states.dat deleted file mode 100644 index 32c993949174a90f34b2e4003f614796a4588f84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208936 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 From b8d1d8f212bb40942f1a9349dcc8a91607e29dc1 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:43:53 -0500 Subject: [PATCH 18/95] Clean up unused imports --- src/pocketmine/item/Consumable.php | 1 - src/pocketmine/network/mcpe/NetworkSession.php | 3 +-- src/pocketmine/network/mcpe/protocol/PacketPool.php | 1 - src/pocketmine/network/mcpe/protocol/PlayerListPacket.php | 3 --- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pocketmine/item/Consumable.php b/src/pocketmine/item/Consumable.php index 2e9606882..5977c37f4 100644 --- a/src/pocketmine/item/Consumable.php +++ b/src/pocketmine/item/Consumable.php @@ -26,7 +26,6 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\entity\EffectInstance; use pocketmine\entity\Living; -use pocketmine\Player; /** * Interface implemented by objects that can be consumed by mobs. diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index 777d55086..207d0c6ca 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\ActorEventPacket; use pocketmine\network\mcpe\protocol\ActorFallPacket; use pocketmine\network\mcpe\protocol\ActorPickRequestPacket; @@ -55,6 +54,7 @@ use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket; use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket; use pocketmine\network\mcpe\protocol\CommandOutputPacket; use pocketmine\network\mcpe\protocol\CommandRequestPacket; +use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket; use pocketmine\network\mcpe\protocol\ContainerOpenPacket; use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; @@ -63,7 +63,6 @@ use pocketmine\network\mcpe\protocol\CraftingEventPacket; use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; use pocketmine\network\mcpe\protocol\EventPacket; -use pocketmine\network\mcpe\protocol\ExplodePacket; use pocketmine\network\mcpe\protocol\GameRulesChangedPacket; use pocketmine\network\mcpe\protocol\GuiDataPickItemPacket; use pocketmine\network\mcpe\protocol\HurtArmorPacket; diff --git a/src/pocketmine/network/mcpe/protocol/PacketPool.php b/src/pocketmine/network/mcpe/protocol/PacketPool.php index 61fecb00a..4c9f09d9e 100644 --- a/src/pocketmine/network/mcpe/protocol/PacketPool.php +++ b/src/pocketmine/network/mcpe/protocol/PacketPool.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\utils\Binary; use pocketmine\utils\BinaryDataException; diff --git a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php index d3f765dd1..b9dff331b 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php @@ -26,11 +26,8 @@ namespace pocketmine\network\mcpe\protocol; #include -use pocketmine\entity\Skin; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; -use pocketmine\utils\SerializedImage; -use pocketmine\utils\SkinAnimation; use function count; class PlayerListPacket extends DataPacket{ From 3061eb41578a1a0b490d65743929c2a6702ae2ab Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:44:49 -0500 Subject: [PATCH 19/95] Fixed convertToLegacyName not using variables, add typehints to it too --- src/pocketmine/entity/Skin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index f06d4d0c5..9c221eaf5 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -75,8 +75,8 @@ class Skin{ $this->capeId = $capeId; } - public static function convertToLegacyName(string $name){ - return '{"geometry" : {"default" : "geometry.humanoid.custom"}}'; + public static function convertToLegacyName(string $name) : string{ + return '{"geometry" : {"default" : "' . $name . '"}}'; } /** From 898af49e97e925ae65ef7d9c1f60afdfe4031c50 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:45:06 -0500 Subject: [PATCH 20/95] Add typehints to completedusingitempacket --- .../network/mcpe/protocol/CompletedUsingItemPacket.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php b/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php index 9a4f96d70..2a7381f23 100644 --- a/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CompletedUsingItemPacket.php @@ -50,12 +50,12 @@ class CompletedUsingItemPacket extends DataPacket{ /** @var int */ public $action; - public function decodePayload(){ + public function decodePayload() : void{ $this->itemId = $this->getShort(); $this->action = $this->getLInt(); } - public function encodePayload(){ + public function encodePayload() : void{ $this->putShort($this->itemId); $this->putLInt($this->action); } From 3bacb1a9cb3195f90e8a7d999feb2732c2ca6509 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:45:20 -0500 Subject: [PATCH 21/95] Moved skin back Not too sure what I planned on doing with that --- .../network/mcpe/protocol/types/PlayerListEntry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php index 95bc19c9e..18ad180ff 100644 --- a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php +++ b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php @@ -34,14 +34,14 @@ class PlayerListEntry{ public $entityUniqueId; /** @var string */ public $username; + /** @var Skin */ + public $skin; /** @var string */ public $xboxUserId; /** @var string */ public $platformChatId = ""; /** @var int */ public $buildPlatform = 0; //Unknown - /** @var Skin */ - public $skin; /** @var bool */ public $isTeacher = false; /** @var bool */ From 35d1d0080a0ceab921fe1787cc4972470285cf00 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:45:33 -0500 Subject: [PATCH 22/95] Removed count const and fixed BC break --- .../network/mcpe/protocol/types/ResourcePackType.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php b/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php index fd0a8e931..bc4a217cf 100644 --- a/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php +++ b/src/pocketmine/network/mcpe/protocol/types/ResourcePackType.php @@ -33,10 +33,9 @@ final class ResourcePackType{ public const ADDON = 1; public const CACHED = 2; public const COPY_PROTECTED = 3; - public const BEHAVIOR = 4; + public const BEHAVIORS = 4; public const PERSONA_PIECE = 5; public const RESOURCES = 6; public const SKINS = 7; public const WORLD_TEMPLATE = 8; - public const COUNT = 9; } From 6166c90bfd37bc420757119bb910cee2ec64ed27 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:45:47 -0500 Subject: [PATCH 23/95] No need for a try catch statement around reading a file --- .../network/mcpe/protocol/types/RuntimeBlockMapping.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php index bc8d7df41..03a383e69 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -52,13 +52,7 @@ final class RuntimeBlockMapping{ public static function init() : void{ $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); - - try{ - /** @var CompoundTag $tag */ - $tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/runtime_block_states.dat")); - }catch(BinaryDataException $e){ - throw new \RuntimeException("", 0, $e); - } + $tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/runtime_block_states.dat")); $decompressed = []; From eef979db4c27588be8f267e21d76489ac3f9b989 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 19:49:04 -0500 Subject: [PATCH 24/95] Switch to isUsingItem() I don't plan on using this, as you informed me on discord that I didn't need the extra step, and it's a BC break anyway --- src/pocketmine/Player.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 82c0a6b80..d2dbd3c02 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -2542,7 +2542,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - if($this->startAction === -1){ + if(!$this->isUsingItem()){ $this->setUsingItem(true); return true; } From dfa603c3350568846d982baa697b24265cd5c833 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 20:06:46 -0500 Subject: [PATCH 25/95] Simplify if null statement --- src/pocketmine/entity/Skin.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index 9c221eaf5..0be896b62 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -142,11 +142,7 @@ class Skin{ * @return SerializedImage */ public function getCapeData() : SerializedImage{ - if($this->capeData === null){ - return new SerializedImage(0, 0, ""); - } - - return $this->capeData; + return $this->capeData ?? new SerializedImage(0, 0, ""); } /** From bfb1ad132763f45d791fb5fb4787565b7bcc5d22 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 23:17:01 -0500 Subject: [PATCH 26/95] Removed PlayerUIInventory and BaseUIInventory --- src/pocketmine/Player.php | 23 ++-- src/pocketmine/block/CraftingTable.php | 2 +- src/pocketmine/inventory/BaseUIInventory.php | 116 ----------------- src/pocketmine/inventory/CraftingGrid.php | 117 ++++++++++++++++-- .../inventory/PlayerCursorInventory.php | 34 ++++- .../inventory/PlayerUIInventory.php | 89 ------------- src/pocketmine/utils/SkinAnimation.php | 2 + 7 files changed, 152 insertions(+), 231 deletions(-) delete mode 100644 src/pocketmine/inventory/BaseUIInventory.php delete mode 100644 src/pocketmine/inventory/PlayerUIInventory.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index d2dbd3c02..21cd48b7f 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -72,7 +72,6 @@ use pocketmine\form\FormValidationException; use pocketmine\inventory\CraftingGrid; use pocketmine\inventory\Inventory; use pocketmine\inventory\PlayerCursorInventory; -use pocketmine\inventory\PlayerUIInventory; use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\CraftingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; @@ -290,8 +289,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ protected $windowIndex = []; /** @var bool[] */ protected $permanentWindows = []; - /** @var PlayerUIInventory */ - protected $playerUIInventory; + /** @var PlayerCursorInventory */ + protected $cursorInventory; /** @var CraftingGrid */ protected $craftingGrid = null; /** @var CraftingTransaction|null */ @@ -3554,7 +3553,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->removeAllWindows(true); $this->windows = []; $this->windowIndex = []; - $this->playerUIInventory = null; + $this->cursorInventory = null; $this->craftingGrid = null; if($this->constructed){ @@ -3819,19 +3818,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->addWindow($this->getArmorInventory(), ContainerIds::ARMOR, true); - $this->playerUIInventory = new PlayerUIInventory($this); - $this->addWindow($this->playerUIInventory, ContainerIds::UI, true); + $this->cursorInventory = new PlayerCursorInventory($this); + $this->addWindow($this->cursorInventory, ContainerIds::UI, true); - $this->craftingGrid = new CraftingGrid($this->playerUIInventory, CraftingGrid::SIZE_SMALL); + $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); //TODO: more windows } - public function getPlayerUIInventory() : PlayerUIInventory{ - return $this->playerUIInventory; - } - public function getCursorInventory() : PlayerCursorInventory{ - return $this->playerUIInventory->getCursorInventory(); + return $this->cursorInventory; } public function getCraftingGrid() : CraftingGrid{ @@ -3847,7 +3842,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public function doCloseInventory() : void{ /** @var Inventory[] $inventories */ - $inventories = [$this->craftingGrid, $this->getCursorInventory()]; + $inventories = [$this->craftingGrid, $this->cursorInventory]; foreach($inventories as $inventory){ $contents = $inventory->getContents(); if(count($contents) > 0){ @@ -3861,7 +3856,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){ - $this->craftingGrid = new CraftingGrid($this->playerUIInventory, CraftingGrid::SIZE_SMALL); + $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); } } diff --git a/src/pocketmine/block/CraftingTable.php b/src/pocketmine/block/CraftingTable.php index 9e416e872..05b52fd6a 100644 --- a/src/pocketmine/block/CraftingTable.php +++ b/src/pocketmine/block/CraftingTable.php @@ -49,7 +49,7 @@ class CraftingTable extends Solid{ public function onActivate(Item $item, Player $player = null) : bool{ if($player instanceof Player){ - $player->setCraftingGrid(new CraftingGrid($player->getPlayerUIInventory(), CraftingGrid::SIZE_BIG)); + $player->setCraftingGrid(new CraftingGrid($player, CraftingGrid::SIZE_BIG)); } return true; diff --git a/src/pocketmine/inventory/BaseUIInventory.php b/src/pocketmine/inventory/BaseUIInventory.php deleted file mode 100644 index 3636e3ab7..000000000 --- a/src/pocketmine/inventory/BaseUIInventory.php +++ /dev/null @@ -1,116 +0,0 @@ -inventory = $inventory; - $this->size = $size; - $this->offset = $offset; - parent::__construct([], $size); - } - - public function getName() : string{ - return "UI"; - } - - public function getDefaultSize() : int{ - return 1; - } - - public function getSize() : int{ - return $this->size; - } - - public function getMaxStackSize() : int{ - return Inventory::MAX_STACK; - } - - public function setMaxStackSize(int $size) : void{ - } - - public function getTitle() : string{ - return ""; - } - - public function getItem(int $index) : Item{ - return $this->inventory->getItem($index + $this->offset); - } - - public function setItem(int $index, Item $item, bool $send = true) : bool{ - return $this->inventory->setItem($index + $this->offset, $item, $send); - } - - public function getContents(bool $includeEmpty = false) : array{ - $contents = []; - $air = null; - foreach($this->slots as $i => $slot){ - if($i < $this->offset || $i > $this->offset + $this->size){ - continue; - } - if($slot !== null){ - $contents[$i] = clone $slot; - }elseif($includeEmpty){ - $contents[$i] = $air ?? ($air = ItemFactory::get(Item::AIR, 0, 0)); - } - } - - return $contents; - } - - public function sendContents($target) : void{ - $this->inventory->sendContents($target); - } - - public function sendSlot(int $index, $target) : void{ - $this->inventory->sendSlot($index + $this->offset, $target); - } - - public function getViewers() : array{ - return $this->inventory->viewers; - } - - public function onOpen(Player $who) : void{} - - public function onClose(Player $who) : void{} - - public function open(Player $who) : bool{ - return false; - } - - public function close(Player $who) : void{} - - public function onSlotChange(int $index, Item $before, bool $send) : void{ - $this->inventory->onSlotChange($index + $this->offset, $before, $send); - } -} \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingGrid.php b/src/pocketmine/inventory/CraftingGrid.php index b54068f4e..e6097562e 100644 --- a/src/pocketmine/inventory/CraftingGrid.php +++ b/src/pocketmine/inventory/CraftingGrid.php @@ -29,23 +29,124 @@ use function max; use function min; use const PHP_INT_MAX; -class CraftingGrid extends BaseUIInventory{ - public const SIZE_SMALL = 4; - public const SIZE_BIG = 9; +class CraftingGrid extends BaseInventory{ + public const SIZE_SMALL = 2; + public const SIZE_BIG = 3; + + public const SMALL_OFFSET = 28; + public const BIG_OFFSET = 32; + + /** @var int */ + public $offset; + /** @var Player */ + private $holder; /** @var int */ private $gridWidth; + /** @var int|null */ + private $startX; + /** @var int|null */ + private $xLen; + /** @var int|null */ + private $startY; + /** @var int|null */ + private $yLen; - public function __construct(PlayerUIInventory $inventory, int $gridWidth){ + public function __construct(Player $holder, int $gridWidth){ + $this->holder = $holder; $this->gridWidth = $gridWidth; - if($gridWidth === self::SIZE_SMALL){ - parent::__construct($inventory, $gridWidth, 28); - }elseif($gridWidth === self::SIZE_BIG){ - parent::__construct($inventory, $gridWidth, 32); + if($this->gridWidth === self::SIZE_SMALL){ + $this->offset = self::SMALL_OFFSET; + }elseif($this->gridWidth === self::SIZE_BIG){ + $this->offset = self::BIG_OFFSET; } + parent::__construct(); } public function getGridWidth() : int{ return $this->gridWidth; } + + public function getDefaultSize() : int{ + return $this->getGridWidth() ** 2; + } + + public function setSize(int $size){ + throw new \BadMethodCallException("Cannot change the size of a crafting grid"); + } + + public function getName() : string{ + return "Crafting"; + } + + public function getItem(int $index) : Item{ + return parent::getItem($index + $this->offset); + } + + public function setItem(int $index, Item $item, bool $send = true) : bool{ + if(parent::setItem($index + $this->offset, $item, $send)){ + $this->seekRecipeBounds(); + return true; + } + return false; + } + + public function sendSlot(int $index, $target) : void{ + //we can't send a slot of a client-sided inventory window + } + + public function sendContents($target) : void{ + //no way to do this + } + + /** + * @return Player + */ + public function getHolder(){ + return $this->holder; + } + + private function seekRecipeBounds() : void{ + $minX = PHP_INT_MAX; + $maxX = 0; + $minY = PHP_INT_MAX; + $maxY = 0; + $empty = true; + for($y = 0; $y < $this->gridWidth; ++$y){ + for($x = 0; $x < $this->gridWidth; ++$x){ + if(!$this->isSlotEmpty($y * $this->gridWidth + $x)){ + $minX = min($minX, $x); + $maxX = max($maxX, $x); + $minY = min($minY, $y); + $maxY = max($maxY, $y); + $empty = false; + } + } + } + if(!$empty){ + $this->startX = $minX; + $this->xLen = $maxX - $minX + 1; + $this->startY = $minY; + $this->yLen = $maxY - $minY + 1; + }else{ + $this->startX = $this->xLen = $this->startY = $this->yLen = null; + } + } + + /** + * Returns the width of the recipe we're trying to craft, based on items currently in the grid. + * + * @return int + */ + public function getRecipeWidth() : int{ + return $this->xLen ?? 0; + } + + /** + * Returns the height of the recipe we're trying to craft, based on items currently in the grid. + * @return int + */ + public function getRecipeHeight() : int{ + return $this->yLen ?? 0; + } } diff --git a/src/pocketmine/inventory/PlayerCursorInventory.php b/src/pocketmine/inventory/PlayerCursorInventory.php index 32939f7f7..2b0869cd5 100644 --- a/src/pocketmine/inventory/PlayerCursorInventory.php +++ b/src/pocketmine/inventory/PlayerCursorInventory.php @@ -23,9 +23,37 @@ declare(strict_types=1); namespace pocketmine\inventory; -class PlayerCursorInventory extends BaseUIInventory{ +use pocketmine\Player; - public function __construct(PlayerUIInventory $inventory){ - parent::__construct($inventory, 1, 0); +class PlayerCursorInventory extends BaseInventory{ + + /** @var Player */ + protected $holder; + /** @var int */ + protected $offset = 0; + + public function __construct(Player $holder){ + $this->holder = $holder; + parent::__construct(); + } + + public function getName() : string{ + return "Cursor"; + } + + public function getDefaultSize() : int{ + return 1; + } + + public function setSize(int $size){ + throw new \BadMethodCallException("Cursor can only carry one item at a time"); + } + + /** + * This override is here for documentation and code completion purposes only. + * @return Player + */ + public function getHolder(){ + return $this->holder; } } diff --git a/src/pocketmine/inventory/PlayerUIInventory.php b/src/pocketmine/inventory/PlayerUIInventory.php deleted file mode 100644 index a3e9e6454..000000000 --- a/src/pocketmine/inventory/PlayerUIInventory.php +++ /dev/null @@ -1,89 +0,0 @@ -holder = $holder; - $this->cursorInventory = new PlayerCursorInventory($this); - parent::__construct(); - } - - public function getCursorInventory(){ - return $this->cursorInventory; - } - - public function getName() : string{ - return "UI"; - } - - public function getDefaultSize() : int{ - return 51; - } - - public function getSize() : int{ - return 51; - } - - public function getHolder(){ - return $this->holder; - } - - public function setSize(int $size){} - - public function sendSlot(int $index, $target) : void{ - if($target instanceof Player){ - $target = [$target]; - } - $pk = new InventorySlotPacket(); - $pk->inventorySlot = $index; - $pk->item = $this->getItem($index); - foreach($target as $player){ - if($player === $this->getHolder()){ - $pk->windowId = ContainerIds::UI; - $player->dataPacket($pk); - }else{ - if(($id = $player->getWindowId($this)) === ContainerIds::NONE){ - $this->close($player); - continue; - } - $pk->windowId = $id; - $player->dataPacket($pk); - } - } - } -} \ No newline at end of file diff --git a/src/pocketmine/utils/SkinAnimation.php b/src/pocketmine/utils/SkinAnimation.php index 025aee3d6..10092059b 100644 --- a/src/pocketmine/utils/SkinAnimation.php +++ b/src/pocketmine/utils/SkinAnimation.php @@ -25,6 +25,8 @@ namespace pocketmine\utils; class SkinAnimation{ + public const TYPE_HEAD = 1; //Might want a double check on this + /** @var SerializedImage */ private $image; /** @var int */ From 6a31628e78460192ae2e8cc03c8a6ab39b1d2013 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 7 Nov 2019 23:43:26 -0500 Subject: [PATCH 27/95] Clean up whitespace disaster and add getIngredient back --- src/pocketmine/inventory/CraftingGrid.php | 27 ++++++++++++++++++- .../inventory/PlayerCursorInventory.php | 3 --- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/inventory/CraftingGrid.php b/src/pocketmine/inventory/CraftingGrid.php index e6097562e..739e17ed9 100644 --- a/src/pocketmine/inventory/CraftingGrid.php +++ b/src/pocketmine/inventory/CraftingGrid.php @@ -39,10 +39,11 @@ class CraftingGrid extends BaseInventory{ /** @var int */ public $offset; /** @var Player */ - private $holder; + protected $holder; /** @var int */ private $gridWidth; + /** @var int|null */ private $startX; /** @var int|null */ @@ -86,8 +87,10 @@ class CraftingGrid extends BaseInventory{ public function setItem(int $index, Item $item, bool $send = true) : bool{ if(parent::setItem($index + $this->offset, $item, $send)){ $this->seekRecipeBounds(); + return true; } + return false; } @@ -109,20 +112,26 @@ class CraftingGrid extends BaseInventory{ private function seekRecipeBounds() : void{ $minX = PHP_INT_MAX; $maxX = 0; + $minY = PHP_INT_MAX; $maxY = 0; + $empty = true; + for($y = 0; $y < $this->gridWidth; ++$y){ for($x = 0; $x < $this->gridWidth; ++$x){ if(!$this->isSlotEmpty($y * $this->gridWidth + $x)){ $minX = min($minX, $x); $maxX = max($maxX, $x); + $minY = min($minY, $y); $maxY = max($maxY, $y); + $empty = false; } } } + if(!$empty){ $this->startX = $minX; $this->xLen = $maxX - $minX + 1; @@ -133,6 +142,22 @@ class CraftingGrid extends BaseInventory{ } } + /** + * Returns the item at offset x,y, offset by where the starts of the recipe rectangle are. + * + * @param int $x + * @param int $y + * + * @return Item + */ + public function getIngredient(int $x, int $y) : Item{ + if($this->startX !== null and $this->startY !== null){ + return $this->getItem(($y + $this->startY) * $this->gridWidth + ($x + $this->startX)); + } + + throw new \InvalidStateException("No ingredients found in grid"); + } + /** * Returns the width of the recipe we're trying to craft, based on items currently in the grid. * diff --git a/src/pocketmine/inventory/PlayerCursorInventory.php b/src/pocketmine/inventory/PlayerCursorInventory.php index 2b0869cd5..b967139f5 100644 --- a/src/pocketmine/inventory/PlayerCursorInventory.php +++ b/src/pocketmine/inventory/PlayerCursorInventory.php @@ -26,11 +26,8 @@ namespace pocketmine\inventory; use pocketmine\Player; class PlayerCursorInventory extends BaseInventory{ - /** @var Player */ protected $holder; - /** @var int */ - protected $offset = 0; public function __construct(Player $holder){ $this->holder = $holder; From d2e4eb40b3b2b3624c062eed933f6acc5a5dcdb9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 8 Nov 2019 08:29:25 +0000 Subject: [PATCH 28/95] CompletedUsingItemPacket is superfluous --- src/pocketmine/Player.php | 13 +------------ src/pocketmine/item/Bow.php | 5 ----- src/pocketmine/item/Bucket.php | 9 --------- src/pocketmine/item/Food.php | 5 ----- src/pocketmine/item/Item.php | 5 ----- src/pocketmine/item/Potion.php | 5 ----- src/pocketmine/item/ProjectileItem.php | 5 ----- 7 files changed, 1 insertion(+), 46 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 21cd48b7f..fcb6d9085 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -99,7 +99,6 @@ use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\DoubleTag; use pocketmine\nbt\tag\ListTag; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\PlayerNetworkSessionAdapter; use pocketmine\network\mcpe\protocol\ActorEventPacket; use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; @@ -1114,13 +1113,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - protected function sendCompletedUsingItemPacket(int $itemId, int $action){ - $pk = new CompletedUsingItemPacket(); - $pk->itemId = $itemId; - $pk->action = $action; - $this->sendDataPacket($pk); - } - protected function sendRespawnPacket(Vector3 $pos, int $respawnState = RespawnPacket::SEARCHING_FOR_SPAWN){ $pk = new RespawnPacket(); $pk->position = $pos->add(0, $this->baseOffset, 0); @@ -2547,9 +2539,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } $this->setUsingItem(false); - if($item->onUse($this)){ - $this->sendCompletedUsingItemPacket($item->getId(), $item->getCompletedAction()); - } + $item->onUse($this); return true; default: @@ -2667,7 +2657,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if($item->onReleaseUsing($this)){ $this->resetItemCooldown($item); $this->inventory->setItemInHand($item); - $this->sendCompletedUsingItemPacket($item->getId(), $item->getCompletedAction()); } }else{ break; diff --git a/src/pocketmine/item/Bow.php b/src/pocketmine/item/Bow.php index dbce96828..48428b576 100644 --- a/src/pocketmine/item/Bow.php +++ b/src/pocketmine/item/Bow.php @@ -29,7 +29,6 @@ use pocketmine\entity\projectile\Projectile; use pocketmine\event\entity\EntityShootBowEvent; use pocketmine\event\entity\ProjectileLaunchEvent; use pocketmine\item\enchantment\Enchantment; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\Player; use function intdiv; @@ -48,10 +47,6 @@ class Bow extends Tool{ return 385; } - public function getCompletedAction(){ - return CompletedUsingItemPacket::ACTION_SHOOT; - } - public function onReleaseUsing(Player $player) : bool{ if($player->isSurvival() and !$player->getInventory()->contains(ItemFactory::get(Item::ARROW, 0, 1))){ $player->getInventory()->sendContents($player); diff --git a/src/pocketmine/item/Bucket.php b/src/pocketmine/item/Bucket.php index 89612a438..380b7a9b3 100644 --- a/src/pocketmine/item/Bucket.php +++ b/src/pocketmine/item/Bucket.php @@ -32,7 +32,6 @@ use pocketmine\event\player\PlayerBucketEmptyEvent; use pocketmine\event\player\PlayerBucketFillEvent; use pocketmine\event\player\PlayerItemConsumeEvent; use pocketmine\math\Vector3; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\Player; class Bucket extends Item implements Consumable{ @@ -117,14 +116,6 @@ class Bucket extends Item implements Consumable{ $consumer->removeAllEffects(); } - public function getCompletedAction(){ - if($this->canBeConsumed()){ - return CompletedUsingItemPacket::ACTION_CONSUME; - }else{ - return CompletedUsingItemPacket::ACTION_POUR_BUCKET; - } - } - public function onUse(Player $player) : bool{ if($this->canBeConsumed()){ $slot = $player->getInventory()->getItemInHand(); diff --git a/src/pocketmine/item/Food.php b/src/pocketmine/item/Food.php index ac1ca4d2e..a97c02822 100644 --- a/src/pocketmine/item/Food.php +++ b/src/pocketmine/item/Food.php @@ -26,7 +26,6 @@ namespace pocketmine\item; use pocketmine\entity\Living; use pocketmine\event\player\PlayerItemConsumeEvent; use pocketmine\math\Vector3; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\Player; abstract class Food extends Item implements FoodSource{ @@ -45,10 +44,6 @@ abstract class Food extends Item implements FoodSource{ return []; } - public function getCompletedAction(){ - return CompletedUsingItemPacket::ACTION_EAT; - } - public function onUse(Player $player) : bool{ $slot = $player->getInventory()->getItemInHand(); diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index bc02fd9f1..16c3df97a 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -41,7 +41,6 @@ use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\NamedTag; use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\Player; use pocketmine\utils\Binary; use function array_map; @@ -849,10 +848,6 @@ class Item implements ItemIds, \JsonSerializable{ return 0; } - public function getCompletedAction(){ - return CompletedUsingItemPacket::ACTION_UNKNOWN; - } - public function onUse(Player $player) : bool{ return false; } diff --git a/src/pocketmine/item/Potion.php b/src/pocketmine/item/Potion.php index 64a0246c4..0221e50a1 100644 --- a/src/pocketmine/item/Potion.php +++ b/src/pocketmine/item/Potion.php @@ -27,7 +27,6 @@ use pocketmine\entity\Effect; use pocketmine\entity\EffectInstance; use pocketmine\entity\Living; use pocketmine\event\player\PlayerItemConsumeEvent; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\Player; class Potion extends Item implements Consumable{ @@ -239,10 +238,6 @@ class Potion extends Item implements Consumable{ return ItemFactory::get(Item::GLASS_BOTTLE); } - public function getCompletedAction(){ - return CompletedUsingItemPacket::ACTION_CONSUME; - } - public function onUse(Player $player) : bool{ $slot = $player->getInventory()->getItemInHand(); diff --git a/src/pocketmine/item/ProjectileItem.php b/src/pocketmine/item/ProjectileItem.php index 3e824316c..88bebf3e1 100644 --- a/src/pocketmine/item/ProjectileItem.php +++ b/src/pocketmine/item/ProjectileItem.php @@ -29,7 +29,6 @@ use pocketmine\entity\projectile\Projectile; use pocketmine\event\entity\ProjectileLaunchEvent; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; -use pocketmine\network\mcpe\protocol\CompletedUsingItemPacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\Player; @@ -48,10 +47,6 @@ abstract class ProjectileItem extends Item{ } - public function getCompletedAction(){ - return CompletedUsingItemPacket::ACTION_THROW; - } - public function onClickAir(Player $player, Vector3 $directionVector) : bool{ $nbt = Entity::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch); $this->addExtraTags($nbt); From 73b923e3a1cc5822f113fb2892392e08bca546e1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 8 Nov 2019 11:08:22 +0000 Subject: [PATCH 29/95] cleanup eating clusterfuck --- src/pocketmine/Player.php | 31 ++++++++++++++++++++++--------- src/pocketmine/item/Bucket.php | 24 ------------------------ src/pocketmine/item/Food.php | 28 ---------------------------- src/pocketmine/item/Item.php | 4 ---- src/pocketmine/item/Potion.php | 23 ----------------------- 5 files changed, 22 insertions(+), 88 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index fcb6d9085..a36d074a4 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -2504,6 +2504,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR: + if($this->isUsingItem()){ + $slot = $this->inventory->getItemInHand(); + if($slot instanceof Consumable){ + $ev = new PlayerItemConsumeEvent($this, $slot); + if($this->hasItemCooldown($slot)){ + $ev->setCancelled(); + } + $ev->call(); + if($ev->isCancelled() or !$this->consumeObject($slot)){ + $this->inventory->sendContents($this); + return true; + } + $this->resetItemCooldown($slot); + if($this->isSurvival()){ + $slot->pop(); + $this->inventory->setItemInHand($slot); + $this->inventory->addItem($slot->getResidue()); + } + $this->setUsingItem(false); + } + } $directionVector = $this->getDirectionVector(); if($this->isCreative()){ @@ -2533,13 +2554,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } } - if(!$this->isUsingItem()){ - $this->setUsingItem(true); - return true; - } - - $this->setUsingItem(false); - $item->onUse($this); + $this->setUsingItem(true); return true; default: @@ -2663,8 +2678,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } return true; - case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME: - break; default: break; } diff --git a/src/pocketmine/item/Bucket.php b/src/pocketmine/item/Bucket.php index 380b7a9b3..ffe4faa62 100644 --- a/src/pocketmine/item/Bucket.php +++ b/src/pocketmine/item/Bucket.php @@ -115,28 +115,4 @@ class Bucket extends Item implements Consumable{ public function onConsume(Living $consumer){ $consumer->removeAllEffects(); } - - public function onUse(Player $player) : bool{ - if($this->canBeConsumed()){ - $slot = $player->getInventory()->getItemInHand(); - - $ev = new PlayerItemConsumeEvent($player, $slot); - $ev->call(); - - /** @var $slot Consumable */ - if($ev->isCancelled() or !$player->consumeObject($slot)){ - $player->getInventory()->sendContents($player); - return true; - } - - if($player->isSurvival()){ - $slot->pop(); - $player->getInventory()->setItemInHand($slot); - $player->getInventory()->addItem($slot->getResidue()); - } - - return true; - } - return false; - } } diff --git a/src/pocketmine/item/Food.php b/src/pocketmine/item/Food.php index a97c02822..36f4cee4f 100644 --- a/src/pocketmine/item/Food.php +++ b/src/pocketmine/item/Food.php @@ -24,9 +24,6 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\entity\Living; -use pocketmine\event\player\PlayerItemConsumeEvent; -use pocketmine\math\Vector3; -use pocketmine\Player; abstract class Food extends Item implements FoodSource{ public function requiresHunger() : bool{ @@ -44,32 +41,7 @@ abstract class Food extends Item implements FoodSource{ return []; } - public function onUse(Player $player) : bool{ - $slot = $player->getInventory()->getItemInHand(); - - $ev = new PlayerItemConsumeEvent($player, $slot); - $ev->call(); - - /** @var $slot Consumable */ - if($ev->isCancelled() or !$player->consumeObject($slot)){ - $player->getInventory()->sendContents($player); - return true; - } - - if($player->isSurvival()){ - $slot->pop(); - $player->getInventory()->setItemInHand($slot); - $player->getInventory()->addItem($slot->getResidue()); - } - - return true; - } - public function onConsume(Living $consumer){ } - - public function onClickAir(Player $player, Vector3 $directionVector) : bool{ - return true; - } } diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index 16c3df97a..664e07bba 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -848,10 +848,6 @@ class Item implements ItemIds, \JsonSerializable{ return 0; } - public function onUse(Player $player) : bool{ - return false; - } - /** * Compares an Item to this Item and check if they match. * diff --git a/src/pocketmine/item/Potion.php b/src/pocketmine/item/Potion.php index 0221e50a1..b2b927d54 100644 --- a/src/pocketmine/item/Potion.php +++ b/src/pocketmine/item/Potion.php @@ -26,8 +26,6 @@ namespace pocketmine\item; use pocketmine\entity\Effect; use pocketmine\entity\EffectInstance; use pocketmine\entity\Living; -use pocketmine\event\player\PlayerItemConsumeEvent; -use pocketmine\Player; class Potion extends Item implements Consumable{ @@ -237,25 +235,4 @@ class Potion extends Item implements Consumable{ public function getResidue(){ return ItemFactory::get(Item::GLASS_BOTTLE); } - - public function onUse(Player $player) : bool{ - $slot = $player->getInventory()->getItemInHand(); - - $ev = new PlayerItemConsumeEvent($player, $slot); - $ev->call(); - - /** @var $slot Consumable */ - if($ev->isCancelled() or !$player->consumeObject($slot)){ - $player->getInventory()->sendContents($player); - return true; - } - - if($player->isSurvival()){ - $slot->pop(); - $player->getInventory()->setItemInHand($slot); - $player->getInventory()->addItem($slot->getResidue()); - } - - return true; - } } From f6b5301e173a7389b277239e0e308053fcb052c6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 8 Nov 2019 11:10:20 +0000 Subject: [PATCH 30/95] Player: remove unused import --- src/pocketmine/Player.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index a36d074a4..c14f88bc0 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -80,7 +80,6 @@ use pocketmine\item\Consumable; use pocketmine\item\Durable; use pocketmine\item\enchantment\EnchantmentInstance; use pocketmine\item\enchantment\MeleeWeaponEnchantment; -use pocketmine\item\Food; use pocketmine\item\Item; use pocketmine\item\WritableBook; use pocketmine\item\WrittenBook; From e60962c31f5b0dd90fb96ec5f3956bda28dc79f0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 8 Nov 2019 11:22:55 +0000 Subject: [PATCH 31/95] reverse BC-breaking change to Player->changeSkin() this change would cause LSP violations for subclasses --- src/pocketmine/Player.php | 4 +++- src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index c14f88bc0..e22a42291 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -836,10 +836,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ * Plugin developers should not use this, use setSkin() and sendSkin() instead. * * @param Skin $skin + * @param string $newSkinName + * @param string $oldSkinName * * @return bool */ - public function changeSkin(Skin $skin) : bool{ + public function changeSkin(Skin $skin, string $newSkinName, string $oldSkinName) : bool{ if(!$skin->isValid()){ return false; } diff --git a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php index 7a41eb194..2500849f5 100644 --- a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php +++ b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php @@ -253,7 +253,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{ } public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{ - return $this->player->changeSkin($packet->skin); + return $this->player->changeSkin($packet->skin, "", ""); } public function handleBookEdit(BookEditPacket $packet) : bool{ From 29cc9283f80bad009b3cf653dd242bfb9c166695 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 8 Nov 2019 11:29:00 +0000 Subject: [PATCH 32/95] reverse unnecessary formatting changes --- src/pocketmine/Player.php | 2 +- src/pocketmine/inventory/CraftingGrid.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index e22a42291..9ee384ba6 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -2721,7 +2721,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if(!$this->spawned or !$this->isAlive()){ return true; } - if($packet->action === InteractPacket::ACTION_MOUSEOVER and $packet->target === 0){ //TODO HACK: silence useless spam (MCPE 1.8) //this packet is EXPECTED to only be sent when interacting with an entity, but due to some messy Mojang @@ -3825,6 +3824,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->addWindow($this->cursorInventory, ContainerIds::UI, true); $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); + //TODO: more windows } diff --git a/src/pocketmine/inventory/CraftingGrid.php b/src/pocketmine/inventory/CraftingGrid.php index 739e17ed9..7d8b02c9a 100644 --- a/src/pocketmine/inventory/CraftingGrid.php +++ b/src/pocketmine/inventory/CraftingGrid.php @@ -40,7 +40,6 @@ class CraftingGrid extends BaseInventory{ public $offset; /** @var Player */ protected $holder; - /** @var int */ private $gridWidth; From a7ed933b37b178905a9e3cb1f9d4ec41d9557009 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 8 Nov 2019 11:32:09 +0000 Subject: [PATCH 33/95] superfluous import --- src/pocketmine/item/Bucket.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pocketmine/item/Bucket.php b/src/pocketmine/item/Bucket.php index ffe4faa62..3c7834350 100644 --- a/src/pocketmine/item/Bucket.php +++ b/src/pocketmine/item/Bucket.php @@ -30,7 +30,6 @@ use pocketmine\block\Liquid; use pocketmine\entity\Living; use pocketmine\event\player\PlayerBucketEmptyEvent; use pocketmine\event\player\PlayerBucketFillEvent; -use pocketmine\event\player\PlayerItemConsumeEvent; use pocketmine\math\Vector3; use pocketmine\Player; From f03bd982b54d5a8a36fe36c68d7fdb263a0d7a35 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sat, 9 Nov 2019 23:56:18 -0500 Subject: [PATCH 34/95] Empty fields in PlayerSkinPacket, thanks @Minejong --- src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index 4da5deeef..d40901c62 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -38,15 +38,23 @@ class PlayerSkinPacket extends DataPacket{ public $uuid; /** @var Skin */ public $skin; + /** @var string */ + public $newSkinName = ""; + /** @var string */ + public $unknownString = ""; //Sent as empty, assuming it is the old skin name protected function decodePayload(){ $this->uuid = $this->getUUID(); $this->skin = $this->getSkin(); + $this->newSkinName = $this->getString(); + $this->unknownString = $this->getString(); } protected function encodePayload(){ $this->putUUID($this->uuid); $this->putSkin($this->skin); + $this->putString($this->newSkinName); + $this->putString($this->unknownString); } public function handle(NetworkSession $session) : bool{ From dd55a0bccd41b56cf8cbaa6b8e89d34dae0e97f4 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 00:01:12 -0500 Subject: [PATCH 35/95] Use unknown os for PlayerListEntry --- src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php index 18ad180ff..525083d03 100644 --- a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php +++ b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php @@ -41,7 +41,7 @@ class PlayerListEntry{ /** @var string */ public $platformChatId = ""; /** @var int */ - public $buildPlatform = 0; //Unknown + public $buildPlatform = -1; /** @var bool */ public $isTeacher = false; /** @var bool */ From 54530da6c18f9faea8182ef7534ab85df397402f Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 00:09:29 -0500 Subject: [PATCH 36/95] reduce visibility of offset to private --- src/pocketmine/inventory/CraftingGrid.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/inventory/CraftingGrid.php b/src/pocketmine/inventory/CraftingGrid.php index 7d8b02c9a..33221a738 100644 --- a/src/pocketmine/inventory/CraftingGrid.php +++ b/src/pocketmine/inventory/CraftingGrid.php @@ -33,15 +33,15 @@ class CraftingGrid extends BaseInventory{ public const SIZE_SMALL = 2; public const SIZE_BIG = 3; - public const SMALL_OFFSET = 28; - public const BIG_OFFSET = 32; + private const SMALL_OFFSET = 28; + private const BIG_OFFSET = 32; - /** @var int */ - public $offset; /** @var Player */ protected $holder; /** @var int */ private $gridWidth; + /** @var int */ + private $offset; /** @var int|null */ private $startX; From 308d9ce3a861397507f03de928ba826894a7cc68 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 10 Nov 2019 17:40:04 -0500 Subject: [PATCH 37/95] auto generated baseline --- .../network/mcpe/NetworkSession.php | 48 +++++++++++++++++-- .../mcpe/protocol/AnvilDamagePacket.php | 44 +++++++++++++++++ .../mcpe/protocol/EducationSettingsPacket.php | 44 +++++++++++++++++ .../network/mcpe/protocol/EmotePacket.php | 44 +++++++++++++++++ .../protocol/MultiplayerSettingsPacket.php | 44 +++++++++++++++++ .../mcpe/protocol/NetworkSettingsPacket.php | 44 +++++++++++++++++ .../network/mcpe/protocol/PacketPool.php | 12 ++++- .../mcpe/protocol/PlayerAuthInputPacket.php | 44 +++++++++++++++++ .../network/mcpe/protocol/ProtocolInfo.php | 4 +- .../mcpe/protocol/SettingsCommandPacket.php | 44 +++++++++++++++++ ...=> StructureTemplateDataRequestPacket.php} | 6 +-- ...> StructureTemplateDataResponsePacket.php} | 6 +-- .../network/mcpe/protocol/TickSyncPacket.php | 44 +++++++++++++++++ 13 files changed, 414 insertions(+), 14 deletions(-) create mode 100644 src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/EmotePacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php create mode 100644 src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php rename src/pocketmine/network/mcpe/protocol/{StructureTemplateDataExportRequestPacket.php => StructureTemplateDataRequestPacket.php} (92%) rename src/pocketmine/network/mcpe/protocol/{StructureTemplateDataExportResponsePacket.php => StructureTemplateDataResponsePacket.php} (90%) create mode 100644 src/pocketmine/network/mcpe/protocol/TickSyncPacket.php diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index 207d0c6ca..19a5a14ba 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -34,6 +34,7 @@ use pocketmine\network\mcpe\protocol\AddPaintingPacket; use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; use pocketmine\network\mcpe\protocol\AnimatePacket; +use pocketmine\network\mcpe\protocol\AnvilDamagePacket; use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket; use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket; use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; @@ -62,6 +63,8 @@ use pocketmine\network\mcpe\protocol\CraftingDataPacket; use pocketmine\network\mcpe\protocol\CraftingEventPacket; use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; +use pocketmine\network\mcpe\protocol\EducationSettingsPacket; +use pocketmine\network\mcpe\protocol\EmotePacket; use pocketmine\network\mcpe\protocol\EventPacket; use pocketmine\network\mcpe\protocol\GameRulesChangedPacket; use pocketmine\network\mcpe\protocol\GuiDataPickItemPacket; @@ -90,12 +93,15 @@ use pocketmine\network\mcpe\protocol\ModalFormResponsePacket; use pocketmine\network\mcpe\protocol\MoveActorAbsolutePacket; use pocketmine\network\mcpe\protocol\MoveActorDeltaPacket; use pocketmine\network\mcpe\protocol\MovePlayerPacket; +use pocketmine\network\mcpe\protocol\MultiplayerSettingsPacket; use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket; +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\PhotoTransferPacket; use pocketmine\network\mcpe\protocol\PlayerActionPacket; +use pocketmine\network\mcpe\protocol\PlayerAuthInputPacket; use pocketmine\network\mcpe\protocol\PlayerHotbarPacket; use pocketmine\network\mcpe\protocol\PlayerInputPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; @@ -134,6 +140,7 @@ use pocketmine\network\mcpe\protocol\SetScoreboardIdentityPacket; use pocketmine\network\mcpe\protocol\SetScorePacket; use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket; use pocketmine\network\mcpe\protocol\SetTimePacket; +use pocketmine\network\mcpe\protocol\SettingsCommandPacket; use pocketmine\network\mcpe\protocol\SetTitlePacket; use pocketmine\network\mcpe\protocol\ShowCreditsPacket; use pocketmine\network\mcpe\protocol\ShowProfilePacket; @@ -144,11 +151,12 @@ use pocketmine\network\mcpe\protocol\SpawnParticleEffectPacket; use pocketmine\network\mcpe\protocol\StartGamePacket; use pocketmine\network\mcpe\protocol\StopSoundPacket; use pocketmine\network\mcpe\protocol\StructureBlockUpdatePacket; -use pocketmine\network\mcpe\protocol\StructureTemplateDataExportRequestPacket; -use pocketmine\network\mcpe\protocol\StructureTemplateDataExportResponsePacket; +use pocketmine\network\mcpe\protocol\StructureTemplateDataRequestPacket; +use pocketmine\network\mcpe\protocol\StructureTemplateDataResponsePacket; use pocketmine\network\mcpe\protocol\SubClientLoginPacket; use pocketmine\network\mcpe\protocol\TakeItemActorPacket; use pocketmine\network\mcpe\protocol\TextPacket; +use pocketmine\network\mcpe\protocol\TickSyncPacket; use pocketmine\network\mcpe\protocol\TransferPacket; use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; use pocketmine\network\mcpe\protocol\UpdateBlockPacket; @@ -247,6 +255,10 @@ abstract class NetworkSession{ return false; } + public function handleTickSync(TickSyncPacket $packet) : bool{ + return false; + } + public function handleLevelSoundEventPacketV1(LevelSoundEventPacketV1 $packet) : bool{ return false; } @@ -675,11 +687,11 @@ abstract class NetworkSession{ return false; } - public function handleStructureTemplateDataExportRequest(StructureTemplateDataExportRequestPacket $packet) : bool{ + public function handleStructureTemplateDataRequest(StructureTemplateDataRequestPacket $packet) : bool{ return false; } - public function handleStructureTemplateDataExportResponse(StructureTemplateDataExportResponsePacket $packet) : bool{ + public function handleStructureTemplateDataResponse(StructureTemplateDataResponsePacket $packet) : bool{ return false; } @@ -695,7 +707,35 @@ abstract class NetworkSession{ return false; } + public function handleEducationSettings(EducationSettingsPacket $packet) : bool{ + return false; + } + + public function handleEmote(EmotePacket $packet) : bool{ + return false; + } + + public function handleMultiplayerSettings(MultiplayerSettingsPacket $packet) : bool{ + return false; + } + + public function handleSettingsCommand(SettingsCommandPacket $packet) : bool{ + return false; + } + + public function handleAnvilDamage(AnvilDamagePacket $packet) : bool{ + return false; + } + public function handleCompletedUsingItem(CompletedUsingItemPacket $packet) : bool{ return false; } + + public function handleNetworkSettings(NetworkSettingsPacket $packet) : bool{ + return false; + } + + public function handlePlayerAuthInput(PlayerAuthInputPacket $packet) : bool{ + return false; + } } diff --git a/src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php b/src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php new file mode 100644 index 000000000..7209297ae --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class AnvilDamagePacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::ANVIL_DAMAGE_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleAnvilDamage($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php new file mode 100644 index 000000000..684904bb6 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class EducationSettingsPacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::EDUCATION_SETTINGS_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleEducationSettings($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/EmotePacket.php b/src/pocketmine/network/mcpe/protocol/EmotePacket.php new file mode 100644 index 000000000..aabbd5e40 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/EmotePacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class EmotePacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::EMOTE_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleEmote($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php new file mode 100644 index 000000000..28a0d9145 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class MultiplayerSettingsPacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::MULTIPLAYER_SETTINGS_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleMultiplayerSettings($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php new file mode 100644 index 000000000..3342cdb4a --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class NetworkSettingsPacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::NETWORK_SETTINGS_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleNetworkSettings($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/PacketPool.php b/src/pocketmine/network/mcpe/protocol/PacketPool.php index 4c9f09d9e..3ca054104 100644 --- a/src/pocketmine/network/mcpe/protocol/PacketPool.php +++ b/src/pocketmine/network/mcpe/protocol/PacketPool.php @@ -54,6 +54,7 @@ class PacketPool{ static::registerPacket(new RiderJumpPacket()); static::registerPacket(new UpdateBlockPacket()); static::registerPacket(new AddPaintingPacket()); + static::registerPacket(new TickSyncPacket()); static::registerPacket(new LevelSoundEventPacketV1()); static::registerPacket(new LevelEventPacket()); static::registerPacket(new BlockEventPacket()); @@ -161,12 +162,19 @@ class PacketPool{ static::registerPacket(new ClientCacheStatusPacket()); static::registerPacket(new OnScreenTextureAnimationPacket()); static::registerPacket(new MapCreateLockedCopyPacket()); - static::registerPacket(new StructureTemplateDataExportRequestPacket()); - static::registerPacket(new StructureTemplateDataExportResponsePacket()); + static::registerPacket(new StructureTemplateDataRequestPacket()); + static::registerPacket(new StructureTemplateDataResponsePacket()); static::registerPacket(new UpdateBlockPropertiesPacket()); static::registerPacket(new ClientCacheBlobStatusPacket()); static::registerPacket(new ClientCacheMissResponsePacket()); + static::registerPacket(new EducationSettingsPacket()); + static::registerPacket(new EmotePacket()); + static::registerPacket(new MultiplayerSettingsPacket()); + static::registerPacket(new SettingsCommandPacket()); + static::registerPacket(new AnvilDamagePacket()); static::registerPacket(new CompletedUsingItemPacket()); + static::registerPacket(new NetworkSettingsPacket()); + static::registerPacket(new PlayerAuthInputPacket()); } /** diff --git a/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php new file mode 100644 index 000000000..26b018f5c --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class PlayerAuthInputPacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::PLAYER_AUTH_INPUT_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handlePlayerAuthInput($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php index a0b459a55..242fe0695 100644 --- a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php +++ b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php @@ -180,8 +180,8 @@ interface ProtocolInfo{ public const CLIENT_CACHE_STATUS_PACKET = 0x81; public const ON_SCREEN_TEXTURE_ANIMATION_PACKET = 0x82; public const MAP_CREATE_LOCKED_COPY_PACKET = 0x83; - public const STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET = 0x84; - public const STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET = 0x85; + public const STRUCTURE_TEMPLATE_DATA_REQUEST_PACKET = 0x84; + public const STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET = 0x85; public const UPDATE_BLOCK_PROPERTIES_PACKET = 0x86; public const CLIENT_CACHE_BLOB_STATUS_PACKET = 0x87; public const CLIENT_CACHE_MISS_RESPONSE_PACKET = 0x88; diff --git a/src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php b/src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php new file mode 100644 index 000000000..cc097d9fd --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class SettingsCommandPacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::SETTINGS_COMMAND_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleSettingsCommand($this); + } +} diff --git a/src/pocketmine/network/mcpe/protocol/StructureTemplateDataExportRequestPacket.php b/src/pocketmine/network/mcpe/protocol/StructureTemplateDataRequestPacket.php similarity index 92% rename from src/pocketmine/network/mcpe/protocol/StructureTemplateDataExportRequestPacket.php rename to src/pocketmine/network/mcpe/protocol/StructureTemplateDataRequestPacket.php index 6ae16cec4..ba100bb60 100644 --- a/src/pocketmine/network/mcpe/protocol/StructureTemplateDataExportRequestPacket.php +++ b/src/pocketmine/network/mcpe/protocol/StructureTemplateDataRequestPacket.php @@ -28,8 +28,8 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\StructureSettings; -class StructureTemplateDataExportRequestPacket extends DataPacket{ - public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_REQUEST_PACKET; +class StructureTemplateDataRequestPacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_REQUEST_PACKET; public const TYPE_ALWAYS_LOAD = 1; public const TYPE_CREATE_AND_LOAD = 2; @@ -62,6 +62,6 @@ class StructureTemplateDataExportRequestPacket extends DataPacket{ } public function handle(NetworkSession $handler) : bool{ - return $handler->handleStructureTemplateDataExportRequest($this); + return $handler->handleStructureTemplateDataRequest($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/StructureTemplateDataExportResponsePacket.php b/src/pocketmine/network/mcpe/protocol/StructureTemplateDataResponsePacket.php similarity index 90% rename from src/pocketmine/network/mcpe/protocol/StructureTemplateDataExportResponsePacket.php rename to src/pocketmine/network/mcpe/protocol/StructureTemplateDataResponsePacket.php index 2ff52b116..98afaaed7 100644 --- a/src/pocketmine/network/mcpe/protocol/StructureTemplateDataExportResponsePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StructureTemplateDataResponsePacket.php @@ -27,8 +27,8 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class StructureTemplateDataExportResponsePacket extends DataPacket{ - public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_EXPORT_RESPONSE_PACKET; +class StructureTemplateDataResponsePacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::STRUCTURE_TEMPLATE_DATA_RESPONSE_PACKET; /** @var string */ public $structureTemplateName; @@ -51,6 +51,6 @@ class StructureTemplateDataExportResponsePacket extends DataPacket{ } public function handle(NetworkSession $handler) : bool{ - return $handler->handleStructureTemplateDataExportResponse($this); + return $handler->handleStructureTemplateDataResponse($this); } } diff --git a/src/pocketmine/network/mcpe/protocol/TickSyncPacket.php b/src/pocketmine/network/mcpe/protocol/TickSyncPacket.php new file mode 100644 index 000000000..5bc022c51 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/TickSyncPacket.php @@ -0,0 +1,44 @@ + + +use pocketmine\network\mcpe\NetworkSession; + +class TickSyncPacket extends DataPacket{ + public const NETWORK_ID = ProtocolInfo::TICK_SYNC_PACKET; + + protected function decodePayload() : void{ + //TODO + } + + protected function encodePayload() : void{ + //TODO + } + + public function handle(NetworkSession $handler) : bool{ + return $handler->handleTickSync($this); + } +} From 635bb08fb9a1bb6073e1b328bd21e5e9167f0ef7 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 19:39:07 -0500 Subject: [PATCH 38/95] Update playerskinpacket to use the strings --- src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php | 2 +- src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php index 2500849f5..8d8672628 100644 --- a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php +++ b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php @@ -253,7 +253,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{ } public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{ - return $this->player->changeSkin($packet->skin, "", ""); + return $this->player->changeSkin($packet->skin, $packet->newSkinName, $packet->oldSkinName); } public function handleBookEdit(BookEditPacket $packet) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index d40901c62..82b03f05d 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -41,20 +41,20 @@ class PlayerSkinPacket extends DataPacket{ /** @var string */ public $newSkinName = ""; /** @var string */ - public $unknownString = ""; //Sent as empty, assuming it is the old skin name + public $oldSkinName = ""; protected function decodePayload(){ $this->uuid = $this->getUUID(); $this->skin = $this->getSkin(); $this->newSkinName = $this->getString(); - $this->unknownString = $this->getString(); + $this->oldSkinName = $this->getString(); } protected function encodePayload(){ $this->putUUID($this->uuid); $this->putSkin($this->skin); $this->putString($this->newSkinName); - $this->putString($this->unknownString); + $this->putString($this->oldSkinName); } public function handle(NetworkSession $session) : bool{ From e2fc7cdf880938dfde3f9b3e81acf01f367dcd41 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 21:04:38 -0500 Subject: [PATCH 39/95] Friendly BC skins (persona not supported) --- src/pocketmine/Player.php | 16 +-- src/pocketmine/entity/Human.php | 31 ++---- src/pocketmine/entity/Skin.php | 100 +++--------------- .../level/particle/FloatingTextParticle.php | 2 +- .../network/mcpe/NetworkBinaryStream.php | 28 ++--- 5 files changed, 42 insertions(+), 135 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 9ee384ba6..f3930e136 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1917,22 +1917,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->uuid = UUID::fromString($packet->clientUUID); $this->rawUUID = $this->uuid->toBinary(); - $animations = []; - foreach($packet->clientData["AnimatedImageData"] as $animatedData){ - $animations[] = new SkinAnimation(new SerializedImage($animatedData["ImageHeight"], $animatedData["ImageWidth"], base64_decode($animatedData["Image"])), $animatedData["Type"], $animatedData["Frames"]); - } $skin = new Skin( $packet->clientData["SkinId"], + base64_decode($packet->clientData["SkinData"] ?? ""), + base64_decode($packet->clientData["CapeData"] ?? ""), base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), - new SerializedImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"] ?? "")), - $animations, - new SerializedImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")), - base64_decode($packet->clientData["SkinGeometryData"] ?? ""), - base64_decode($packet->clientData["AnimationData"] ?? ""), - $packet->clientData["PremiumSkin"] ?? false, - $packet->clientData["PersonaSkin"] ?? false, - $packet->clientData["CapeOnClassicSkin"] ?? false, - $packet->clientData["CapeId"] ?? "" + base64_decode($packet->clientData["SkinGeometryData"] ?? "") ); if(!$skin->isValid()){ diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 2955d1f88..59fd5c266 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -120,16 +120,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ protected static function deserializeSkinNBT(CompoundTag $skinTag) : Skin{ $skin = new Skin( $skinTag->getString("Name"), - $skinTag->getString("SkinResourcePatch", ""), - new SerializedImage($skinTag->getInt("SkinImageHeight"), $skinTag->getInt("SkinImageWidth"), $skinTag->getByteArray("Data")), - "", //TODO: animations - new SerializedImage($skinTag->getInt("CapeImageHeight"), $skinTag->getInt("CapeImageWidth"), $skinTag->getByteArray("CapeData")), - $skinTag->getByteArray("GeometryData", ""), - $skinTag->getByteArray("AnimationData", ""), - $skinTag->getByte("PremiumSkin") === 1, - $skinTag->getByte("PersonaSkin") === 1, - $skinTag->getByte("CapeOnClassic") === 1, - $skinTag->getString("CapeId", "") + $skinTag->hasTag("Data", StringTag::class) ? $skinTag->getString("Data") : $skinTag->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM) + $skinTag->getByteArray("CapeData", ""), + $skinTag->getString("GeometryName", ""), + $skinTag->getByteArray("GeometryData", "") ); $skin->validate(); return $skin; @@ -839,19 +833,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ if($this->skin !== null){ $this->namedtag->setTag(new CompoundTag("Skin", [ new StringTag("Name", $this->skin->getSkinId()), - new StringTag("SkinResourcePatch", $this->skin->getSkinResourcePatch()), - new ByteArrayTag("Data", $this->skin->getSkinData()->getData()), - new IntTag("SkinImageHeight", $this->skin->getSkinData()->getHeight()), - new IntTag("SkinImageWidth", $this->skin->getSkinData()->getWidth()), - new ByteArrayTag("CapeData", $this->skin->getCapeData()->getData()), - new IntTag("CapeImageHeight", $this->skin->getCapeData()->getHeight()), - new IntTag("CapeImageWidth", $this->skin->getCapeData()->getWidth()), - new ByteArrayTag("GeometryData", $this->skin->getGeometryData()), - new ByteArrayTag("AnimationData", $this->skin->getAnimationData()), - new ByteTag("PremiumSkin", $this->skin->getPremium() ? 1 : 0), - new ByteTag("PersonaSkin", $this->skin->getPersona() ? 1 : 0), - new ByteTag("CapeOnClassic", $this->skin->getCapeOnClassic() ? 1 : 0), - new StringTag("CapeId", $this->skin->getCapeId()) + new ByteArrayTag("Data", $this->skin->getSkinData()), + new ByteArrayTag("CapeData", $this->skin->getCapeData()), + new StringTag("GeometryName", $this->skin->getGeometryName()), + new ByteArrayTag("GeometryData", $this->skin->getGeometryData()) ])); } } diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index 0be896b62..17630a679 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -41,38 +41,20 @@ class Skin{ /** @var string */ private $skinId; /** @var string */ - private $skinResourcePatch; - /** @var SerializedImage */ private $skinData; - /** @var SkinAnimation[] */ - private $animations = []; - /** @var SerializedImage */ + /** @var string */ private $capeData; /** @var string */ + private $geometryName; + /** @var string */ private $geometryData; - /** @var string */ - private $animationData; - /** @var bool */ - private $premium; - /** @var bool */ - private $persona; - /** @var bool */ - private $capeOnClassic; - /** @var string */ - private $capeId; - public function __construct(string $skinId, string $skinResourcePatch, SerializedImage $skinData, array $animations = [], SerializedImage $capeData = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $capeOnClassic = false, string $capeId = ""){ + public function __construct(string $skinId, string $skinData, string $capeData = "", string $geometryName = "", string $geometryData = ""){ $this->skinId = $skinId; - $this->skinResourcePatch = $skinResourcePatch; $this->skinData = $skinData; - $this->animations = $animations; $this->capeData = $capeData; + $this->geometryName = $geometryName; $this->geometryData = $geometryData; - $this->animationData = $animationData; - $this->premium = $premium; - $this->persona = $persona; - $this->capeOnClassic = $capeOnClassic; - $this->capeId = $capeId; } public static function convertToLegacyName(string $name) : string{ @@ -99,14 +81,13 @@ class Skin{ if($this->skinId === ""){ throw new \InvalidArgumentException("Skin ID must not be empty"); } - //Broken with Persona skins - /*$len = strlen($this->skinData->getData()); + $len = strlen($this->skinData); if(!in_array($len, self::ACCEPTED_SKIN_SIZES, true)){ throw new \InvalidArgumentException("Invalid skin data size $len bytes (allowed sizes: " . implode(", ", self::ACCEPTED_SKIN_SIZES) . ")"); } - if($this->capeData->getData() !== "" and strlen($this->capeData->getData()) !== 8192){ - throw new \InvalidArgumentException("Invalid cape data size " . strlen($this->capeData->getData()) . " bytes (must be exactly 8192 bytes)"); - }*/ + if($this->capeData !== "" and strlen($this->capeData) !== 8192){ + throw new \InvalidArgumentException("Invalid cape data size " . strlen($this->capeData) . " bytes (must be exactly 8192 bytes)"); + } //TODO: validate geometry } @@ -120,29 +101,22 @@ class Skin{ /** * @return string */ - public function getSkinResourcePatch() : string{ - return $this->skinResourcePatch; - } - - /** - * @return SerializedImage - */ - public function getSkinData() : SerializedImage{ + public function getSkinData() : string{ return $this->skinData; } /** - * @return SkinAnimation[] + * @return string */ - public function getAnimations() : array{ - return $this->animations; + public function getCapeData() : string{ + return $this->capeData; } /** - * @return SerializedImage + * @return string */ - public function getCapeData() : SerializedImage{ - return $this->capeData ?? new SerializedImage(0, 0, ""); + public function getGeometryName() : string{ + return $this->geometryName; } /** @@ -152,48 +126,6 @@ class Skin{ return $this->geometryData; } - /** - * @return string - */ - public function getAnimationData() : string{ - return $this->animationData; - } - - /** - * @return bool - */ - public function getPremium() : bool{ - return $this->premium; - } - - /** - * @return bool - */ - public function getPersona() : bool{ - return $this->persona; - } - - /** - * @return bool - */ - public function getCapeOnClassic() : bool{ - return $this->capeOnClassic; - } - - /** - * @return string - */ - public function getCapeId() : string{ - return $this->capeId; - } - - /** - * @return string - */ - public function getFullSkinId() : string{ - return $this->skinId . "_" . $this->capeId; - } - /** * Hack to cut down on network overhead due to skins, by un-pretty-printing geometry JSON. * diff --git a/src/pocketmine/level/particle/FloatingTextParticle.php b/src/pocketmine/level/particle/FloatingTextParticle.php index a8b859e20..7a7e622d3 100644 --- a/src/pocketmine/level/particle/FloatingTextParticle.php +++ b/src/pocketmine/level/particle/FloatingTextParticle.php @@ -97,7 +97,7 @@ class FloatingTextParticle extends Particle{ $add = new PlayerListPacket(); $add->type = PlayerListPacket::TYPE_ADD; - $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", Skin::convertToLegacyName("geometry.humanoid.custom"), SerializedImage::fromLegacy(str_repeat("\x00", 8192))))]; + $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192), "", Skin::convertToLegacyName("geometry.humanoid.custom"), ""))]; $p[] = $add; $pk = new AddPlayerPacket(); diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 5b87a2d26..7d969dd0d 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -95,29 +95,29 @@ class NetworkBinaryStream extends BinaryStream{ $capeId = $this->getString(); $fullSkinId = $this->getString(); - return new Skin( - $skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId - ); + return new Skin($skinId, $skinData->getData(), $capeData->getData(), $skinResourcePatch, $geometryData); } public function putSkin(Skin $skin){ $this->putString($skin->getSkinId()); - $this->putString($skin->getSkinResourcePatch()); - $this->putImage($skin->getSkinData()); - $this->putLInt(count($skin->getAnimations())); - foreach($skin->getAnimations() as $animation){ + $this->putString($skin->getGeometryName()); //resource patch + $this->putImage(SerializedImage::fromLegacy($skin->getSkinData())); + /** @var SkinAnimation[] $animations */ + $animations = []; + $this->putLInt(count($animations)); + foreach($animations as $animation){ $this->putImage($animation->getImage()); $this->putLInt($animation->getType()); $this->putLFloat($animation->getFrames()); } - $this->putImage($skin->getCapeData()); + $this->putImage(new SerializedImage(0, 0, $skin->getCapeData())); $this->putString($skin->getGeometryData()); - $this->putString($skin->getAnimationData()); - $this->putBool($skin->getPremium()); - $this->putBool($skin->getPersona()); - $this->putBool($skin->getCapeOnClassic()); - $this->putString($skin->getCapeId()); - $this->putString($skin->getFullSkinId()); + $this->putString(""); //animation data + $this->putBool(false); //isPremium + $this->putBool(false); //isPersona + $this->putBool(false); //isCapeOnClassic + $this->putString(""); //capeId + $this->putString(""); //fullskinId } public function getImage() : SerializedImage{ From da67a085fc162b8408e048cee304d7d1030f07f8 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 21:37:55 -0500 Subject: [PATCH 40/95] Remove unused imports and unused todo --- src/pocketmine/entity/Human.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 59fd5c266..0a9def938 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -42,7 +42,6 @@ use pocketmine\item\Totem; use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\ByteArrayTag; -use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; @@ -55,8 +54,6 @@ use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\Player; -use pocketmine\utils\SerializedImage; -use pocketmine\utils\SkinAnimation; use pocketmine\utils\UUID; use function array_filter; use function array_merge; @@ -829,7 +826,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ $this->namedtag->setTag(new ListTag("EnderChestInventory", $items, NBT::TAG_Compound)); } - //TODO: Save animations if($this->skin !== null){ $this->namedtag->setTag(new CompoundTag("Skin", [ new StringTag("Name", $this->skin->getSkinId()), From 6b97281c58e4fdd44a66c26aadb582263497d48b Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 21:41:00 -0500 Subject: [PATCH 41/95] Update SkinAnimation constants and docs --- src/pocketmine/utils/SkinAnimation.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/utils/SkinAnimation.php b/src/pocketmine/utils/SkinAnimation.php index 10092059b..91b63a89c 100644 --- a/src/pocketmine/utils/SkinAnimation.php +++ b/src/pocketmine/utils/SkinAnimation.php @@ -25,7 +25,9 @@ namespace pocketmine\utils; class SkinAnimation{ - public const TYPE_HEAD = 1; //Might want a double check on this + public const TYPE_HEAD = 1; + public const TYPE_BODY_32 = 2; + public const TYPE_BODY_64 = 3; /** @var SerializedImage */ private $image; @@ -40,14 +42,29 @@ class SkinAnimation{ $this->frames = $frames; } + /** + * Images of the animation. + * + * @return SerializedImage + */ public function getImage() : SerializedImage{ return $this->image; } + /** + * The type of animation you are applying. + * + * @return int + */ public function getType() : int{ return $this->type; } + /** + * The total amount of frames in an animation. + * + * @return float + */ public function getFrames() : float{ return $this->frames; } From 57e9fe78a3f44faf34d405f7e3212e0f56f064b5 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 21:45:33 -0500 Subject: [PATCH 42/95] change buildPlatform parameter to -1 --- src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php index 525083d03..4803a4f1d 100644 --- a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php +++ b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php @@ -54,7 +54,7 @@ class PlayerListEntry{ return $entry; } - public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = 0, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{ + public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = -1, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{ $entry = new PlayerListEntry(); $entry->uuid = $uuid; $entry->entityUniqueId = $entityUniqueId; From fb23aade34b71ff41ba8949e414fe6cc1196d060 Mon Sep 17 00:00:00 2001 From: Stephen Date: Sun, 10 Nov 2019 21:49:58 -0500 Subject: [PATCH 43/95] Clean up unused imports --- src/pocketmine/entity/Skin.php | 2 -- src/pocketmine/level/particle/FloatingTextParticle.php | 1 - 2 files changed, 3 deletions(-) diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index 17630a679..5164e4e51 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace pocketmine\entity; use Ahc\Json\Comment as CommentedJsonDecoder; -use pocketmine\utils\SerializedImage; -use pocketmine\utils\SkinAnimation; use function implode; use function in_array; use function json_encode; diff --git a/src/pocketmine/level/particle/FloatingTextParticle.php b/src/pocketmine/level/particle/FloatingTextParticle.php index 7a7e622d3..8c6f6df83 100644 --- a/src/pocketmine/level/particle/FloatingTextParticle.php +++ b/src/pocketmine/level/particle/FloatingTextParticle.php @@ -32,7 +32,6 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; -use pocketmine\utils\SerializedImage; use pocketmine\utils\UUID; use function str_repeat; From 19377c86a4959624e0111d5b8b51a253ed290393 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Mon, 11 Nov 2019 09:47:04 +0000 Subject: [PATCH 44/95] PlayerSkinPacket: reduce diff pollution --- src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index 82b03f05d..bbe513bd6 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -36,12 +36,12 @@ class PlayerSkinPacket extends DataPacket{ /** @var UUID */ public $uuid; - /** @var Skin */ - public $skin; - /** @var string */ - public $newSkinName = ""; /** @var string */ public $oldSkinName = ""; + /** @var string */ + public $newSkinName = ""; + /** @var Skin */ + public $skin; protected function decodePayload(){ $this->uuid = $this->getUUID(); From fe4354959bee495194eabed121b195ea799616da Mon Sep 17 00:00:00 2001 From: Dylan T Date: Mon, 11 Nov 2019 10:07:45 +0000 Subject: [PATCH 45/95] SkinAnimation: fix typo --- src/pocketmine/utils/SkinAnimation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/utils/SkinAnimation.php b/src/pocketmine/utils/SkinAnimation.php index 91b63a89c..b35c41cea 100644 --- a/src/pocketmine/utils/SkinAnimation.php +++ b/src/pocketmine/utils/SkinAnimation.php @@ -43,7 +43,7 @@ class SkinAnimation{ } /** - * Images of the animation. + * Image of the animation. * * @return SerializedImage */ @@ -68,4 +68,4 @@ class SkinAnimation{ public function getFrames() : float{ return $this->frames; } -} \ No newline at end of file +} From 7fcd40df15eecde41e8ad369a333273f78b52e63 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 11 Nov 2019 11:20:37 -0500 Subject: [PATCH 46/95] add encode/decode for some new packets --- .../mcpe/protocol/AnvilDamagePacket.php | 40 +++++++++++++++-- .../network/mcpe/protocol/EmotePacket.php | 43 +++++++++++++++++-- .../mcpe/protocol/NetworkSettingsPacket.php | 22 ++++++++-- .../mcpe/protocol/SettingsCommandPacket.php | 28 ++++++++++-- .../network/mcpe/protocol/TickSyncPacket.php | 35 +++++++++++++-- 5 files changed, 153 insertions(+), 15 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php b/src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php index 7209297ae..f43c596e4 100644 --- a/src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php +++ b/src/pocketmine/network/mcpe/protocol/AnvilDamagePacket.php @@ -27,15 +27,49 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class AnvilDamagePacket extends DataPacket{ +class AnvilDamagePacket extends DataPacket/* implements ServerboundPacket*/{ public const NETWORK_ID = ProtocolInfo::ANVIL_DAMAGE_PACKET; + /** @var int */ + private $x; + /** @var int */ + private $y; + /** @var int */ + private $z; + /** @var int */ + private $damageAmount; + + public static function create(int $x, int $y, int $z, int $damageAmount) : self{ + $result = new self; + [$result->x, $result->y, $result->z] = [$x, $y, $z]; + $result->damageAmount = $damageAmount; + return $result; + } + + public function getDamageAmount() : int{ + return $this->damageAmount; + } + + public function getX() : int{ + return $this->x; + } + + public function getY() : int{ + return $this->y; + } + + public function getZ() : int{ + return $this->z; + } + protected function decodePayload() : void{ - //TODO + $this->damageAmount = $this->getByte(); + $this->getBlockPosition($this->x, $this->y, $this->z); } protected function encodePayload() : void{ - //TODO + $this->putByte($this->damageAmount); + $this->putBlockPosition($this->x, $this->y, $this->z); } public function handle(NetworkSession $handler) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/EmotePacket.php b/src/pocketmine/network/mcpe/protocol/EmotePacket.php index aabbd5e40..b973b39bd 100644 --- a/src/pocketmine/network/mcpe/protocol/EmotePacket.php +++ b/src/pocketmine/network/mcpe/protocol/EmotePacket.php @@ -27,15 +27,52 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class EmotePacket extends DataPacket{ +class EmotePacket extends DataPacket/* implements ClientboundPacket, ServerboundPacket*/{ public const NETWORK_ID = ProtocolInfo::EMOTE_PACKET; + private const FLAG_SERVER = 1 << 0; + + /** @var int */ + private $entityRuntimeId; + /** @var string */ + private $emoteId; + /** @var int */ + private $flags; + + public static function create(int $entityRuntimeId, string $emoteId, int $flags) : self{ + $result = new self; + $result->entityRuntimeId = $entityRuntimeId; + $result->emoteId = $emoteId; + $result->flags = $flags; + return $result; + } + + /** + * TODO: we can't call this getEntityRuntimeId() because of base class collision (crap architecture, thanks Shoghi) + * @return int + */ + public function getEntityRuntimeIdField() : int{ + return $this->entityRuntimeId; + } + + public function getEmoteId() : string{ + return $this->emoteId; + } + + public function getFlags() : int{ + return $this->flags; + } + protected function decodePayload() : void{ - //TODO + $this->entityRuntimeId = $this->getEntityRuntimeId(); + $this->emoteId = $this->getString(); + $this->flags = $this->getByte(); } protected function encodePayload() : void{ - //TODO + $this->putEntityRuntimeId($this->entityRuntimeId); + $this->putString($this->emoteId); + $this->putByte($this->flags); } public function handle(NetworkSession $handler) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php index 3342cdb4a..f37292129 100644 --- a/src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/NetworkSettingsPacket.php @@ -27,15 +27,31 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class NetworkSettingsPacket extends DataPacket{ +class NetworkSettingsPacket extends DataPacket/* implements ClientboundPacket*/{ public const NETWORK_ID = ProtocolInfo::NETWORK_SETTINGS_PACKET; + public const COMPRESS_NOTHING = 0; + public const COMPRESS_EVERYTHING = 1; + + /** @var int */ + private $compressionThreshold; + + public static function create(int $compressionThreshold) : self{ + $result = new self; + $result->compressionThreshold = $compressionThreshold; + return $result; + } + + public function getCompressionThreshold() : int{ + return $this->compressionThreshold; + } + protected function decodePayload() : void{ - //TODO + $this->compressionThreshold = $this->getLShort(); } protected function encodePayload() : void{ - //TODO + $this->putLShort($this->compressionThreshold); } public function handle(NetworkSession $handler) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php b/src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php index cc097d9fd..d0ac8f5f5 100644 --- a/src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php +++ b/src/pocketmine/network/mcpe/protocol/SettingsCommandPacket.php @@ -27,15 +27,37 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class SettingsCommandPacket extends DataPacket{ +class SettingsCommandPacket extends DataPacket/* implements ServerboundPacket*/{ public const NETWORK_ID = ProtocolInfo::SETTINGS_COMMAND_PACKET; + /** @var string */ + private $command; + /** @var bool */ + private $suppressOutput; + + public static function create(string $command, bool $suppressOutput) : self{ + $result = new self; + $result->command = $command; + $result->suppressOutput = $suppressOutput; + return $result; + } + + public function getCommand() : string{ + return $this->command; + } + + public function getSuppressOutput() : bool{ + return $this->suppressOutput; + } + protected function decodePayload() : void{ - //TODO + $this->command = $this->getString(); + $this->suppressOutput = $this->getBool(); } protected function encodePayload() : void{ - //TODO + $this->putString($this->command); + $this->putBool($this->suppressOutput); } public function handle(NetworkSession $handler) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/TickSyncPacket.php b/src/pocketmine/network/mcpe/protocol/TickSyncPacket.php index 5bc022c51..4c14fc8aa 100644 --- a/src/pocketmine/network/mcpe/protocol/TickSyncPacket.php +++ b/src/pocketmine/network/mcpe/protocol/TickSyncPacket.php @@ -27,15 +27,44 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class TickSyncPacket extends DataPacket{ +class TickSyncPacket extends DataPacket/* implements ClientboundPacket, ServerboundPacket*/{ public const NETWORK_ID = ProtocolInfo::TICK_SYNC_PACKET; + /** @var int */ + private $clientSendTime; + /** @var int */ + private $serverReceiveTime; + + public static function request(int $clientTime) : self{ + $result = new self; + $result->clientSendTime = $clientTime; + $result->serverReceiveTime = 0; //useless + return $result; + } + + public static function response(int $clientSendTime, int $serverReceiveTime) : self{ + $result = new self; + $result->clientSendTime = $clientSendTime; + $result->serverReceiveTime = $serverReceiveTime; + return $result; + } + + public function getClientSendTime() : int{ + return $this->clientSendTime; + } + + public function getServerReceiveTime() : int{ + return $this->serverReceiveTime; + } + protected function decodePayload() : void{ - //TODO + $this->clientSendTime = $this->getLLong(); + $this->serverReceiveTime = $this->getLLong(); } protected function encodePayload() : void{ - //TODO + $this->putLLong($this->clientSendTime); + $this->putLLong($this->serverReceiveTime); } public function handle(NetworkSession $handler) : bool{ From 38c759c86eb8377dc0540ac68dd94160136aa30d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 11 Nov 2019 14:58:45 -0500 Subject: [PATCH 47/95] ResourcePackStack: missing field --- .../network/mcpe/protocol/ResourcePackStackPacket.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php index 8ec61a229..41fd445e7 100644 --- a/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php @@ -44,6 +44,8 @@ class ResourcePackStackPacket extends DataPacket{ /** @var bool */ public $isExperimental = false; + /** @var string */ + public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK; protected function decodePayload(){ $this->mustAccept = $this->getBool(); @@ -62,6 +64,7 @@ class ResourcePackStackPacket extends DataPacket{ } $this->isExperimental = $this->getBool(); + $this->baseGameVersion = $this->getString(); } protected function encodePayload(){ @@ -82,6 +85,7 @@ class ResourcePackStackPacket extends DataPacket{ } $this->putBool($this->isExperimental); + $this->putString($this->baseGameVersion); } public function handle(NetworkSession $session) : bool{ From 7b152def7db154ef86c4581062e0f4d1ab79d9c1 Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 11 Nov 2019 15:23:48 -0500 Subject: [PATCH 48/95] Move SkinAnimation and changed SerializedImage to SkinImage Also change putImage and getImage to getSkinImage and putSkinImage --- src/pocketmine/Player.php | 2 -- .../network/mcpe/NetworkBinaryStream.php | 22 +++++++++---------- .../mcpe/protocol/PlayerSkinPacket.php | 2 -- .../mcpe/protocol/types}/SkinAnimation.php | 10 ++++----- .../mcpe/protocol/types/SkinImage.php} | 6 ++--- 5 files changed, 19 insertions(+), 23 deletions(-) rename src/pocketmine/{utils => network/mcpe/protocol/types}/SkinAnimation.php (86%) rename src/pocketmine/{utils/SerializedImage.php => network/mcpe/protocol/types/SkinImage.php} (92%) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index f3930e136..c70e17772 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -160,8 +160,6 @@ use pocketmine\tile\ItemFrame; use pocketmine\tile\Spawnable; use pocketmine\tile\Tile; use pocketmine\timings\Timings; -use pocketmine\utils\SerializedImage; -use pocketmine\utils\SkinAnimation; use pocketmine\utils\TextFormat; use pocketmine\utils\UUID; use function abs; diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 890a57a19..c05b365bf 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -38,10 +38,10 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\network\mcpe\protocol\types\CommandOriginData; use pocketmine\network\mcpe\protocol\types\EntityLink; +use pocketmine\network\mcpe\protocol\types\SkinImage; +use pocketmine\network\mcpe\protocol\types\SkinAnimation; use pocketmine\network\mcpe\protocol\types\StructureSettings; use pocketmine\utils\BinaryStream; -use pocketmine\utils\SerializedImage; -use pocketmine\utils\SkinAnimation; use pocketmine\utils\UUID; use function count; use function strlen; @@ -80,13 +80,13 @@ class NetworkBinaryStream extends BinaryStream{ public function getSkin() : Skin{ $skinId = $this->getString(); $skinResourcePatch = $this->getString(); - $skinData = $this->getImage(); + $skinData = $this->getSkinImage(); $animationCount = $this->getLInt(); $animations = []; for($i = 0; $i < $animationCount; ++$i){ - $animations[] = new SkinAnimation($this->getImage(), $this->getLInt(), $this->getLFloat()); + $animations[] = new SkinAnimation($this->getSkinImage(), $this->getLInt(), $this->getLFloat()); } - $capeData = $this->getImage(); + $capeData = $this->getSkinImage(); $geometryData = $this->getString(); $animationData = $this->getString(); $premium = $this->getBool(); @@ -101,16 +101,16 @@ class NetworkBinaryStream extends BinaryStream{ public function putSkin(Skin $skin){ $this->putString($skin->getSkinId()); $this->putString($skin->getGeometryName()); //resource patch - $this->putImage(SerializedImage::fromLegacy($skin->getSkinData())); + $this->putSkinImage(SkinImage::fromLegacy($skin->getSkinData())); /** @var SkinAnimation[] $animations */ $animations = []; $this->putLInt(count($animations)); foreach($animations as $animation){ - $this->putImage($animation->getImage()); + $this->putSkinImage($animation->getImage()); $this->putLInt($animation->getType()); $this->putLFloat($animation->getFrames()); } - $this->putImage(new SerializedImage(0, 0, $skin->getCapeData())); + $this->putSkinImage(new SkinImage(0, 0, $skin->getCapeData())); $this->putString($skin->getGeometryData()); $this->putString(""); //animation data $this->putBool(false); //isPremium @@ -120,14 +120,14 @@ class NetworkBinaryStream extends BinaryStream{ $this->putString(""); //fullskinId } - public function getImage() : SerializedImage{ + public function getSkinImage() : SkinImage{ $width = $this->getLInt(); $height = $this->getLInt(); $data = $this->getString(); - return new SerializedImage($height, $width, $data); + return new SkinImage($height, $width, $data); } - public function putImage(SerializedImage $image) : void{ + public function putSkinImage(SkinImage $image) : void{ $this->putLInt($image->getWidth()); $this->putLInt($image->getHeight()); $this->putString($image->getData()); diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index bbe513bd6..219e7fbf0 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -27,8 +27,6 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\entity\Skin; use pocketmine\network\mcpe\NetworkSession; -use pocketmine\utils\SerializedImage; -use pocketmine\utils\SkinAnimation; use pocketmine\utils\UUID; class PlayerSkinPacket extends DataPacket{ diff --git a/src/pocketmine/utils/SkinAnimation.php b/src/pocketmine/network/mcpe/protocol/types/SkinAnimation.php similarity index 86% rename from src/pocketmine/utils/SkinAnimation.php rename to src/pocketmine/network/mcpe/protocol/types/SkinAnimation.php index b35c41cea..0f6726cf7 100644 --- a/src/pocketmine/utils/SkinAnimation.php +++ b/src/pocketmine/network/mcpe/protocol/types/SkinAnimation.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace pocketmine\utils; +namespace pocketmine\network\mcpe\protocol\types; class SkinAnimation{ @@ -29,14 +29,14 @@ class SkinAnimation{ public const TYPE_BODY_32 = 2; public const TYPE_BODY_64 = 3; - /** @var SerializedImage */ + /** @var SkinImage */ private $image; /** @var int */ private $type; /** @var float */ private $frames; - public function __construct(SerializedImage $image, int $type, float $frames){ + public function __construct(SkinImage $image, int $type, float $frames){ $this->image = $image; $this->type = $type; $this->frames = $frames; @@ -45,9 +45,9 @@ class SkinAnimation{ /** * Image of the animation. * - * @return SerializedImage + * @return SkinImage */ - public function getImage() : SerializedImage{ + public function getImage() : SkinImage{ return $this->image; } diff --git a/src/pocketmine/utils/SerializedImage.php b/src/pocketmine/network/mcpe/protocol/types/SkinImage.php similarity index 92% rename from src/pocketmine/utils/SerializedImage.php rename to src/pocketmine/network/mcpe/protocol/types/SkinImage.php index 6996e2208..cbd7ebc16 100644 --- a/src/pocketmine/utils/SerializedImage.php +++ b/src/pocketmine/network/mcpe/protocol/types/SkinImage.php @@ -21,9 +21,9 @@ declare(strict_types=1); -namespace pocketmine\utils; +namespace pocketmine\network\mcpe\protocol\types; -class SerializedImage{ +class SkinImage{ /** @var int */ private $height; @@ -38,7 +38,7 @@ class SerializedImage{ $this->data = $data; } - public static function fromLegacy(string $data) : SerializedImage{ + public static function fromLegacy(string $data) : SkinImage{ switch(strlen($data)){ case 64 * 32 * 4: return new self(64, 32, $data); From 49a9e0a8809484060b88ce3e0dae003e68359cb0 Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 11 Nov 2019 15:26:36 -0500 Subject: [PATCH 49/95] Remove changes to FloatingTextParticle --- src/pocketmine/level/particle/FloatingTextParticle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/level/particle/FloatingTextParticle.php b/src/pocketmine/level/particle/FloatingTextParticle.php index 8c6f6df83..d12282874 100644 --- a/src/pocketmine/level/particle/FloatingTextParticle.php +++ b/src/pocketmine/level/particle/FloatingTextParticle.php @@ -96,7 +96,7 @@ class FloatingTextParticle extends Particle{ $add = new PlayerListPacket(); $add->type = PlayerListPacket::TYPE_ADD; - $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192), "", Skin::convertToLegacyName("geometry.humanoid.custom"), ""))]; + $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192)))]; $p[] = $add; $pk = new AddPlayerPacket(); From 99d350914ec71b3613d95c515081f606514f12fc Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 11 Nov 2019 15:29:42 -0500 Subject: [PATCH 50/95] Remove convertToLegacyName --- src/pocketmine/entity/Skin.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pocketmine/entity/Skin.php b/src/pocketmine/entity/Skin.php index 5164e4e51..d5a1477ea 100644 --- a/src/pocketmine/entity/Skin.php +++ b/src/pocketmine/entity/Skin.php @@ -55,10 +55,6 @@ class Skin{ $this->geometryData = $geometryData; } - public static function convertToLegacyName(string $name) : string{ - return '{"geometry" : {"default" : "' . $name . '"}}'; - } - /** * @deprecated * @return bool From 1c67f094e37c6a619b482cb606a144bd8d2c884b Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 11 Nov 2019 15:29:57 -0500 Subject: [PATCH 51/95] Change get and put SkinImage visibility to private --- src/pocketmine/network/mcpe/NetworkBinaryStream.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index c05b365bf..03b36a544 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -120,14 +120,14 @@ class NetworkBinaryStream extends BinaryStream{ $this->putString(""); //fullskinId } - public function getSkinImage() : SkinImage{ + private function getSkinImage() : SkinImage{ $width = $this->getLInt(); $height = $this->getLInt(); $data = $this->getString(); return new SkinImage($height, $width, $data); } - public function putSkinImage(SkinImage $image) : void{ + private function putSkinImage(SkinImage $image) : void{ $this->putLInt($image->getWidth()); $this->putLInt($image->getHeight()); $this->putString($image->getData()); From 5bcbef90ea88d4174afc17bd43250c3865103516 Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 11 Nov 2019 15:32:48 -0500 Subject: [PATCH 52/95] Added variables for getSkin's animation for easier readability --- src/pocketmine/network/mcpe/NetworkBinaryStream.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 03b36a544..4c5bb7b8d 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -84,7 +84,11 @@ class NetworkBinaryStream extends BinaryStream{ $animationCount = $this->getLInt(); $animations = []; for($i = 0; $i < $animationCount; ++$i){ - $animations[] = new SkinAnimation($this->getSkinImage(), $this->getLInt(), $this->getLFloat()); + $animations[] = new SkinAnimation( + $skinImage = $this->getSkinImage(), + $animationType = $this->getLInt(), + $animationFrames = $this->getLFloat() + ); } $capeData = $this->getSkinImage(); $geometryData = $this->getString(); From a81d8dd6d53788ac59cc3cd52075ccdba72aecee Mon Sep 17 00:00:00 2001 From: Stephen Date: Mon, 11 Nov 2019 16:25:53 -0500 Subject: [PATCH 53/95] Return false in unexpected condition --- src/pocketmine/Player.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index c70e17772..2be8043a0 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -2914,7 +2914,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->sendRespawnPacket($this, RespawnPacket::READY_TO_SPAWN); } - return true; + return false; } /** From c428596009751400d4dbc82dc6d060a733142910 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 11 Nov 2019 16:36:55 -0500 Subject: [PATCH 54/95] AddPlayerPacket: missing field --- src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php b/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php index 7964b1b45..28c61b970 100644 --- a/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AddPlayerPacket.php @@ -74,6 +74,8 @@ class AddPlayerPacket extends DataPacket{ /** @var string */ public $deviceId = ""; //TODO: fill player's device ID (???) + /** @var int */ + public $buildPlatform = -1; protected function decodePayload(){ $this->uuid = $this->getUUID(); @@ -103,6 +105,7 @@ class AddPlayerPacket extends DataPacket{ } $this->deviceId = $this->getString(); + $this->buildPlatform = $this->getLInt(); } protected function encodePayload(){ @@ -133,6 +136,7 @@ class AddPlayerPacket extends DataPacket{ } $this->putString($this->deviceId); + $this->putLInt($this->buildPlatform); } public function handle(NetworkSession $session) : bool{ From ba5a5981a034ead001b9eeefc735904f5c607841 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Tue, 12 Nov 2019 08:19:05 +0000 Subject: [PATCH 55/95] Update Player.php --- src/pocketmine/Player.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 2be8043a0..f60ecc736 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -2912,6 +2912,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public function handleRespawn(RespawnPacket $packet) : bool{ if(!$this->isAlive() && $packet->respawnState === RespawnPacket::CLIENT_READY_TO_SPAWN){ $this->sendRespawnPacket($this, RespawnPacket::READY_TO_SPAWN); + return true; } return false; From ead572fab9bbb2f1b7698b11d51c4771667b9848 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Nov 2019 07:08:48 -0500 Subject: [PATCH 56/95] fix skin sharing stupidity --- src/pocketmine/network/mcpe/NetworkBinaryStream.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 4c5bb7b8d..1cb75d468 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -121,7 +121,9 @@ class NetworkBinaryStream extends BinaryStream{ $this->putBool(false); //isPersona $this->putBool(false); //isCapeOnClassic $this->putString(""); //capeId - $this->putString(""); //fullskinId + + //this has to be unique or the client will do stupid things + $this->putString(UUID::fromRandom()->toString()); //full skin ID } private function getSkinImage() : SkinImage{ From 70f81334ae60aed9c543c351eb5b940908935a01 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Nov 2019 11:58:40 -0500 Subject: [PATCH 57/95] MultiplayerSettingsPacket --- .../protocol/MultiplayerSettingsPacket.php | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php index 28a0d9145..b12afd01a 100644 --- a/src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/MultiplayerSettingsPacket.php @@ -27,15 +27,32 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; -class MultiplayerSettingsPacket extends DataPacket{ +class MultiplayerSettingsPacket extends DataPacket/* implements ServerboundPacket*/{ //TODO: this might be clientbound too, but unsure public const NETWORK_ID = ProtocolInfo::MULTIPLAYER_SETTINGS_PACKET; + public const ACTION_ENABLE_MULTIPLAYER = 0; + public const ACTION_DISABLE_MULTIPLAYER = 1; + public const ACTION_REFRESH_JOIN_CODE = 2; + + /** @var int */ + private $action; + + public static function create(int $action) : self{ + $result = new self; + $result->action = $action; + return $result; + } + + public function getAction() : int{ + return $this->action; + } + protected function decodePayload() : void{ - //TODO + $this->action = $this->getVarInt(); } protected function encodePayload() : void{ - //TODO + $this->putVarInt($this->action); } public function handle(NetworkSession $handler) : bool{ From 10d44292e1600d69719b9c7852cfbeb399c6919f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Nov 2019 12:27:50 -0500 Subject: [PATCH 58/95] fix classic capes --- src/pocketmine/network/mcpe/NetworkBinaryStream.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 1cb75d468..0f0af85cf 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -114,7 +114,11 @@ class NetworkBinaryStream extends BinaryStream{ $this->putLInt($animation->getType()); $this->putLFloat($animation->getFrames()); } - $this->putSkinImage(new SkinImage(0, 0, $skin->getCapeData())); + if($skin->getCapeData() !== ""){ + $this->putSkinImage(new SkinImage(32, 64, $skin->getCapeData())); + }else{ + $this->putSkinImage(new SkinImage(0, 0, "")); + } $this->putString($skin->getGeometryData()); $this->putString(""); //animation data $this->putBool(false); //isPremium From be9c413a9ebf40be5cfae096b3441a9edf698578 Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 20 Nov 2019 21:43:41 -0500 Subject: [PATCH 59/95] Added network components for skins, to collect instead of throw out data --- src/pocketmine/Player.php | 28 +++++++- src/pocketmine/Server.php | 5 +- src/pocketmine/entity/Human.php | 5 +- .../level/particle/FloatingTextParticle.php | 5 +- .../network/mcpe/NetworkBinaryStream.php | 37 +++++------ .../mcpe/PlayerNetworkSessionAdapter.php | 4 +- .../mcpe/protocol/PlayerListPacket.php | 3 +- .../mcpe/protocol/PlayerSkinPacket.php | 4 +- .../mcpe/protocol/types/LegacySkinAdapter.php | 37 +++++++++++ .../mcpe/protocol/types/PlayerListEntry.php | 8 +-- .../mcpe/protocol/types/SkinAdapter.php | 33 ++++++++++ .../protocol/types/SkinAdapterSingleton.php | 40 ++++++++++++ .../network/mcpe/protocol/types/SkinData.php | 64 +++++++++++++++++++ 13 files changed, 238 insertions(+), 35 deletions(-) create mode 100644 src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/SkinAdapterSingleton.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/SkinData.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index f60ecc736..c45578fe5 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -146,6 +146,9 @@ use pocketmine\network\mcpe\protocol\types\CommandParameter; use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; +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\UpdateAttributesPacket; use pocketmine\network\mcpe\protocol\UpdateBlockPacket; use pocketmine\network\mcpe\VerifyLoginTask; @@ -1915,9 +1918,32 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->uuid = UUID::fromString($packet->clientUUID); $this->rawUUID = $this->uuid->toBinary(); + $animations = []; + foreach($packet->clientData["AnimatedImageData"] as $animation){ + $animations[] = new SkinAnimation(new SkinImage($animation["ImageHeight"], $animation["ImageWidth"], $animation["Image"]), $animation["Type"], $animation["Frames"]); + } + new SkinData( + $packet->clientData["SkinId"], + base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), + new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"])), + $animations, + new SkinImage($packet->clientData["CapeImageHeight"], $packet->clientData["CapeImageWidth"], base64_decode($packet->clientData["CapeData"] ?? "")), + base64_decode($packet->clientData["SkinGeometryData"] ?? ""), + base64_decode($packet->clientData["AnimationData"] ?? ""), + $packet->clientData["PremiumSkin"] ?? false, + $packet->clientData["PersonaSkin"] ?? false, + $packet->clientData["CapeOnClassicSkin"] ?? false, + $packet->clientData["CapeId"] ?? "" + ); + + $skinData = base64_decode($packet->clientData["SkinData"]); + if((bool) $packet->clientData["PersonaSkin"]){ + $skinData = str_repeat(random_bytes(3) . "\xff", 2048); + } + $skin = new Skin( $packet->clientData["SkinId"], - base64_decode($packet->clientData["SkinData"] ?? ""), + $skinData, base64_decode($packet->clientData["CapeData"] ?? ""), base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), base64_decode($packet->clientData["SkinGeometryData"] ?? "") diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 742bd2277..62410eed5 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -76,6 +76,7 @@ use pocketmine\network\mcpe\protocol\DataPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; +use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton; use pocketmine\network\mcpe\RakLibInterface; use pocketmine\network\Network; use pocketmine\network\query\QueryHandler; @@ -2399,7 +2400,7 @@ class Server{ $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_ADD; - $pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, $skin, $xboxUserId); + $pk->entries[] = PlayerListEntry::createAdditionEntry($uuid, $entityId, $name, SkinAdapterSingleton::get()->toSkinData($skin), $xboxUserId); $this->broadcastPacket($players ?? $this->playerList, $pk); } @@ -2422,7 +2423,7 @@ class Server{ $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_ADD; foreach($this->playerList as $player){ - $pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkin(), $player->getXuid()); + $pk->entries[] = PlayerListEntry::createAdditionEntry($player->getUniqueId(), $player->getId(), $player->getDisplayName(), SkinAdapterSingleton::get()->toSkinData($player->getSkin()), $player->getXuid()); } $p->dataPacket($pk); diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 0a9def938..e0f2c9abc 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -53,6 +53,7 @@ use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\PlayerSkinPacket; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; +use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton; use pocketmine\Player; use pocketmine\utils\UUID; use function array_filter; @@ -182,7 +183,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ public function sendSkin(?array $targets = null) : void{ $pk = new PlayerSkinPacket(); $pk->uuid = $this->getUniqueId(); - $pk->skin = $this->skin; + $pk->skin = SkinAdapterSingleton::get()->toSkinData($this->skin); $this->server->broadcastPacket($targets ?? $this->hasSpawned, $pk); } @@ -850,7 +851,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ /* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */ $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_ADD; - $pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->skin)]; + $pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), SkinAdapterSingleton::get()->toSkinData($this->skin))]; $player->dataPacket($pk); } diff --git a/src/pocketmine/level/particle/FloatingTextParticle.php b/src/pocketmine/level/particle/FloatingTextParticle.php index d12282874..eea8b7d71 100644 --- a/src/pocketmine/level/particle/FloatingTextParticle.php +++ b/src/pocketmine/level/particle/FloatingTextParticle.php @@ -32,6 +32,7 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket; use pocketmine\network\mcpe\protocol\PlayerListPacket; use pocketmine\network\mcpe\protocol\RemoveActorPacket; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; +use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton; use pocketmine\utils\UUID; use function str_repeat; @@ -96,7 +97,9 @@ class FloatingTextParticle extends Particle{ $add = new PlayerListPacket(); $add->type = PlayerListPacket::TYPE_ADD; - $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, new Skin("Standard_Custom", str_repeat("\x00", 8192)))]; + $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin( + "Standard_Custom", str_repeat("\x00", 8192), "", '{"geometry" : {"default" : "geometry.humanoid.custom"}}' //TODO: Remove, hack to fix ftp + )))]; $p[] = $add; $pk = new AddPlayerPacket(); diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 0f0af85cf..96c7b8c9f 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -38,6 +38,7 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\network\mcpe\protocol\types\CommandOriginData; use pocketmine\network\mcpe\protocol\types\EntityLink; +use pocketmine\network\mcpe\protocol\types\SkinData; use pocketmine\network\mcpe\protocol\types\SkinImage; use pocketmine\network\mcpe\protocol\types\SkinAnimation; use pocketmine\network\mcpe\protocol\types\StructureSettings; @@ -77,7 +78,7 @@ class NetworkBinaryStream extends BinaryStream{ $this->putLInt($uuid->getPart(2)); } - public function getSkin() : Skin{ + public function getSkin() : SkinData{ $skinId = $this->getString(); $skinResourcePatch = $this->getString(); $skinData = $this->getSkinImage(); @@ -99,32 +100,26 @@ class NetworkBinaryStream extends BinaryStream{ $capeId = $this->getString(); $fullSkinId = $this->getString(); - return new Skin($skinId, $skinData->getData(), $capeData->getData(), $skinResourcePatch, $geometryData); + return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId); } - public function putSkin(Skin $skin){ - $this->putString($skin->getSkinId()); - $this->putString($skin->getGeometryName()); //resource patch - $this->putSkinImage(SkinImage::fromLegacy($skin->getSkinData())); - /** @var SkinAnimation[] $animations */ - $animations = []; - $this->putLInt(count($animations)); - foreach($animations as $animation){ + public function putSkin(SkinData $skin){ + $this->putString($skin->skinId); + $this->putString($skin->resourcePatch); //resource patch + $this->putSkinImage($skin->skinImage); + $this->putLInt(count($skin->animations)); + foreach($skin->animations as $animation){ $this->putSkinImage($animation->getImage()); $this->putLInt($animation->getType()); $this->putLFloat($animation->getFrames()); } - if($skin->getCapeData() !== ""){ - $this->putSkinImage(new SkinImage(32, 64, $skin->getCapeData())); - }else{ - $this->putSkinImage(new SkinImage(0, 0, "")); - } - $this->putString($skin->getGeometryData()); - $this->putString(""); //animation data - $this->putBool(false); //isPremium - $this->putBool(false); //isPersona - $this->putBool(false); //isCapeOnClassic - $this->putString(""); //capeId + $this->putSkinImage($skin->capeImage); + $this->putString($skin->geometryData); + $this->putString($skin->animationData); + $this->putBool($skin->premium); + $this->putBool($skin->persona); + $this->putBool($skin->capeOnClassic); + $this->putString($skin->capeId); //this has to be unique or the client will do stupid things $this->putString(UUID::fromRandom()->toString()); //full skin ID diff --git a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php index 8d8672628..b064ae803 100644 --- a/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php +++ b/src/pocketmine/network/mcpe/PlayerNetworkSessionAdapter.php @@ -66,6 +66,8 @@ use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket; use pocketmine\network\mcpe\protocol\ShowCreditsPacket; use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket; use pocketmine\network\mcpe\protocol\TextPacket; +use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton; +use pocketmine\network\mcpe\protocol\types\SkinData; use pocketmine\Player; use pocketmine\Server; use pocketmine\timings\Timings; @@ -253,7 +255,7 @@ class PlayerNetworkSessionAdapter extends NetworkSession{ } public function handlePlayerSkin(PlayerSkinPacket $packet) : bool{ - return $this->player->changeSkin($packet->skin, $packet->newSkinName, $packet->oldSkinName); + return $this->player->changeSkin(SkinAdapterSingleton::get()->fromSkinData($packet->skin), $packet->newSkinName, $packet->oldSkinName); } public function handleBookEdit(BookEditPacket $packet) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php index b9dff331b..47997f235 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php @@ -27,6 +27,7 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\SkinData; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use function count; @@ -81,7 +82,7 @@ class PlayerListPacket extends DataPacket{ $this->putString($entry->xboxUserId); $this->putString($entry->platformChatId); $this->putLInt($entry->buildPlatform); - $this->putSkin($entry->skin); + $this->putSkin($entry->skinData); $this->putBool($entry->isTeacher); $this->putBool($entry->isHost); }else{ diff --git a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php index 219e7fbf0..f197386b4 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerSkinPacket.php @@ -25,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol; #include -use pocketmine\entity\Skin; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\SkinData; use pocketmine\utils\UUID; class PlayerSkinPacket extends DataPacket{ @@ -38,7 +38,7 @@ class PlayerSkinPacket extends DataPacket{ public $oldSkinName = ""; /** @var string */ public $newSkinName = ""; - /** @var Skin */ + /** @var SkinData */ public $skin; protected function decodePayload(){ diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php new file mode 100644 index 000000000..8157a8510 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -0,0 +1,37 @@ +getSkinId(), $skin->getGeometryName(), SkinImage::fromLegacy($skin->getSkinData()), [], new SkinImage(32, 64, $skin->getCapeData()), $skin->getGeometryData()); + } + + public function fromSkinData(SkinData $data) : Skin{ + return new Skin($data->skinId, $data->skinImage->getData(), $data->capeImage->getData(), $data->resourcePatch, $data->geometryData); + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php index 4803a4f1d..ee46417a9 100644 --- a/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php +++ b/src/pocketmine/network/mcpe/protocol/types/PlayerListEntry.php @@ -34,8 +34,8 @@ class PlayerListEntry{ public $entityUniqueId; /** @var string */ public $username; - /** @var Skin */ - public $skin; + /** @var SkinData */ + public $skinData; /** @var string */ public $xboxUserId; /** @var string */ @@ -54,12 +54,12 @@ class PlayerListEntry{ return $entry; } - public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, Skin $skin, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = -1, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{ + public static function createAdditionEntry(UUID $uuid, int $entityUniqueId, string $username, SkinData $skinData, string $xboxUserId = "", string $platformChatId = "", int $buildPlatform = -1, bool $isTeacher = false, bool $isHost = false) : PlayerListEntry{ $entry = new PlayerListEntry(); $entry->uuid = $uuid; $entry->entityUniqueId = $entityUniqueId; $entry->username = $username; - $entry->skin = $skin; + $entry->skinData = $skinData; $entry->xboxUserId = $xboxUserId; $entry->platformChatId = $platformChatId; $entry->buildPlatform = $buildPlatform; diff --git a/src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php new file mode 100644 index 000000000..7904917fb --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php @@ -0,0 +1,33 @@ +skinId = $skinId; + $this->resourcePatch = $resourcePatch; + $this->skinImage = $skinImage; + $this->animations = $animations; + $this->capeImage = $capeImage; + $this->geometryData = $geometryData; + $this->animationData = $animationData; + $this->premium = $premium; + $this->persona = $persona; + $this->capeOnClassic = $capeOnClassic; + $this->capeId = $capeId; + } +} \ No newline at end of file From c8d0cb315b9749e0d12194a18dc8cfd7d7a1360e Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 20 Nov 2019 21:52:49 -0500 Subject: [PATCH 60/95] Move persona skin hack to legacyskinadapter --- src/pocketmine/Player.php | 17 ++++------------- .../mcpe/protocol/types/LegacySkinAdapter.php | 6 +++++- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index c45578fe5..154c7b5e2 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -146,6 +146,7 @@ use pocketmine\network\mcpe\protocol\types\CommandParameter; use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; +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; @@ -1922,7 +1923,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ foreach($packet->clientData["AnimatedImageData"] as $animation){ $animations[] = new SkinAnimation(new SkinImage($animation["ImageHeight"], $animation["ImageWidth"], $animation["Image"]), $animation["Type"], $animation["Frames"]); } - new SkinData( + + $skinData = new SkinData( $packet->clientData["SkinId"], base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), new SkinImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"])), @@ -1936,18 +1938,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $packet->clientData["CapeId"] ?? "" ); - $skinData = base64_decode($packet->clientData["SkinData"]); - if((bool) $packet->clientData["PersonaSkin"]){ - $skinData = str_repeat(random_bytes(3) . "\xff", 2048); - } - - $skin = new Skin( - $packet->clientData["SkinId"], - $skinData, - base64_decode($packet->clientData["CapeData"] ?? ""), - base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), - base64_decode($packet->clientData["SkinGeometryData"] ?? "") - ); + $skin = SkinAdapterSingleton::get()->fromSkinData($skinData); if(!$skin->isValid()){ $this->close("", "disconnectionScreen.invalidSkin"); diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index 8157a8510..8f57dfdd5 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -32,6 +32,10 @@ class LegacySkinAdapter implements SkinAdapter{ } public function fromSkinData(SkinData $data) : Skin{ - return new Skin($data->skinId, $data->skinImage->getData(), $data->capeImage->getData(), $data->resourcePatch, $data->geometryData); + $skinData = $data->skinImage->getData(); + if($data->persona){ + $skinData = str_repeat(random_bytes(3) . "\xff", 2048); + } + return new Skin($data->skinId, $skinData, $data->capeImage->getData(), $data->resourcePatch, $data->geometryData); } } \ No newline at end of file From c96ba13c23bcc302da6e5518342ba40d0904e2ef Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 20 Nov 2019 22:42:56 -0500 Subject: [PATCH 61/95] Extract geometry name from resource patch --- .../network/mcpe/protocol/types/LegacySkinAdapter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index 8f57dfdd5..721acd8a6 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -28,7 +28,7 @@ use pocketmine\entity\Skin; class LegacySkinAdapter implements SkinAdapter{ public function toSkinData(Skin $skin) : SkinData{ - return new SkinData($skin->getSkinId(), $skin->getGeometryName(), SkinImage::fromLegacy($skin->getSkinData()), [], new SkinImage(32, 64, $skin->getCapeData()), $skin->getGeometryData()); + return new SkinData($skin->getSkinId(), json_encode(["geometry" => ["default" => $skin->getGeometryName()]]), SkinImage::fromLegacy($skin->getSkinData()), [], new SkinImage(32, 64, $skin->getCapeData()), $skin->getGeometryData()); } public function fromSkinData(SkinData $data) : Skin{ @@ -36,6 +36,6 @@ class LegacySkinAdapter implements SkinAdapter{ if($data->persona){ $skinData = str_repeat(random_bytes(3) . "\xff", 2048); } - return new Skin($data->skinId, $skinData, $data->capeImage->getData(), $data->resourcePatch, $data->geometryData); + return new Skin($data->skinId, $skinData, $data->capeImage->getData(), json_decode($data->resourcePatch, true)["geometry"]["default"], $data->geometryData); } } \ No newline at end of file From 61051983137c07f92495a6e2df60e4151ac49b1a Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 20 Nov 2019 22:47:48 -0500 Subject: [PATCH 62/95] Update AvailableCommandsPacket constants --- .../mcpe/protocol/AvailableCommandsPacket.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index 9af852bba..0ea175b00 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -56,17 +56,17 @@ class AvailableCommandsPacket extends DataPacket{ public const ARG_TYPE_FILEPATH = 0x0e; - public const ARG_TYPE_STRING = 0x1b; + public const ARG_TYPE_STRING = 0x1d; - public const ARG_TYPE_POSITION = 0x1d; + public const ARG_TYPE_POSITION = 0x25; - public const ARG_TYPE_MESSAGE = 0x20; + public const ARG_TYPE_MESSAGE = 0x29; - public const ARG_TYPE_RAWTEXT = 0x22; + public const ARG_TYPE_RAWTEXT = 0x2b; - public const ARG_TYPE_JSON = 0x25; + public const ARG_TYPE_JSON = 0x2f; - public const ARG_TYPE_COMMAND = 0x2c; + public const ARG_TYPE_COMMAND = 0x36; /** * Enums are a little different: they are composed as follows: From 4340349db7bfaef9099e46d73179c87868f45c4d Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 13:52:27 -0500 Subject: [PATCH 63/95] Generate getters for SkinData, applied suggested change, and fixed an underfined variable in PlayerListPacket --- .../network/mcpe/NetworkBinaryStream.php | 24 ++-- .../mcpe/protocol/PlayerListPacket.php | 2 +- .../mcpe/protocol/types/LegacySkinAdapter.php | 8 +- .../network/mcpe/protocol/types/SkinData.php | 104 +++++++++++++++--- 4 files changed, 108 insertions(+), 30 deletions(-) diff --git a/src/pocketmine/network/mcpe/NetworkBinaryStream.php b/src/pocketmine/network/mcpe/NetworkBinaryStream.php index 96c7b8c9f..2478d5b26 100644 --- a/src/pocketmine/network/mcpe/NetworkBinaryStream.php +++ b/src/pocketmine/network/mcpe/NetworkBinaryStream.php @@ -104,22 +104,22 @@ class NetworkBinaryStream extends BinaryStream{ } public function putSkin(SkinData $skin){ - $this->putString($skin->skinId); - $this->putString($skin->resourcePatch); //resource patch - $this->putSkinImage($skin->skinImage); - $this->putLInt(count($skin->animations)); - foreach($skin->animations as $animation){ + $this->putString($skin->getSkinId()); + $this->putString($skin->getResourcePatch()); + $this->putSkinImage($skin->getSkinImage()); + $this->putLInt(count($skin->getAnimations())); + foreach($skin->getAnimations() as $animation){ $this->putSkinImage($animation->getImage()); $this->putLInt($animation->getType()); $this->putLFloat($animation->getFrames()); } - $this->putSkinImage($skin->capeImage); - $this->putString($skin->geometryData); - $this->putString($skin->animationData); - $this->putBool($skin->premium); - $this->putBool($skin->persona); - $this->putBool($skin->capeOnClassic); - $this->putString($skin->capeId); + $this->putSkinImage($skin->getCapeImage()); + $this->putString($skin->getGeometryData()); + $this->putString($skin->getAnimationData()); + $this->putBool($skin->isPremium()); + $this->putBool($skin->isPersona()); + $this->putBool($skin->isPersonaCapeOnClassic()); + $this->putString($skin->getCapeId()); //this has to be unique or the client will do stupid things $this->putString(UUID::fromRandom()->toString()); //full skin ID diff --git a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php index 47997f235..f73abb254 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerListPacket.php @@ -60,7 +60,7 @@ class PlayerListPacket extends DataPacket{ $entry->xboxUserId = $this->getString(); $entry->platformChatId = $this->getString(); $entry->buildPlatform = $this->getLInt(); - $entry->skin = $this->getSkin(); + $entry->skinData = $this->getSkin(); $entry->isTeacher = $this->getBool(); $entry->isHost = $this->getBool(); }else{ diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index 721acd8a6..0fb85414a 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -32,10 +32,10 @@ class LegacySkinAdapter implements SkinAdapter{ } public function fromSkinData(SkinData $data) : Skin{ - $skinData = $data->skinImage->getData(); - if($data->persona){ - $skinData = str_repeat(random_bytes(3) . "\xff", 2048); + $skinData = $data->getSkinImage()->getData(); + if($data->isPersona()){ + return new Skin("Standard_Custom", str_repeat(random_bytes(3) . "\xff", 2048), "", "geometry.humanoid.custom"); } - return new Skin($data->skinId, $skinData, $data->capeImage->getData(), json_decode($data->resourcePatch, true)["geometry"]["default"], $data->geometryData); + return new Skin($data->getSkinId(), $skinData, $data->getCapeImage()->getData(), json_decode($data->getResourcePatch(), true)["geometry"]["default"], $data->getGeometryData()); } } \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/types/SkinData.php b/src/pocketmine/network/mcpe/protocol/types/SkinData.php index ea356eea3..65d3af76e 100644 --- a/src/pocketmine/network/mcpe/protocol/types/SkinData.php +++ b/src/pocketmine/network/mcpe/protocol/types/SkinData.php @@ -26,29 +26,29 @@ namespace pocketmine\network\mcpe\protocol\types; class SkinData{ /** @var string */ - public $skinId; + private $skinId; /** @var string */ - public $resourcePatch; + private $resourcePatch; /** @var SkinImage */ - public $skinImage; + private $skinImage; /** @var SkinAnimation[] */ - public $animations; + private $animations; /** @var SkinImage */ - public $capeImage; + private $capeImage; /** @var string */ - public $geometryData; + private $geometryData; /** @var string */ - public $animationData; + private $animationData; /** @var bool */ - public $persona; + private $persona; /** @var bool */ - public $premium; + private $premium; /** @var bool */ - public $capeOnClassic; + private $personaCapeOnClassic; /** @var string */ - public $capeId; + private $capeId; - public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $capeOnClassic = false, string $capeId = ""){ + public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = ""){ $this->skinId = $skinId; $this->resourcePatch = $resourcePatch; $this->skinImage = $skinImage; @@ -58,7 +58,85 @@ class SkinData{ $this->animationData = $animationData; $this->premium = $premium; $this->persona = $persona; - $this->capeOnClassic = $capeOnClassic; + $this->personaCapeOnClassic = $personaCapeOnClassic; $this->capeId = $capeId; } + + /** + * @return string + */ + public function getSkinId() : string{ + return $this->skinId; + } + + /** + * @return string + */ + public function getResourcePatch() : string{ + return $this->resourcePatch; + } + + /** + * @return SkinImage + */ + public function getSkinImage() : SkinImage{ + return $this->skinImage; + } + + /** + * @return SkinAnimation[] + */ + public function getAnimations() : array{ + return $this->animations; + } + + /** + * @return SkinImage + */ + public function getCapeImage() : SkinImage{ + return $this->capeImage; + } + + /** + * @return string + */ + public function getGeometryData() : string{ + return $this->geometryData; + } + + /** + * @return string + */ + public function getAnimationData() : string{ + return $this->animationData; + } + + /** + * @return bool + */ + public function isPersona() : bool{ + return $this->persona; + } + + /** + * @return bool + */ + public function isPremium() : bool{ + return $this->premium; + } + + /** + * @return bool + */ + public function isPersonaCapeOnClassic() : bool{ + return $this->personaCapeOnClassic; + } + + /** + * @return string + */ + public function getCapeId() : string{ + return $this->capeId; + } + } \ No newline at end of file From 600e16d9f6dfeef880d52592b72ea47c229749ac Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 13:54:00 -0500 Subject: [PATCH 64/95] Fixed floatingtextparticle yet again --- src/pocketmine/level/particle/FloatingTextParticle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/level/particle/FloatingTextParticle.php b/src/pocketmine/level/particle/FloatingTextParticle.php index eea8b7d71..29ef05133 100644 --- a/src/pocketmine/level/particle/FloatingTextParticle.php +++ b/src/pocketmine/level/particle/FloatingTextParticle.php @@ -98,7 +98,7 @@ class FloatingTextParticle extends Particle{ $add = new PlayerListPacket(); $add->type = PlayerListPacket::TYPE_ADD; $add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, SkinAdapterSingleton::get()->toSkinData(new Skin( - "Standard_Custom", str_repeat("\x00", 8192), "", '{"geometry" : {"default" : "geometry.humanoid.custom"}}' //TODO: Remove, hack to fix ftp + "Standard_Custom", str_repeat("\x00", 8192), "", "geometry.humanoid.custom" )))]; $p[] = $add; From 8d06018d810f27a25d37c6f12013b2f4675b1cd1 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 13:54:22 -0500 Subject: [PATCH 65/95] make toSkinData return statement multi lined --- .../network/mcpe/protocol/types/LegacySkinAdapter.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index 0fb85414a..86a7ce923 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -28,7 +28,12 @@ use pocketmine\entity\Skin; class LegacySkinAdapter implements SkinAdapter{ public function toSkinData(Skin $skin) : SkinData{ - return new SkinData($skin->getSkinId(), json_encode(["geometry" => ["default" => $skin->getGeometryName()]]), SkinImage::fromLegacy($skin->getSkinData()), [], new SkinImage(32, 64, $skin->getCapeData()), $skin->getGeometryData()); + return new SkinData( + $skin->getSkinId(), + json_encode(["geometry" => ["default" => $skin->getGeometryName()]]), + SkinImage::fromLegacy($skin->getSkinData()), [], + new SkinImage(32, 64, $skin->getCapeData()), + $skin->getGeometryData()); } public function fromSkinData(SkinData $data) : Skin{ From 221e6db47d815c1cd8fc3af0d291a88c77b3a67d Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 14:21:08 -0500 Subject: [PATCH 66/95] Remove persona capes off of classic skins, add checks for empty cape data when converting --- .../mcpe/protocol/types/LegacySkinAdapter.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index 86a7ce923..da22c402f 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -28,19 +28,26 @@ use pocketmine\entity\Skin; class LegacySkinAdapter implements SkinAdapter{ public function toSkinData(Skin $skin) : SkinData{ + $capeData = new SkinImage(32, 64, $skin->getCapeData()); + if($capeData === ""){ + $capeData = new SkinImage(0, 0, $skin->getCapeData()); + } return new SkinData( $skin->getSkinId(), json_encode(["geometry" => ["default" => $skin->getGeometryName()]]), SkinImage::fromLegacy($skin->getSkinData()), [], - new SkinImage(32, 64, $skin->getCapeData()), - $skin->getGeometryData()); + $capeData, + $skin->getGeometryData() + ); } public function fromSkinData(SkinData $data) : Skin{ - $skinData = $data->getSkinImage()->getData(); + $capeData = $data->getCapeImage()->getData(); if($data->isPersona()){ return new Skin("Standard_Custom", str_repeat(random_bytes(3) . "\xff", 2048), "", "geometry.humanoid.custom"); + }elseif($data->isPersonaCapeOnClassic()){ + $capeData = ""; } - return new Skin($data->getSkinId(), $skinData, $data->getCapeImage()->getData(), json_decode($data->getResourcePatch(), true)["geometry"]["default"], $data->getGeometryData()); + return new Skin($data->getSkinId(), $data->getSkinImage()->getData(), $capeData, json_decode($data->getResourcePatch(), true)["geometry"]["default"], $data->getGeometryData()); } } \ No newline at end of file From 5cb0eafcb2d96fdde3e4bbde07a38fc83898a064 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 14:24:47 -0500 Subject: [PATCH 67/95] Should be checking the string, not an object --- .../network/mcpe/protocol/types/LegacySkinAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index da22c402f..4779300b4 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -29,7 +29,7 @@ class LegacySkinAdapter implements SkinAdapter{ public function toSkinData(Skin $skin) : SkinData{ $capeData = new SkinImage(32, 64, $skin->getCapeData()); - if($capeData === ""){ + if($skin->getCapeData() === ""){ $capeData = new SkinImage(0, 0, $skin->getCapeData()); } return new SkinData( From 0917b675738ef9219b34ad2a0c9bd2369771c141 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 14:46:10 -0500 Subject: [PATCH 68/95] Generate param docs for constructor --- .../network/mcpe/protocol/types/SkinData.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/SkinData.php b/src/pocketmine/network/mcpe/protocol/types/SkinData.php index 65d3af76e..025595a92 100644 --- a/src/pocketmine/network/mcpe/protocol/types/SkinData.php +++ b/src/pocketmine/network/mcpe/protocol/types/SkinData.php @@ -48,7 +48,20 @@ class SkinData{ /** @var string */ private $capeId; - public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = ""){ + /** + * @param string $skinId + * @param string $resourcePatch + * @param SkinImage $skinImage + * @param SkinAnimation[] $animations + * @param SkinImage|null $capeImage + * @param string $geometryData + * @param string $animationData + * @param bool $premium + * @param bool $persona + * @param bool $personaCapeOnClassic + * @param string $capeId + */ + public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = ""){ $this->skinId = $skinId; $this->resourcePatch = $resourcePatch; $this->skinImage = $skinImage; @@ -138,5 +151,5 @@ class SkinData{ public function getCapeId() : string{ return $this->capeId; } - + } \ No newline at end of file From 74c09dc20210d8436ac4ed264ea772f185594ef0 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 14:49:01 -0500 Subject: [PATCH 69/95] Remove inline assignment --- src/pocketmine/network/mcpe/protocol/StartGamePacket.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php index a251112aa..e4acbc61f 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -315,7 +315,8 @@ class StartGamePacket extends DataPacket{ $state->setShort("id", $v["legacy_id"]); $states->push($state); } - ($stream = new NetworkLittleEndianNBTStream())->writeTag($states); + $stream = new NetworkLittleEndianNBTStream(); + $stream->writeTag($states); return $stream->buffer; } From f682c167409b3b3fad821c606b3a3785e184fced Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 14:55:29 -0500 Subject: [PATCH 70/95] Changed eduMode to eduEditionOffer and added an unsignedVarInt in availablecommandspacket --- .../network/mcpe/protocol/AvailableCommandsPacket.php | 2 ++ src/pocketmine/network/mcpe/protocol/StartGamePacket.php | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index 0ea175b00..42607e17b 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -405,6 +405,8 @@ class AvailableCommandsPacket extends DataPacket{ foreach($this->softEnums as $enum){ $this->putSoftEnum($enum); } + + $this->putUnsignedVarInt(0); //TODO } 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 e4acbc61f..e1e2212c3 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -83,8 +83,8 @@ class StartGamePacket extends DataPacket{ public $hasAchievementsDisabled = true; /** @var int */ public $time = -1; - /** @var bool */ - public $eduMode = false; + /** @var int */ + public $eduEditionOffer = 0; /** @var bool */ public $hasEduFeaturesEnabled = false; /** @var float */ @@ -177,7 +177,7 @@ class StartGamePacket extends DataPacket{ $this->getBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ); $this->hasAchievementsDisabled = $this->getBool(); $this->time = $this->getVarInt(); - $this->eduMode = $this->getBool(); + $this->eduEditionOffer = $this->getVarInt(); $this->hasEduFeaturesEnabled = $this->getBool(); $this->rainLevel = $this->getLFloat(); $this->lightningLevel = $this->getLFloat(); @@ -249,7 +249,7 @@ class StartGamePacket extends DataPacket{ $this->putBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ); $this->putBool($this->hasAchievementsDisabled); $this->putVarInt($this->time); - $this->putBool($this->eduMode); + $this->putVarInt($this->eduEditionOffer); $this->putBool($this->hasEduFeaturesEnabled); $this->putLFloat($this->rainLevel); $this->putLFloat($this->lightningLevel); From 01d6cbe9c32b0f553354eb23ac84a117db6aff5d Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 15:01:34 -0500 Subject: [PATCH 71/95] Added : void typehint to SkinAdapterSingleton & provide documentation --- .../network/mcpe/protocol/types/SkinAdapter.php | 15 +++++++++++++++ .../mcpe/protocol/types/SkinAdapterSingleton.php | 7 +++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php index 7904917fb..56b940063 100644 --- a/src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/SkinAdapter.php @@ -25,9 +25,24 @@ namespace pocketmine\network\mcpe\protocol\types; use pocketmine\entity\Skin; +/** + * Used to convert new skin data to the skin entity or old skin entity to skin data. + */ interface SkinAdapter{ + /** + * Allows you to convert a skin entity to skin data. + * + * @param Skin $skin + * @return SkinData + */ public function toSkinData(Skin $skin) : SkinData; + /** + * Allows you to convert skin data to a skin entity. + * + * @param SkinData $data + * @return Skin + */ public function fromSkinData(SkinData $data) : Skin; } \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/types/SkinAdapterSingleton.php b/src/pocketmine/network/mcpe/protocol/types/SkinAdapterSingleton.php index 38a928e6e..b75f228b3 100644 --- a/src/pocketmine/network/mcpe/protocol/types/SkinAdapterSingleton.php +++ b/src/pocketmine/network/mcpe/protocol/types/SkinAdapterSingleton.php @@ -23,8 +23,11 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol\types; +/** + * Accessor for SkinAdapter +*/ class SkinAdapterSingleton{ - + /** @var SkinAdapter|null */ private static $skinAdapter = null; public static function get() : SkinAdapter{ @@ -34,7 +37,7 @@ class SkinAdapterSingleton{ return self::$skinAdapter; } - public static function set(SkinAdapter $adapter){ + public static function set(SkinAdapter $adapter) : void{ self::$skinAdapter = $adapter; } } \ No newline at end of file From fa56290bb475b649e3ca8b4a91833692e02fe963 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 15:05:09 -0500 Subject: [PATCH 72/95] Reflect StartGamePacket changes in Player --- src/pocketmine/Player.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 154c7b5e2..5b85e205a 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -2175,7 +2175,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $pk->spawnZ = $spawnPosition->getFloorZ(); $pk->hasAchievementsDisabled = true; $pk->time = $this->level->getTime(); - $pk->eduMode = false; + $pk->eduEditionOffer = 0; $pk->rainLevel = 0; //TODO: implement these properly $pk->lightningLevel = 0; $pk->commandsEnabled = true; From a303c4b294525651523a4f012bee2f58bf56acf0 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 21 Nov 2019 17:58:33 -0500 Subject: [PATCH 73/95] Add some isset checks to prevent server from crashing on an invalid resourcepatch --- .../network/mcpe/protocol/types/LegacySkinAdapter.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index 4779300b4..75c4c1fd9 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -43,11 +43,16 @@ class LegacySkinAdapter implements SkinAdapter{ public function fromSkinData(SkinData $data) : Skin{ $capeData = $data->getCapeImage()->getData(); + $geometryName = ""; + $resourcePatch = json_decode($data->getResourcePatch(), true); + if(isset($resourcePatch["geometry"]) && isset($resourcePatch["geometry"]["default"])){ + $geometryName = $resourcePatch["geometry"]["default"]; + } if($data->isPersona()){ return new Skin("Standard_Custom", str_repeat(random_bytes(3) . "\xff", 2048), "", "geometry.humanoid.custom"); }elseif($data->isPersonaCapeOnClassic()){ $capeData = ""; } - return new Skin($data->getSkinId(), $data->getSkinImage()->getData(), $capeData, json_decode($data->getResourcePatch(), true)["geometry"]["default"], $data->getGeometryData()); + return new Skin($data->getSkinId(), $data->getSkinImage()->getData(), $capeData, $geometryName, $data->getGeometryData()); } } \ No newline at end of file From 3dd53ad998b4f834166d35a47387a20c48881548 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 28 Nov 2019 19:42:34 -0500 Subject: [PATCH 74/95] Remove offset for crafting grid --- build/php | 2 +- src/pocketmine/inventory/CraftingGrid.php | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/build/php b/build/php index 185d74199..8666ae5ad 160000 --- a/build/php +++ b/build/php @@ -1 +1 @@ -Subproject commit 185d7419914005530298bd5e069449bdf4c0be56 +Subproject commit 8666ae5add7a8b5213d68b491ebf0de211998cc9 diff --git a/src/pocketmine/inventory/CraftingGrid.php b/src/pocketmine/inventory/CraftingGrid.php index 33221a738..4beb9ed71 100644 --- a/src/pocketmine/inventory/CraftingGrid.php +++ b/src/pocketmine/inventory/CraftingGrid.php @@ -33,15 +33,10 @@ class CraftingGrid extends BaseInventory{ public const SIZE_SMALL = 2; public const SIZE_BIG = 3; - private const SMALL_OFFSET = 28; - private const BIG_OFFSET = 32; - /** @var Player */ protected $holder; /** @var int */ private $gridWidth; - /** @var int */ - private $offset; /** @var int|null */ private $startX; @@ -55,11 +50,6 @@ class CraftingGrid extends BaseInventory{ public function __construct(Player $holder, int $gridWidth){ $this->holder = $holder; $this->gridWidth = $gridWidth; - if($this->gridWidth === self::SIZE_SMALL){ - $this->offset = self::SMALL_OFFSET; - }elseif($this->gridWidth === self::SIZE_BIG){ - $this->offset = self::BIG_OFFSET; - } parent::__construct(); } @@ -79,12 +69,8 @@ class CraftingGrid extends BaseInventory{ return "Crafting"; } - public function getItem(int $index) : Item{ - return parent::getItem($index + $this->offset); - } - public function setItem(int $index, Item $item, bool $send = true) : bool{ - if(parent::setItem($index + $this->offset, $item, $send)){ + if(parent::setItem($index, $item, $send)){ $this->seekRecipeBounds(); return true; From 5d17405b923032acaf8fa5342cfd133c3a443eba Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 28 Nov 2019 19:48:34 -0500 Subject: [PATCH 75/95] Better checks on resource patch --- .../network/mcpe/protocol/types/LegacySkinAdapter.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php index 75c4c1fd9..6434e90d4 100644 --- a/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php +++ b/src/pocketmine/network/mcpe/protocol/types/LegacySkinAdapter.php @@ -25,6 +25,9 @@ namespace pocketmine\network\mcpe\protocol\types; use pocketmine\entity\Skin; +use function is_array; +use function is_string; + class LegacySkinAdapter implements SkinAdapter{ public function toSkinData(Skin $skin) : SkinData{ @@ -45,8 +48,10 @@ class LegacySkinAdapter implements SkinAdapter{ $capeData = $data->getCapeImage()->getData(); $geometryName = ""; $resourcePatch = json_decode($data->getResourcePatch(), true); - if(isset($resourcePatch["geometry"]) && isset($resourcePatch["geometry"]["default"])){ + if(is_array($resourcePatch["geometry"]) && is_string($resourcePatch["geometry"]["default"])){ $geometryName = $resourcePatch["geometry"]["default"]; + }else{ + //TODO: Kick for invalid skin } if($data->isPersona()){ return new Skin("Standard_Custom", str_repeat(random_bytes(3) . "\xff", 2048), "", "geometry.humanoid.custom"); From 363556e9b6b8bad73828c0d38866a341ce847c4b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Nov 2019 12:31:31 +0000 Subject: [PATCH 76/95] AvailableCommandsPacket: encode & decode for enum value constraints This is a peculiarly overengineered system that is used for restricting access to enum members under certain conditions, e.g. to disallow changing specific gamerules in survival. --- .../mcpe/protocol/AvailableCommandsPacket.php | 60 ++++++++++++++++- .../protocol/types/CommandEnumConstraint.php | 67 +++++++++++++++++++ .../mcpe/protocol/types/CommandParameter.php | 3 + 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/pocketmine/network/mcpe/protocol/types/CommandEnumConstraint.php diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index 5a32a277c..ba3fa2abf 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\types\CommandData; use pocketmine\network\mcpe\protocol\types\CommandEnum; +use pocketmine\network\mcpe\protocol\types\CommandEnumConstraint; use pocketmine\network\mcpe\protocol\types\CommandParameter; use pocketmine\utils\BinaryDataException; use function count; @@ -92,6 +93,12 @@ class AvailableCommandsPacket extends DataPacket{ */ public $softEnums = []; + /** + * @var CommandEnumConstraint[] + * List of constraints for enum members. Used to constrain gamerules that can bechanged in nocheats mode and more. + */ + public $enumConstraints = []; + protected function decodePayload(){ /** @var string[] $enumValues */ $enumValues = []; @@ -118,6 +125,10 @@ class AvailableCommandsPacket extends DataPacket{ for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ $this->softEnums[] = $this->getSoftEnum(); } + + for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ + $this->enumConstraints[] = $this->getEnumConstraint($enums, $enumValues); + } } /** @@ -210,6 +221,50 @@ class AvailableCommandsPacket extends DataPacket{ } } + /** + * @param CommandEnum[] $enums + * @param string[] $enumValues + * + * @return CommandEnumConstraint + */ + protected function getEnumConstraint(array $enums, array $enumValues) : CommandEnumConstraint{ + //wtf, what was wrong with an offset inside the enum? :( + $valueIndex = $this->getLInt(); + if(!isset($enumValues[$valueIndex])){ + throw new \UnexpectedValueException("Enum constraint refers to unknown enum value index $valueIndex"); + } + $enumIndex = $this->getLInt(); + if(!isset($enums[$enumIndex])){ + throw new \UnexpectedValueException("Enum constraint refers to unknown enum index $enumIndex"); + } + $enum = $enums[$enumIndex]; + $valueOffset = array_search($enumValues[$valueIndex], $enum->enumValues, true); + if($valueOffset === false){ + throw new \UnexpectedValueException("Value \"" . $enumValues[$valueIndex] . "\" does not belong to enum \"$enum->enumName\""); + } + + $constraintIds = []; + for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ + $constraintIds[] = $this->getByte(); + } + + return new CommandEnumConstraint($enum, $valueOffset, $constraintIds); + } + + /** + * @param CommandEnumConstraint $value + * @param int[] $enumIndexes string enum name -> int index + * @param int[] $enumValueIndexes string value -> int index + */ + protected function putEnumConstraint(CommandEnumConstraint $constraint, array $enumIndexes, array $enumValueIndexes) : void{ + $this->putLInt($enumValueIndexes[$constraint->getAffectedValue()]); + $this->putLInt($enumIndexes[$constraint->getEnum()->enumName]); + $this->putUnsignedVarInt(count($constraint->getConstraints())); + foreach($constraint->getConstraints() as $v){ + $this->putByte($v); + } + } + /** * @param CommandEnum[] $enums * @param string[] $postfixes @@ -407,7 +462,10 @@ class AvailableCommandsPacket extends DataPacket{ $this->putSoftEnum($enum); } - $this->putUnsignedVarInt(0); //TODO + $this->putUnsignedVarInt(count($this->enumConstraints)); + foreach($this->enumConstraints as $constraint){ + $this->putEnumConstraint($constraint, $enumIndexes, $enumValueIndexes); + } } public function handle(NetworkSession $session) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/types/CommandEnumConstraint.php b/src/pocketmine/network/mcpe/protocol/types/CommandEnumConstraint.php new file mode 100644 index 000000000..a97bb9cda --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/CommandEnumConstraint.php @@ -0,0 +1,67 @@ +enumValues[$valueOffset])){ + throw new \InvalidArgumentException("Invalid enum value offset $valueOffset"); + } + $this->enum = $enum; + $this->valueOffset = $valueOffset; + $this->constraints = $constraints; + } + + public function getEnum() : CommandEnum{ + return $this->enum; + } + + public function getValueOffset() : int{ + return $this->valueOffset; + } + + public function getAffectedValue() : string{ + return $this->enum->enumValues[$this->valueOffset]; + } + + /** + * @return int[] + */ + public function getConstraints() : array{ + return $this->constraints; + } +} diff --git a/src/pocketmine/network/mcpe/protocol/types/CommandParameter.php b/src/pocketmine/network/mcpe/protocol/types/CommandParameter.php index b1a00137b..a2f1a26ed 100644 --- a/src/pocketmine/network/mcpe/protocol/types/CommandParameter.php +++ b/src/pocketmine/network/mcpe/protocol/types/CommandParameter.php @@ -24,6 +24,9 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol\types; class CommandParameter{ + public const FLAG_FORCE_COLLAPSE_ENUM = 0x1; + public const FLAG_HAS_ENUM_CONSTRAINT = 0x2; + /** @var string */ public $paramName; /** @var int */ From 76bd0f452cf0584f5919b6265084884f88fd0dd4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Nov 2019 12:41:44 +0000 Subject: [PATCH 77/95] AvailableCommandsPacket: add special handling for enums which aren't referenced by any command directly the CommandName enum is a magic enum used by the argtype. TODO: It's possible that not sending the CommandName enum might be causing client sided crashes. Investigate. --- .../mcpe/protocol/AvailableCommandsPacket.php | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index ba3fa2abf..719e36dfd 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -75,6 +75,10 @@ class AvailableCommandsPacket extends DataPacket{ */ public const ARG_FLAG_ENUM = 0x200000; + public const HARDCODED_ENUM_NAMES = [ + "CommandName" => true + ]; + /** * This is used for /xp L. It can only be applied to integer parameters. */ @@ -86,6 +90,13 @@ class AvailableCommandsPacket extends DataPacket{ */ public $commandData = []; + /** + * @var CommandEnum[] + * List of enums which aren't directly referenced by any vanilla command. + * This is used for the `CommandName` enum, which is a magic enum used by the `command` argument type. + */ + public $hardcodedEnums = []; + /** * @var CommandEnum[] * List of dynamic command enums, also referred to as "soft" enums. These can by dynamically updated mid-game @@ -115,7 +126,10 @@ class AvailableCommandsPacket extends DataPacket{ /** @var CommandEnum[] $enums */ $enums = []; for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ - $enums[] = $this->getEnum($enumValues); + $enums[] = $enum = $this->getEnum($enumValues); + if(isset(self::HARDCODED_ENUM_NAMES[$enum->enumName])){ + $this->hardcodedEnums[] = $enum; + } } for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ @@ -404,15 +418,21 @@ class AvailableCommandsPacket extends DataPacket{ $enumIndexes = []; /** @var CommandEnum[] $enums */ $enums = []; + + $addEnumFn = static function(CommandEnum $enum) use (&$enums, &$enumIndexes, &$enumValueIndexes){ + if(!isset($enumIndexes[$enum->enumName])){ + $enums[$enumIndexes[$enum->enumName] = count($enumIndexes)] = $enum; + } + foreach($enum->enumValues as $str){ + $enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes); + } + }; + foreach($this->hardcodedEnums as $enum){ + $addEnumFn($enum); + } foreach($this->commandData as $commandData){ if($commandData->aliases !== null){ - if(!isset($enumIndexes[$commandData->aliases->enumName])){ - $enums[$enumIndexes[$commandData->aliases->enumName] = count($enumIndexes)] = $commandData->aliases; - } - - foreach($commandData->aliases->enumValues as $str){ - $enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes); //latest index - } + $addEnumFn($commandData->aliases); } foreach($commandData->overloads as $overload){ @@ -422,12 +442,7 @@ class AvailableCommandsPacket extends DataPacket{ */ foreach($overload as $parameter){ if($parameter->enum !== null){ - if(!isset($enumIndexes[$parameter->enum->enumName])){ - $enums[$enumIndexes[$parameter->enum->enumName] = count($enumIndexes)] = $parameter->enum; - } - foreach($parameter->enum->enumValues as $str){ - $enumValueIndexes[$str] = $enumValueIndexes[$str] ?? count($enumValueIndexes); - } + $addEnumFn($parameter->enum); } if($parameter->postfix !== null){ From b7a5a53c9df3d735fa9c054d22eac8e35fda08a3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Nov 2019 12:56:16 +0000 Subject: [PATCH 78/95] MoveActorDeltaPacket: flags is now a short --- src/pocketmine/network/mcpe/protocol/MoveActorDeltaPacket.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/MoveActorDeltaPacket.php b/src/pocketmine/network/mcpe/protocol/MoveActorDeltaPacket.php index 21ee91309..c92265abd 100644 --- a/src/pocketmine/network/mcpe/protocol/MoveActorDeltaPacket.php +++ b/src/pocketmine/network/mcpe/protocol/MoveActorDeltaPacket.php @@ -70,7 +70,7 @@ class MoveActorDeltaPacket extends DataPacket{ protected function decodePayload(){ $this->entityRuntimeId = $this->getEntityRuntimeId(); - $this->flags = $this->getByte(); + $this->flags = $this->getLShort(); $this->xDiff = $this->maybeReadCoord(self::FLAG_HAS_X); $this->yDiff = $this->maybeReadCoord(self::FLAG_HAS_Y); $this->zDiff = $this->maybeReadCoord(self::FLAG_HAS_Z); @@ -93,7 +93,7 @@ class MoveActorDeltaPacket extends DataPacket{ protected function encodePayload(){ $this->putEntityRuntimeId($this->entityRuntimeId); - $this->putByte($this->flags); + $this->putLShort($this->flags); $this->maybeWriteCoord(self::FLAG_HAS_X, $this->xDiff); $this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yDiff); $this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zDiff); From d8188b807a3d3e708c32483f463342da12d8f3a1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Nov 2019 21:18:54 +0000 Subject: [PATCH 79/95] CraftingDataPacket: read & write potion recipes --- .../mcpe/protocol/CraftingDataPacket.php | 30 +++++++++++ .../types/PotionContainerChangeRecipe.php | 51 +++++++++++++++++++ .../mcpe/protocol/types/PotionTypeRecipe.php | 51 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/pocketmine/network/mcpe/protocol/types/PotionContainerChangeRecipe.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php diff --git a/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php index bb5f7cd8f..a05d2bc27 100644 --- a/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php +++ b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php @@ -33,6 +33,8 @@ use pocketmine\item\Item; use pocketmine\item\ItemFactory; use pocketmine\network\mcpe\NetworkBinaryStream; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\PotionContainerChangeRecipe; +use pocketmine\network\mcpe\protocol\types\PotionTypeRecipe; #ifndef COMPILE use pocketmine\utils\Binary; #endif @@ -53,6 +55,10 @@ class CraftingDataPacket extends DataPacket{ /** @var object[] */ public $entries = []; + /** @var PotionTypeRecipe[] */ + public $potionTypeRecipes = []; + /** @var PotionContainerChangeRecipe[] */ + public $potionContainerRecipes = []; /** @var bool */ public $cleanRecipes = false; @@ -140,6 +146,18 @@ class CraftingDataPacket extends DataPacket{ } $this->decodedEntries[] = $entry; } + for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ + $input = $this->getVarInt(); + $ingredient = $this->getVarInt(); + $output = $this->getVarInt(); + $this->potionTypeRecipes[] = new PotionTypeRecipe($input, $ingredient, $output); + } + for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ + $input = $this->getVarInt(); + $ingredient = $this->getVarInt(); + $output = $this->getVarInt(); + $this->potionContainerRecipes[] = new PotionContainerChangeRecipe($input, $ingredient, $output); + } $this->cleanRecipes = $this->getBool(); } @@ -240,6 +258,18 @@ class CraftingDataPacket extends DataPacket{ $writer->reset(); } + $this->putUnsignedVarInt(count($this->potionTypeRecipes)); + foreach($this->potionTypeRecipes as $recipe){ + $this->putVarInt($recipe->getInputPotionType()); + $this->putVarInt($recipe->getIngredientItemId()); + $this->putVarInt($recipe->getOutputPotionType()); + } + $this->putUnsignedVarInt(count($this->potionContainerRecipes)); + foreach($this->potionContainerRecipes as $recipe){ + $this->putVarInt($recipe->getInputItemId()); + $this->putVarInt($recipe->getIngredientItemId()); + $this->putVarInt($recipe->getOutputItemId()); + } $this->putBool($this->cleanRecipes); } diff --git a/src/pocketmine/network/mcpe/protocol/types/PotionContainerChangeRecipe.php b/src/pocketmine/network/mcpe/protocol/types/PotionContainerChangeRecipe.php new file mode 100644 index 000000000..8d3ad196e --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/PotionContainerChangeRecipe.php @@ -0,0 +1,51 @@ +inputItemId = $inputItemId; + $this->ingredientItemId = $ingredientItemId; + $this->outputItemId = $outputItemId; + } + + public function getInputItemId() : int{ + return $this->inputItemId; + } + + public function getIngredientItemId() : int{ + return $this->ingredientItemId; + } + + public function getOutputItemId() : int{ + return $this->outputItemId; + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php b/src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php new file mode 100644 index 000000000..faacb6bf1 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/PotionTypeRecipe.php @@ -0,0 +1,51 @@ +inputPotionType = $inputPotionType; + $this->ingredientItemId = $ingredientItemId; + $this->outputPotionType = $outputPotionType; + } + + public function getInputPotionType() : int{ + return $this->inputPotionType; + } + + public function getIngredientItemId() : int{ + return $this->ingredientItemId; + } + + public function getOutputPotionType() : int{ + return $this->outputPotionType; + } +} \ No newline at end of file From 8594cb3e7438591d840468fdda492415560a4a2b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Dec 2019 08:17:00 +0000 Subject: [PATCH 80/95] AvailableCommandsPacket: fixed doc comment for putEnumConstraint() --- .../network/mcpe/protocol/AvailableCommandsPacket.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index 719e36dfd..b4fb969e2 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -266,7 +266,7 @@ class AvailableCommandsPacket extends DataPacket{ } /** - * @param CommandEnumConstraint $value + * @param CommandEnumConstraint $constraint * @param int[] $enumIndexes string enum name -> int index * @param int[] $enumValueIndexes string value -> int index */ From abf8081ebcbb31c044188f7e7fda5496828ea852 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Dec 2019 08:21:42 +0000 Subject: [PATCH 81/95] RuntimeBlockMapping: add a type check for decoded NBT root type this is kinda redundant since this function can blow up in so many other ways anyway, but it makes PHPStan happy, so it'll do. --- .../network/mcpe/protocol/types/RuntimeBlockMapping.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php index 03a383e69..ccb92e6e3 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -53,6 +53,9 @@ final class RuntimeBlockMapping{ public static function init() : void{ $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); $tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/runtime_block_states.dat")); + if(!($tag instanceof CompoundTag)){ //this is a little redundant currently, but good for auto complete and makes phpstan happy + throw new \RuntimeException("Invalid blockstates table, expected CompoundTag root"); + } $decompressed = []; From f37ea6a20349e23bd5076b86b945d9c37bf200e0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Dec 2019 12:57:05 +0000 Subject: [PATCH 82/95] AvailableCommandsPacket: fix nonsensical placement of HARDCODED_ENUMS --- .../network/mcpe/protocol/AvailableCommandsPacket.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php index b4fb969e2..0a94ca9a8 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableCommandsPacket.php @@ -75,15 +75,15 @@ class AvailableCommandsPacket extends DataPacket{ */ public const ARG_FLAG_ENUM = 0x200000; - public const HARDCODED_ENUM_NAMES = [ - "CommandName" => true - ]; - /** * This is used for /xp L. It can only be applied to integer parameters. */ public const ARG_FLAG_POSTFIX = 0x1000000; + public const HARDCODED_ENUM_NAMES = [ + "CommandName" => true + ]; + /** * @var CommandData[] * List of command data, including name, description, alias indexes and parameters. From 85521f5e7a34ea07034d3715b46e47548a570783 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Dec 2019 13:57:19 +0000 Subject: [PATCH 83/95] EducationSettingsPacket: added encode & decode --- .../mcpe/protocol/EducationSettingsPacket.php | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php b/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php index 684904bb6..e8f5eea4e 100644 --- a/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php +++ b/src/pocketmine/network/mcpe/protocol/EducationSettingsPacket.php @@ -30,12 +30,34 @@ use pocketmine\network\mcpe\NetworkSession; class EducationSettingsPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::EDUCATION_SETTINGS_PACKET; + /** @var string */ + private $codeBuilderDefaultUri; + /** @var bool */ + private $hasQuiz; + + public static function create(string $codeBuilderDefaultUri, bool $hasQuiz) : self{ + $result = new self; + $result->codeBuilderDefaultUri = $codeBuilderDefaultUri; + $result->hasQuiz = $hasQuiz; + return $result; + } + + public function getCodeBuilderDefaultUri() : string{ + return $this->codeBuilderDefaultUri; + } + + public function getHasQuiz() : bool{ + return $this->hasQuiz; + } + protected function decodePayload() : void{ - //TODO + $this->codeBuilderDefaultUri = $this->getString(); + $this->hasQuiz = $this->getBool(); } protected function encodePayload() : void{ - //TODO + $this->putString($this->codeBuilderDefaultUri); + $this->putBool($this->hasQuiz); } public function handle(NetworkSession $handler) : bool{ From 20b74189161ded96e9c503a9c8fdf243c3523af8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Dec 2019 15:09:45 +0000 Subject: [PATCH 84/95] PlayerAuthInputPacket: added encode & decode, and some aux classes --- .../mcpe/protocol/PlayerAuthInputPacket.php | 137 +++++++++++++++++- .../network/mcpe/protocol/types/InputMode.php | 36 +++++ .../network/mcpe/protocol/types/PlayMode.php | 45 ++++++ 3 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 src/pocketmine/network/mcpe/protocol/types/InputMode.php create mode 100644 src/pocketmine/network/mcpe/protocol/types/PlayMode.php diff --git a/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php b/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php index 26b018f5c..91cac5c16 100644 --- a/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php +++ b/src/pocketmine/network/mcpe/protocol/PlayerAuthInputPacket.php @@ -25,17 +25,148 @@ namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\math\Vector3; use pocketmine\network\mcpe\NetworkSession; +use pocketmine\network\mcpe\protocol\types\InputMode; +use pocketmine\network\mcpe\protocol\types\PlayMode; +use function assert; -class PlayerAuthInputPacket extends DataPacket{ +class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{ public const NETWORK_ID = ProtocolInfo::PLAYER_AUTH_INPUT_PACKET; + /** @var Vector3 */ + private $position; + /** @var float */ + private $pitch; + /** @var float */ + private $yaw; + /** @var float */ + private $headYaw; + /** @var float */ + private $moveVecX; + /** @var float */ + private $moveVecZ; + /** @var int */ + private $inputFlags; + /** @var int */ + private $inputMode; + /** @var int */ + private $playMode; + /** @var Vector3|null */ + private $vrGazeDirection = null; + + /** + * @param Vector3 $position + * @param float $pitch + * @param float $yaw + * @param float $headYaw + * @param float $moveVecX + * @param float $moveVecZ + * @param int $inputFlags + * @param int $inputMode @see InputMode + * @param int $playMode @see PlayMode + * @param Vector3|null $vrGazeDirection only used when PlayMode::VR + * + * @return self + */ + public static function create(Vector3 $position, float $pitch, float $yaw, float $headYaw, float $moveVecX, float $moveVecZ, int $inputFlags, int $inputMode, int $playMode, ?Vector3 $vrGazeDirection = null) : self{ + if($playMode === PlayMode::VR and $vrGazeDirection === null){ + //yuck, can we get a properly written packet just once? ... + throw new \InvalidArgumentException("Gaze direction must be provided for VR play mode"); + } + $result = new self; + $result->position = $position->asVector3(); + $result->pitch = $pitch; + $result->yaw = $yaw; + $result->headYaw = $headYaw; + $result->moveVecX = $moveVecX; + $result->moveVecZ = $moveVecZ; + $result->inputFlags = $inputFlags; + $result->inputMode = $inputMode; + $result->playMode = $playMode; + if($vrGazeDirection !== null){ + $this->vrGazeDirection = $vrGazeDirection->asVector3(); + } + return $result; + } + + public function getPosition() : Vector3{ + return $this->position; + } + + public function getPitch() : float{ + return $this->pitch; + } + + public function getYaw() : float{ + return $this->yaw; + } + + public function getHeadYaw() : float{ + return $this->headYaw; + } + + public function getMoveVecX() : float{ + return $this->moveVecX; + } + + public function getMoveVecZ() : float{ + return $this->moveVecZ; + } + + public function getInputFlags() : int{ + return $this->inputFlags; + } + + /** + * @see InputMode + * @return int + */ + public function getInputMode() : int{ + return $this->inputMode; + } + + /** + * @see PlayMode + * @return int + */ + public function getPlayMode() : int{ + return $this->playMode; + } + + public function getVrGazeDirection() : ?Vector3{ + return $this->vrGazeDirection; + } + protected function decodePayload() : void{ - //TODO + $this->yaw = $this->getLFloat(); + $this->pitch = $this->getLFloat(); + $this->position = $this->getVector3(); + $this->moveVecX = $this->getLFloat(); + $this->moveVecZ = $this->getLFloat(); + $this->headYaw = $this->getLFloat(); + $this->inputFlags = $this->getUnsignedVarLong(); + $this->inputMode = $this->getUnsignedVarInt(); + $this->playMode = $this->getUnsignedVarInt(); + if($this->playMode === PlayMode::VR){ + $this->vrGazeDirection = $this->getVector3(); + } } protected function encodePayload() : void{ - //TODO + $this->putLFloat($this->yaw); + $this->putLFloat($this->pitch); + $this->putVector3($this->position); + $this->putLFloat($this->moveVecX); + $this->putLFloat($this->moveVecZ); + $this->putLFloat($this->headYaw); + $this->putUnsignedVarLong($this->inputFlags); + $this->putUnsignedVarInt($this->inputMode); + $this->putUnsignedVarInt($this->playMode); + if($this->playMode === PlayMode::VR){ + assert($this->vrGazeDirection !== null); + $this->putVector3($this->vrGazeDirection); + } } public function handle(NetworkSession $handler) : bool{ diff --git a/src/pocketmine/network/mcpe/protocol/types/InputMode.php b/src/pocketmine/network/mcpe/protocol/types/InputMode.php new file mode 100644 index 000000000..48b1dccb7 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/InputMode.php @@ -0,0 +1,36 @@ + Date: Mon, 2 Dec 2019 19:35:55 +0000 Subject: [PATCH 85/95] StartGamePacket: fixed asymmetry in block table handling --- .../network/mcpe/protocol/StartGamePacket.php | 31 +++---------------- .../protocol/types/RuntimeBlockMapping.php | 20 +++++++++--- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php index e1e2212c3..64084152c 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -153,7 +153,7 @@ class StartGamePacket extends DataPacket{ /** @var string */ public $multiplayerCorrelationId = ""; //TODO: this should be filled with a UUID of some sort - /** @var array|null ["name" (string), "data" (int16), "legacy_id" (int16)] */ + /** @var ListTag|null */ public $blockTable = null; /** @var array|null string (name) => int16 (legacyID) */ public $itemTable = null; @@ -211,14 +211,8 @@ class StartGamePacket extends DataPacket{ $this->enchantmentSeed = $this->getVarInt(); - $this->blockTable = []; - for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ - $id = $this->getString(); - $data = $this->getSignedLShort(); - $unknown = $this->getSignedLShort(); + $this->blockTable = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512); - $this->blockTable[$i] = ["name" => $id, "data" => $data, "legacy_id" => $unknown]; - } $this->itemTable = []; for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ $id = $this->getString(); @@ -286,11 +280,11 @@ class StartGamePacket extends DataPacket{ if($this->blockTable === null){ if(self::$blockTableCache === null){ //this is a really nasty hack, but it'll do for now - self::$blockTableCache = self::serializeBlockTable(RuntimeBlockMapping::getBedrockKnownStates()); + self::$blockTableCache = (new NetworkLittleEndianNBTStream())->write(RuntimeBlockMapping::generateBlockTable()); } $this->put(self::$blockTableCache); }else{ - $this->put(self::serializeBlockTable($this->blockTable)); + $this->put((new NetworkLittleEndianNBTStream())->write($this->blockTable)); } if($this->itemTable === null){ if(self::$itemTableCache === null){ @@ -304,23 +298,6 @@ class StartGamePacket extends DataPacket{ $this->putString($this->multiplayerCorrelationId); } - private static function serializeBlockTable(array $table) : string{ - $states = new ListTag(); - foreach($table as $v){ - $state = new CompoundTag(); - $state->setTag(new CompoundTag("block", [ - new StringTag("name", $v["name"]), - $v["states"] - ])); - $state->setShort("id", $v["legacy_id"]); - $states->push($state); - } - $stream = new NetworkLittleEndianNBTStream(); - $stream->writeTag($states); - - return $stream->buffer; - } - private static function serializeItemTable(array $table) : string{ $stream = new NetworkBinaryStream(); $stream->putUnsignedVarInt(count($table)); diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php index ccb92e6e3..0709582c8 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -26,6 +26,8 @@ namespace pocketmine\network\mcpe\protocol\types; use pocketmine\block\BlockIds; use pocketmine\nbt\BigEndianNBTStream; use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\tag\ListTag; +use pocketmine\nbt\tag\StringTag; use pocketmine\utils\BinaryDataException; use function file_get_contents; use function getmypid; @@ -139,11 +141,19 @@ final class RuntimeBlockMapping{ self::$runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta; } - /** - * @return array - */ - public static function getBedrockKnownStates() : array{ + public static function generateBlockTable() : ListTag{ self::lazyInit(); - return self::$bedrockKnownStates; + $states = new ListTag(); + //TODO: this assoc array mess really doesn't make sense anymore, we can store NBT directly + foreach(self::$bedrockKnownStates as $v){ + $state = new CompoundTag(); + $state->setTag(new CompoundTag("block", [ + new StringTag("name", $v["name"]), + $v["states"] + ])); + $state->setShort("id", $v["legacy_id"]); + $states->push($state); + } + return $states; } } From 7fdfe947b074d39d1806772987c1c2a43e6bb609 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Dec 2019 20:15:22 +0000 Subject: [PATCH 86/95] inventory: fix some transactions being rejected for no good reason since 1.13, transactions such as interacting with creative inventory cause a spoof windowID 124 slot 50 action to appear which changes air -> air. This currently gets rejected because only cursor is mapped to ID 124, and it only has a single slot. It is not clear what the purpose of 124:50 is, but this fix filters out any actions which do not change anything, since they won't affect transaction balance anyway. --- .../network/mcpe/protocol/types/NetworkInventoryAction.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php index abe15d8b8..ecd9eff5d 100644 --- a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php +++ b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php @@ -167,6 +167,10 @@ class NetworkInventoryAction{ * @throws \UnexpectedValueException */ public function createInventoryAction(Player $player){ + if($this->oldItem->equalsExact($this->newItem)){ + //filter out useless noise in 1.13 + return null; + } switch($this->sourceType){ case self::SOURCE_CONTAINER: $window = $player->getWindow($this->windowId); From 740f0a2314ac6ae034a917a76cdabd8e56f3af97 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 2 Dec 2019 21:50:52 +0000 Subject: [PATCH 87/95] crafting now works on 1.13, but it's not good --- .../protocol/InventoryTransactionPacket.php | 21 +++++++++- .../protocol/types/NetworkInventoryAction.php | 39 ++++++++++++------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index cd6bddc97..2c423397a 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\ContainerIds; use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction; use function count; @@ -74,7 +75,25 @@ class InventoryTransactionPacket extends DataPacket{ $this->transactionType = $this->getUnsignedVarInt(); for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ - $this->actions[] = (new NetworkInventoryAction())->read($this); + $this->actions[] = $action = (new NetworkInventoryAction())->read($this); + + if( + $action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER and + $action->windowId === ContainerIds::UI and + $action->inventorySlot === 50 + ){ + $this->isCraftingPart = true; + if(!$action->oldItem->isNull() and $action->newItem->isNull()){ + $this->isFinalCraftingPart = true; + } + }elseif( + $action->sourceType === NetworkInventoryAction::SOURCE_TODO and ( + $action->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or + $action->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT + ) + ){ + $this->isCraftingPart = true; + } } $this->trData = new \stdClass(); diff --git a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php index ecd9eff5d..54ca69682 100644 --- a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php +++ b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol\types; +use pocketmine\inventory\CraftingGrid; use pocketmine\inventory\transaction\action\CreativeInventoryAction; use pocketmine\inventory\transaction\action\DropItemAction; use pocketmine\inventory\transaction\action\InventoryAction; @@ -36,7 +37,6 @@ class NetworkInventoryAction{ public const SOURCE_WORLD = 2; //drop/pickup item entity public const SOURCE_CREATIVE = 3; - public const SOURCE_CRAFTING_GRID = 100; public const SOURCE_TODO = 99999; /** @@ -108,17 +108,8 @@ class NetworkInventoryAction{ break; case self::SOURCE_CREATIVE: break; - case self::SOURCE_CRAFTING_GRID: case self::SOURCE_TODO: $this->windowId = $packet->getVarInt(); - switch($this->windowId){ - /** @noinspection PhpMissingBreakStatementInspection */ - case self::SOURCE_TYPE_CRAFTING_RESULT: - $packet->isFinalCraftingPart = true; - case self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT: - $packet->isCraftingPart = true; - break; - } break; default: throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType"); @@ -146,7 +137,6 @@ class NetworkInventoryAction{ break; case self::SOURCE_CREATIVE: break; - case self::SOURCE_CRAFTING_GRID: case self::SOURCE_TODO: $packet->putVarInt($this->windowId); break; @@ -173,9 +163,31 @@ class NetworkInventoryAction{ } switch($this->sourceType){ case self::SOURCE_CONTAINER: - $window = $player->getWindow($this->windowId); + if($this->windowId === ContainerIds::UI and $this->inventorySlot > 0){ + if($this->inventorySlot === 50){ + return null; //useless noise + } + if($this->inventorySlot >= 28 and $this->inventorySlot <= 31){ + $window = $player->getCraftingGrid(); + if($window->getGridWidth() !== CraftingGrid::SIZE_SMALL){ + throw new \UnexpectedValueException("Expected small crafting grid"); + } + $slot = $this->inventorySlot - 28; + }elseif($this->inventorySlot >= 32 and $this->inventorySlot <= 40){ + $window = $player->getCraftingGrid(); + if($window->getGridWidth() !== CraftingGrid::SIZE_BIG){ + throw new \UnexpectedValueException("Expected big crafting grid"); + } + $slot = $this->inventorySlot - 32; + }else{ + throw new \UnexpectedValueException("Unhandled magic UI slot offset $this->inventorySlot"); + } + }else{ + $window = $player->getWindow($this->windowId); + $slot = $this->inventorySlot; + } if($window !== null){ - return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem); + return new SlotChangeAction($window, $slot, $this->oldItem, $this->newItem); } throw new \UnexpectedValueException("Player " . $player->getName() . " has no open container with window ID $this->windowId"); @@ -199,7 +211,6 @@ class NetworkInventoryAction{ } return new CreativeInventoryAction($this->oldItem, $this->newItem, $type); - case self::SOURCE_CRAFTING_GRID: case self::SOURCE_TODO: //These types need special handling. switch($this->windowId){ From e3cffca34b946e09fe568418046e7e18d39da0c0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 5 Dec 2019 11:05:34 +0000 Subject: [PATCH 88/95] StartGamePacket: fixed possible type violation on decoding block table --- src/pocketmine/network/mcpe/protocol/StartGamePacket.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php index 64084152c..fb32c4f0c 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -211,7 +211,11 @@ class StartGamePacket extends DataPacket{ $this->enchantmentSeed = $this->getVarInt(); - $this->blockTable = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512); + $blockTable = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512); + if(!($blockTable instanceof ListTag)){ + throw new \UnexpectedValueException("Wrong block table root NBT tag type"); + } + $this->blockTable = $blockTable; $this->itemTable = []; for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){ From 124e60301aeb43760cea2fc00b540a39b0b73822 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 6 Dec 2019 11:26:58 +0000 Subject: [PATCH 89/95] updated submodule to pmmp/BedrockData@a38b42788883fa2094f67874f15594044be1ac4d --- src/pocketmine/resources/vanilla | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/resources/vanilla b/src/pocketmine/resources/vanilla index b5a8c68c4..a38b42788 160000 --- a/src/pocketmine/resources/vanilla +++ b/src/pocketmine/resources/vanilla @@ -1 +1 @@ -Subproject commit b5a8c68c4262e5d9d7f8280c1d07c252a5e8dbf8 +Subproject commit a38b42788883fa2094f67874f15594044be1ac4d From 8e984a1bc38e7cc353a7ca4c64f4f2b1a9668257 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 6 Dec 2019 11:31:18 +0000 Subject: [PATCH 90/95] RuntimeBlockMapping: use new data, item frames & doors fixed floor & ceiling item frames not supported though --- .../network/mcpe/protocol/StartGamePacket.php | 2 +- .../protocol/types/RuntimeBlockMapping.php | 96 +++++++++++-------- 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php index fb32c4f0c..8d3611c8f 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -284,7 +284,7 @@ class StartGamePacket extends DataPacket{ if($this->blockTable === null){ if(self::$blockTableCache === null){ //this is a really nasty hack, but it'll do for now - self::$blockTableCache = (new NetworkLittleEndianNBTStream())->write(RuntimeBlockMapping::generateBlockTable()); + self::$blockTableCache = (new NetworkLittleEndianNBTStream())->write(new ListTag("", RuntimeBlockMapping::getBedrockKnownStates())); } $this->put(self::$blockTableCache); }else{ diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php index 0709582c8..5fedc28b6 100644 --- a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -24,7 +24,8 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\protocol\types; use pocketmine\block\BlockIds; -use pocketmine\nbt\BigEndianNBTStream; +use pocketmine\nbt\NBT; +use pocketmine\nbt\NetworkLittleEndianNBTStream; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\StringTag; @@ -45,7 +46,7 @@ final class RuntimeBlockMapping{ private static $legacyToRuntimeMap = []; /** @var int[] */ private static $runtimeToLegacyMap = []; - /** @var mixed[]|null */ + /** @var CompoundTag[]|null */ private static $bedrockKnownStates = null; private function __construct(){ @@ -53,36 +54,57 @@ final class RuntimeBlockMapping{ } public static function init() : void{ + $tag = (new NetworkLittleEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt")); + if(!($tag instanceof ListTag) or $tag->getTagType() !== NBT::TAG_Compound){ //this is a little redundant currently, but good for auto complete and makes phpstan happy + throw new \RuntimeException("Invalid blockstates table, expected TAG_List root"); + } + + /** @var CompoundTag[] $list */ + $list = $tag->getValue(); + self::$bedrockKnownStates = self::randomizeTable($list); + + self::setupLegacyMappings(); + } + + private static function setupLegacyMappings() : void{ $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); - $tag = (new BigEndianNBTStream())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/runtime_block_states.dat")); - if(!($tag instanceof CompoundTag)){ //this is a little redundant currently, but good for auto complete and makes phpstan happy - throw new \RuntimeException("Invalid blockstates table, expected CompoundTag root"); + $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"); } - $decompressed = []; - - $states = $tag->getListTag("Palette"); - /** @var CompoundTag $state */ - foreach($states as $state){ - $block = $state->getCompoundTag("block"); - $name = $block->getString("name"); - $decompressed[] = [ - "name" => $name, - "states" => $block->getCompoundTag("states"), - "data" => $state->getShort("meta"), - "legacy_id" => $legacyIdMap[$name] - ]; - + /** + * @var int[][] $idToStatesMap string id -> int[] list of candidate state indices + */ + $idToStatesMap = []; + foreach(self::$bedrockKnownStates as $k => $state){ + $idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k; } - self::$bedrockKnownStates = self::randomizeTable($decompressed); - - foreach(self::$bedrockKnownStates as $k => $obj){ - if($obj["data"] > 15){ - //TODO: in 1.12 they started using data values bigger than 4 bits which we can't handle right now + /** @var CompoundTag $pair */ + foreach($legacyStateMap as $pair){ + $oldState = $pair->getCompoundTag("old"); + $id = $legacyIdMap[$oldState->getString("name")]; + $data = $oldState->getShort("val"); + if($data > 15){ + //we can't handle metadata with more than 4 bits continue; } - //this has to use the json offset to make sure the mapping is consistent with what we send over network, even though we aren't using all the entries - self::registerMapping($k, $obj["legacy_id"], $obj["data"]); + $mappedState = $pair->getCompoundTag("new"); + + //TODO HACK: idiotic NBT compare behaviour on 3.x compares keys which are stored by values + $mappedState->setName("block"); + $mappedName = $mappedState->getString("name"); + if(!isset($idToStatesMap[$mappedName])){ + throw new \RuntimeException("Mapped new state does not appear in network table"); + } + foreach($idToStatesMap[$mappedName] as $k){ + $networkState = self::$bedrockKnownStates[$k]; + if($mappedState->equals($networkState->getCompoundTag("block"))){ + self::registerMapping($k, $id, $data); + continue 2; + } + } + throw new \RuntimeException("Mapped new state does not appear in network table"); } } @@ -97,9 +119,9 @@ final class RuntimeBlockMapping{ * Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they * aren't supposed to do, so we have to deliberately break it to make them stop. * - * @param array $table + * @param CompoundTag[] $table * - * @return array + * @return CompoundTag[] */ private static function randomizeTable(array $table) : array{ $postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms @@ -141,19 +163,11 @@ final class RuntimeBlockMapping{ self::$runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta; } - public static function generateBlockTable() : ListTag{ + /** + * @return CompoundTag[] + */ + public static function getBedrockKnownStates() : array{ self::lazyInit(); - $states = new ListTag(); - //TODO: this assoc array mess really doesn't make sense anymore, we can store NBT directly - foreach(self::$bedrockKnownStates as $v){ - $state = new CompoundTag(); - $state->setTag(new CompoundTag("block", [ - new StringTag("name", $v["name"]), - $v["states"] - ])); - $state->setShort("id", $v["legacy_id"]); - $states->push($state); - } - return $states; + return self::$bedrockKnownStates; } } From da8c54cf8b734977223b7ed35a7ded16a7019a5c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 6 Dec 2019 11:31:55 +0000 Subject: [PATCH 91/95] AvailableEntityIdentifiersPacket: load pregenerated data from file --- .../protocol/AvailableActorIdentifiersPacket.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/AvailableActorIdentifiersPacket.php b/src/pocketmine/network/mcpe/protocol/AvailableActorIdentifiersPacket.php index c70ece1b0..e1c3baea7 100644 --- a/src/pocketmine/network/mcpe/protocol/AvailableActorIdentifiersPacket.php +++ b/src/pocketmine/network/mcpe/protocol/AvailableActorIdentifiersPacket.php @@ -27,15 +27,14 @@ namespace pocketmine\network\mcpe\protocol; use pocketmine\network\mcpe\NetworkSession; use function base64_decode; +use function file_get_contents; class AvailableActorIdentifiersPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::AVAILABLE_ACTOR_IDENTIFIERS_PACKET; - /** - * Hardcoded NBT blob extracted from MCPE vanilla server. - * TODO: this needs to be generated dynamically, but this is here for stable backwards compatibility, so we don't care for now. - */ - private const HARDCODED_NBT_BLOB = "CgAJBmlkbGlzdArOAQgDYmlkCm1pbmVjcmFmdDoBDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQNbWluZWNyYWZ0Om5wYwMDcmlkhgQBCnN1bW1vbmFibGUAAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA1taW5lY3JhZnQ6Y293AwNyaWQWAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQabWluZWNyYWZ0OndhbmRlcmluZ190cmFkZXIDA3JpZOwBAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQRbWluZWNyYWZ0OmJhbGxvb24DA3JpZNYBAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQSbWluZWNyYWZ0OmljZV9ib21iAwNyaWTUAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkDm1pbmVjcmFmdDpodXNrAwNyaWReAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQPbWluZWNyYWZ0OnN0cmF5AwNyaWRcAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQPbWluZWNyYWZ0OndpdGNoAwNyaWRaAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQZbWluZWNyYWZ0OnpvbWJpZV92aWxsYWdlcgMDcmlkWAEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkD21pbmVjcmFmdDpibGF6ZQMDcmlkVgEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkFG1pbmVjcmFmdDptYWdtYV9jdWJlAwNyaWRUAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQPbWluZWNyYWZ0OmdoYXN0AwNyaWRSAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQVbWluZWNyYWZ0OmNhdmVfc3BpZGVyAwNyaWRQAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQUbWluZWNyYWZ0OnNpbHZlcmZpc2gDA3JpZE4BCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBJtaW5lY3JhZnQ6ZW5kZXJtYW4DA3JpZEwBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA9taW5lY3JhZnQ6c2xpbWUDA3JpZEoBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBdtaW5lY3JhZnQ6em9tYmllX3BpZ21hbgMDcmlkSAEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkEG1pbmVjcmFmdDpzcGlkZXIDA3JpZEYBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBJtaW5lY3JhZnQ6c2tlbGV0b24DA3JpZEQBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBFtaW5lY3JhZnQ6Y3JlZXBlcgMDcmlkQgEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkEG1pbmVjcmFmdDp6b21iaWUDA3JpZEABCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBhtaW5lY3JhZnQ6c2tlbGV0b25faG9yc2UDA3JpZDQBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA5taW5lY3JhZnQ6bXVsZQMDcmlkMgEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkEG1pbmVjcmFmdDpkb25rZXkDA3JpZDABCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBFtaW5lY3JhZnQ6ZG9scGhpbgMDcmlkPgEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkFm1pbmVjcmFmdDp6b21iaWVfaG9yc2UDA3JpZDYBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBBtaW5lY3JhZnQ6dHVydGxlAwNyaWSUAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkE21pbmVjcmFmdDptb29zaHJvb20DA3JpZCABCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA9taW5lY3JhZnQ6cGFuZGEDA3JpZOIBAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQPbWluZWNyYWZ0OmhvcnNlAwNyaWQuAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQQbWluZWNyYWZ0OnNhbG1vbgMDcmlk2gEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA1taW5lY3JhZnQ6cGlnAwNyaWQYAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQSbWluZWNyYWZ0OnZpbGxhZ2VyAwNyaWQeAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQNbWluZWNyYWZ0OmNvZAMDcmlk4AEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBRtaW5lY3JhZnQ6cHVmZmVyZmlzaAMDcmlk2AEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA5taW5lY3JhZnQ6d29sZgMDcmlkHAEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkFm1pbmVjcmFmdDp0cm9waWNhbGZpc2gDA3JpZN4BAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQPbWluZWNyYWZ0OnNoZWVwAwNyaWQaAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQRbWluZWNyYWZ0OmRyb3duZWQDA3JpZNwBAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQRbWluZWNyYWZ0OmNoaWNrZW4DA3JpZBQBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA9taW5lY3JhZnQ6bGxhbWEDA3JpZDoBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZA9taW5lY3JhZnQ6c3F1aWQDA3JpZCIBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBJtaW5lY3JhZnQ6cGlsbGFnZXIDA3JpZOQBAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQUbWluZWNyYWZ0Omlyb25fZ29sZW0DA3JpZCgBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBBtaW5lY3JhZnQ6cmFiYml0AwNyaWQkAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQVbWluZWNyYWZ0OnZpbGxhZ2VyX3YyAwNyaWTmAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkFG1pbmVjcmFmdDpzbm93X2dvbGVtAwNyaWQqAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQNbWluZWNyYWZ0OmJhdAMDcmlkJgEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkHG1pbmVjcmFmdDp6b21iaWVfdmlsbGFnZXJfdjIDA3JpZOgBAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQQbWluZWNyYWZ0Om9jZWxvdAMDcmlkLAEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkDW1pbmVjcmFmdDpjYXQDA3JpZJYBAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQUbWluZWNyYWZ0OnBvbGFyX2JlYXIDA3JpZDgBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBBtaW5lY3JhZnQ6cGFycm90AwNyaWQ8AQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQZbWluZWNyYWZ0OndpdGhlcl9za2VsZXRvbgMDcmlkYAEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkEm1pbmVjcmFmdDpndWFyZGlhbgMDcmlkYgEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkGG1pbmVjcmFmdDplbGRlcl9ndWFyZGlhbgMDcmlkZAEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkFG1pbmVjcmFmdDp2aW5kaWNhdG9yAwNyaWRyAQpzdW1tb25hYmxlAQAIA2JpZAptaW5lY3JhZnQ6AQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkEG1pbmVjcmFmdDpwbGF5ZXIDA3JpZIIEAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQRbWluZWNyYWZ0OnBoYW50b20DA3JpZHQBCnN1bW1vbmFibGUBAAgDYmlkCm1pbmVjcmFmdDoBDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQXbWluZWNyYWZ0OnRyaXBvZF9jYW1lcmEDA3JpZIQEAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQRbWluZWNyYWZ0OnJhdmFnZXIDA3JpZHYBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBBtaW5lY3JhZnQ6d2l0aGVyAwNyaWRoAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQWbWluZWNyYWZ0OmVuZGVyX2RyYWdvbgMDcmlkagEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwEIAmlkEW1pbmVjcmFmdDpzaHVsa2VyAwNyaWRsAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQTbWluZWNyYWZ0OmVuZGVybWl0ZQMDcmlkbgEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkEm1pbmVjcmFmdDptaW5lY2FydAMDcmlkqAEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBltaW5lY3JhZnQ6aG9wcGVyX21pbmVjYXJ0AwNyaWTAAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkFm1pbmVjcmFmdDp0bnRfbWluZWNhcnQDA3JpZMIBAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQYbWluZWNyYWZ0OmNoZXN0X21pbmVjYXJ0AwNyaWTEAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkIG1pbmVjcmFmdDpjb21tYW5kX2Jsb2NrX21pbmVjYXJ0AwNyaWTIAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkFW1pbmVjcmFmdDphcm1vcl9zdGFuZAMDcmlkegEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkDm1pbmVjcmFmdDppdGVtAwNyaWSAAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkDW1pbmVjcmFmdDp0bnQDA3JpZIIBAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQXbWluZWNyYWZ0OmZhbGxpbmdfYmxvY2sDA3JpZIQBAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQTbWluZWNyYWZ0OnhwX2JvdHRsZQMDcmlkiAEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBBtaW5lY3JhZnQ6eHBfb3JiAwNyaWSKAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkHW1pbmVjcmFmdDpleWVfb2ZfZW5kZXJfc2lnbmFsAwNyaWSMAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkF21pbmVjcmFmdDplbmRlcl9jcnlzdGFsAwNyaWSOAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkGG1pbmVjcmFmdDpzaHVsa2VyX2J1bGxldAMDcmlkmAEBCnN1bW1vbmFibGUAAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBZtaW5lY3JhZnQ6ZmlzaGluZ19ob29rAwNyaWSaAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkGW1pbmVjcmFmdDpkcmFnb25fZmlyZWJhbGwDA3JpZJ4BAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQPbWluZWNyYWZ0OmFycm93AwNyaWSgAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkEm1pbmVjcmFmdDpzbm93YmFsbAMDcmlkogEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZA1taW5lY3JhZnQ6ZWdnAwNyaWSkAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkEm1pbmVjcmFmdDpwYWludGluZwMDcmlkpgEBCnN1bW1vbmFibGUAAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBhtaW5lY3JhZnQ6dGhyb3duX3RyaWRlbnQDA3JpZJIBAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQSbWluZWNyYWZ0OmZpcmViYWxsAwNyaWSqAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkF21pbmVjcmFmdDpzcGxhc2hfcG90aW9uAwNyaWSsAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkFW1pbmVjcmFmdDplbmRlcl9wZWFybAMDcmlkrgEBCnN1bW1vbmFibGUAAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBRtaW5lY3JhZnQ6bGVhc2hfa25vdAMDcmlksAEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBZtaW5lY3JhZnQ6d2l0aGVyX3NrdWxsAwNyaWSyAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkIG1pbmVjcmFmdDp3aXRoZXJfc2t1bGxfZGFuZ2Vyb3VzAwNyaWS2AQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkDm1pbmVjcmFmdDpib2F0AwNyaWS0AQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkGG1pbmVjcmFmdDpsaWdodG5pbmdfYm9sdAMDcmlkugEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZBhtaW5lY3JhZnQ6c21hbGxfZmlyZWJhbGwDA3JpZLwBAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQUbWluZWNyYWZ0OmxsYW1hX3NwaXQDA3JpZMwBAQpzdW1tb25hYmxlAAAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAAgCaWQbbWluZWNyYWZ0OmFyZWFfZWZmZWN0X2Nsb3VkAwNyaWS+AQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkGm1pbmVjcmFmdDpsaW5nZXJpbmdfcG90aW9uAwNyaWTKAQEKc3VtbW9uYWJsZQAACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkGm1pbmVjcmFmdDpmaXJld29ya3Nfcm9ja2V0AwNyaWSQAQEKc3VtbW9uYWJsZQEACANiaWQAAQxleHBlcmltZW50YWwAAQtoYXNzcGF3bmVnZwAIAmlkGG1pbmVjcmFmdDpldm9jYXRpb25fZmFuZwMDcmlkzgEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cBCAJpZBttaW5lY3JhZnQ6ZXZvY2F0aW9uX2lsbGFnZXIDA3JpZNABAQpzdW1tb25hYmxlAQAIA2JpZAABDGV4cGVyaW1lbnRhbAABC2hhc3NwYXduZWdnAQgCaWQNbWluZWNyYWZ0OnZleAMDcmlk0gEBCnN1bW1vbmFibGUBAAgDYmlkAAEMZXhwZXJpbWVudGFsAAELaGFzc3Bhd25lZ2cACAJpZA9taW5lY3JhZnQ6YWdlbnQDA3JpZHABCnN1bW1vbmFibGUAAAA="; + /** @var string|null */ + private static $DEFAULT_NBT_CACHE = null; + /** @var string */ public $namedtag; @@ -44,7 +43,11 @@ class AvailableActorIdentifiersPacket extends DataPacket{ } protected function encodePayload(){ - $this->put($this->namedtag ?? base64_decode(self::HARDCODED_NBT_BLOB)); + $this->put( + $this->namedtag ?? + self::$DEFAULT_NBT_CACHE ?? + (self::$DEFAULT_NBT_CACHE = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/entity_identifiers.nbt')) + ); } public function handle(NetworkSession $session) : bool{ From 3d2c0184429f9f2abdecc8a86d03110df3e31ccd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 6 Dec 2019 11:32:33 +0000 Subject: [PATCH 92/95] BiomeDefinitionListPacket: load pregenerated data from file --- .../mcpe/protocol/BiomeDefinitionListPacket.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/network/mcpe/protocol/BiomeDefinitionListPacket.php b/src/pocketmine/network/mcpe/protocol/BiomeDefinitionListPacket.php index 7a132e991..32599533a 100644 --- a/src/pocketmine/network/mcpe/protocol/BiomeDefinitionListPacket.php +++ b/src/pocketmine/network/mcpe/protocol/BiomeDefinitionListPacket.php @@ -26,11 +26,14 @@ namespace pocketmine\network\mcpe\protocol; #include use pocketmine\network\mcpe\NetworkSession; +use function file_get_contents; class BiomeDefinitionListPacket extends DataPacket{ public const NETWORK_ID = ProtocolInfo::BIOME_DEFINITION_LIST_PACKET; - public const HARDCODED_NBT_BLOB = "CgAKDWJhbWJvb19qdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTYmFtYm9vX2p1bmdsZV9oaWxscwUIZG93bmZhbGxmZmY/BQt0ZW1wZXJhdHVyZTMzcz8ACgViZWFjaAUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACgxiaXJjaF9mb3Jlc3QFCGRvd25mYWxsmpkZPwULdGVtcGVyYXR1cmWamRk/AAoSYmlyY2hfZm9yZXN0X2hpbGxzBQhkb3duZmFsbJqZGT8FC3RlbXBlcmF0dXJlmpkZPwAKGmJpcmNoX2ZvcmVzdF9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFGJpcmNoX2ZvcmVzdF9tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKCmNvbGRfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw9AAoKY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgpjb2xkX3RhaWdhBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKEGNvbGRfdGFpZ2FfaGlsbHMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmUAAAC/AAoSY29sZF90YWlnYV9tdXRhdGVkBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKD2RlZXBfY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8AChFkZWVwX2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChNkZWVwX2x1a2V3YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCmRlZXBfb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAoPZGVlcF93YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKBmRlc2VydAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgxkZXNlcnRfaGlsbHMFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoOZGVzZXJ0X211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoNZXh0cmVtZV9oaWxscwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4AChJleHRyZW1lX2hpbGxzX2VkZ2UFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoVZXh0cmVtZV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbJqZmT4FC3RlbXBlcmF0dXJlzcxMPgAKGGV4dHJlbWVfaGlsbHNfcGx1c190cmVlcwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACiBleHRyZW1lX2hpbGxzX3BsdXNfdHJlZXNfbXV0YXRlZAUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACg1mbG93ZXJfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKBmZvcmVzdAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgxmb3Jlc3RfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzMzM/AAoMZnJvemVuX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAAAAKDGZyb3plbl9yaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACgRoZWxsBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDWljZV9tb3VudGFpbnMFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAAAAAoKaWNlX3BsYWlucwUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChFpY2VfcGxhaW5zX3NwaWtlcwUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZQAAAAAACgZqdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoLanVuZ2xlX2VkZ2UFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzM3M/AAoTanVuZ2xlX2VkZ2VfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzcz8ACgxqdW5nbGVfaGlsbHMFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoOanVuZ2xlX211dGF0ZWQFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTbGVnYWN5X2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACg5sdWtld2FybV9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgptZWdhX3RhaWdhBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKEG1lZ2FfdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmWamZk+AAoEbWVzYQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgptZXNhX2JyeWNlBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDG1lc2FfcGxhdGVhdQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChRtZXNhX3BsYXRlYXVfbXV0YXRlZAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChJtZXNhX3BsYXRlYXVfc3RvbmUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoabWVzYV9wbGF0ZWF1X3N0b25lX211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoPbXVzaHJvb21faXNsYW5kBQhkb3duZmFsbAAAgD8FC3RlbXBlcmF0dXJlZmZmPwAKFW11c2hyb29tX2lzbGFuZF9zaG9yZQUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZWZmZj8ACgVvY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgZwbGFpbnMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmXNzEw/AAobcmVkd29vZF90YWlnYV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKFXJlZHdvb2RfdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgVyaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACg1yb29mZWRfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFXJvb2ZlZF9mb3Jlc3RfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgdzYXZhbm5hBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlmpmZPwAKD3NhdmFubmFfbXV0YXRlZAUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZc3MjD8ACg9zYXZhbm5hX3BsYXRlYXUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAIA/AAoXc2F2YW5uYV9wbGF0ZWF1X211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAIA/AAoLc3RvbmVfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoQc3VuZmxvd2VyX3BsYWlucwUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACglzd2FtcGxhbmQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoRc3dhbXBsYW5kX211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoFdGFpZ2EFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoLdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoNdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgd0aGVfZW5kBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCndhcm1fb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAA="; + /** @var string|null */ + private static $DEFAULT_NBT_CACHE = null; + /** @var string */ public $namedtag; @@ -39,7 +42,11 @@ class BiomeDefinitionListPacket extends DataPacket{ } protected function encodePayload(){ - $this->put($this->namedtag ?? self::HARDCODED_NBT_BLOB); + $this->put( + $this->namedtag ?? + self::$DEFAULT_NBT_CACHE ?? + (self::$DEFAULT_NBT_CACHE = file_get_contents(\pocketmine\RESOURCE_PATH . '/vanilla/biome_definitions.nbt')) + ); } public function handle(NetworkSession $session) : bool{ From b6bbf655d70698be692c7a0d1add67ce857f1af8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 6 Dec 2019 20:08:09 +0000 Subject: [PATCH 93/95] InventoryTransactionPacket: fixed crafting flags being set on useless empty transactions this fixes some misleading debug noise when getting stuff from creative inventory and triggers some different spam instead. --- .../network/mcpe/protocol/InventoryTransactionPacket.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php index 2c423397a..db57e7c9b 100644 --- a/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php +++ b/src/pocketmine/network/mcpe/protocol/InventoryTransactionPacket.php @@ -80,7 +80,8 @@ class InventoryTransactionPacket extends DataPacket{ if( $action->sourceType === NetworkInventoryAction::SOURCE_CONTAINER and $action->windowId === ContainerIds::UI and - $action->inventorySlot === 50 + $action->inventorySlot === 50 and + !$action->oldItem->equalsExact($action->newItem) ){ $this->isCraftingPart = true; if(!$action->oldItem->isNull() and $action->newItem->isNull()){ From d27c7f714184fe5c9c4b96319c2f62c7f7b96b21 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 6 Dec 2019 20:53:15 +0000 Subject: [PATCH 94/95] Release 3.10.0 --- changelogs/3.10.md | 16 ++++++++++++++++ src/pocketmine/VersionInfo.php | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 changelogs/3.10.md diff --git a/changelogs/3.10.md b/changelogs/3.10.md new file mode 100644 index 000000000..90e8e93f5 --- /dev/null +++ b/changelogs/3.10.md @@ -0,0 +1,16 @@ +**For Minecraft: Bedrock Edition 1.13.0** + +### Note about API versions +Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps. +Plugin developers should **only** update their required API to this version if you need the changes in this build. + +**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do. + +# 3.10.0 +- Added support for Minecraft: Bedrock Edition 1.13.0 +- Removed compatibility with 1.12.0 + +## Note about skins +PocketMine-MP **does not support skins made in the Charactor Creator** (known as Persona skins), due to technical changes which would require premature backwards compatibility breaks. The dev team has decided not to support Persona yet. +These skins will be **replaced with a random solid-colour skin. This is not a bug.** +Skins chosen from the Classic tab (classic skins) will continue to work as normal. \ No newline at end of file diff --git a/src/pocketmine/VersionInfo.php b/src/pocketmine/VersionInfo.php index 22397a4a0..4512c3d0f 100644 --- a/src/pocketmine/VersionInfo.php +++ b/src/pocketmine/VersionInfo.php @@ -30,6 +30,6 @@ const _VERSION_INFO_INCLUDED = true; const NAME = "PocketMine-MP"; -const BASE_VERSION = "3.9.8"; +const BASE_VERSION = "3.10.0"; const IS_DEVELOPMENT_BUILD = false; const BUILD_NUMBER = 0; From 82d9e481d2a0a389fbbc6dfd3672fc366127febc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 6 Dec 2019 20:53:15 +0000 Subject: [PATCH 95/95] 3.10.1 is next --- src/pocketmine/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pocketmine/VersionInfo.php b/src/pocketmine/VersionInfo.php index 4512c3d0f..314a46f06 100644 --- a/src/pocketmine/VersionInfo.php +++ b/src/pocketmine/VersionInfo.php @@ -30,6 +30,6 @@ const _VERSION_INFO_INCLUDED = true; const NAME = "PocketMine-MP"; -const BASE_VERSION = "3.10.0"; -const IS_DEVELOPMENT_BUILD = false; +const BASE_VERSION = "3.10.1"; +const IS_DEVELOPMENT_BUILD = true; const BUILD_NUMBER = 0;