diff --git a/changelogs/3.12.md b/changelogs/3.12.md new file mode 100644 index 000000000..037d691a0 --- /dev/null +++ b/changelogs/3.12.md @@ -0,0 +1,11 @@ +**For Minecraft: Bedrock Edition 1.14.60** + +### 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.12.0 +- Added support for Minecraft: Bedrock Edition 1.14.60 +- Removed compatibility with 1.14.0-1.14.30 diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index 3c020ec75..7777ff5c4 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -31,6 +31,10 @@ use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\protocol\LoginPacket; use pocketmine\network\mcpe\protocol\PlayStatusPacket; use pocketmine\network\mcpe\protocol\ProtocolInfo; +use pocketmine\network\mcpe\protocol\types\login\ClientDataPersonaPieceTintColor; +use pocketmine\network\mcpe\protocol\types\login\ClientDataPersonaSkinPiece; +use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor; +use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece; use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton; use pocketmine\network\mcpe\protocol\types\SkinAnimation; use pocketmine\network\mcpe\protocol\types\SkinData; @@ -110,7 +114,16 @@ class LoginPacketHandler extends PacketHandler{ $clientData->PremiumSkin, $clientData->PersonaSkin, $clientData->CapeOnClassicSkin, - $clientData->CapeId + $clientData->CapeId, + null, + $clientData->ArmSize, + $clientData->SkinColor, + array_map(function(ClientDataPersonaSkinPiece $piece) : PersonaSkinPiece{ + return new PersonaSkinPiece($piece->PieceId, $piece->PieceType, $piece->PackId, $piece->IsDefault, $piece->ProductId); + }, $clientData->PersonaPieces), + array_map(function(ClientDataPersonaPieceTintColor $tint) : PersonaPieceTintColor{ + return new PersonaPieceTintColor($tint->PieceType, $tint->Colors); + }, $clientData->PieceTintColors) ); $skin = SkinAdapterSingleton::get()->fromSkinData($skinData); diff --git a/src/network/mcpe/protocol/PlayerListPacket.php b/src/network/mcpe/protocol/PlayerListPacket.php index f23d40bac..8655f781c 100644 --- a/src/network/mcpe/protocol/PlayerListPacket.php +++ b/src/network/mcpe/protocol/PlayerListPacket.php @@ -83,6 +83,11 @@ class PlayerListPacket extends DataPacket implements ClientboundPacket{ $this->entries[$i] = $entry; } + if($this->type === self::TYPE_ADD){ + for($i = 0; $i < $count; ++$i){ + $this->entries[$i]->skinData->setVerified($in->getBool()); + } + } } protected function encodePayload(NetworkBinaryStream $out) : void{ @@ -103,6 +108,11 @@ class PlayerListPacket extends DataPacket implements ClientboundPacket{ $out->putUUID($entry->uuid); } } + if($this->type === self::TYPE_ADD){ + foreach($this->entries as $entry){ + $out->putBool($entry->skinData->isVerified()); + } + } } public function handle(PacketHandler $handler) : bool{ diff --git a/src/network/mcpe/protocol/PlayerSkinPacket.php b/src/network/mcpe/protocol/PlayerSkinPacket.php index 2b66aa4dd..c44c29de8 100644 --- a/src/network/mcpe/protocol/PlayerSkinPacket.php +++ b/src/network/mcpe/protocol/PlayerSkinPacket.php @@ -47,6 +47,7 @@ class PlayerSkinPacket extends DataPacket implements ClientboundPacket, Serverbo $this->skin = $in->getSkin(); $this->newSkinName = $in->getString(); $this->oldSkinName = $in->getString(); + $this->skin->setVerified($in->getBool()); } protected function encodePayload(NetworkBinaryStream $out) : void{ @@ -54,6 +55,7 @@ class PlayerSkinPacket extends DataPacket implements ClientboundPacket, Serverbo $out->putSkin($this->skin); $out->putString($this->newSkinName); $out->putString($this->oldSkinName); + $out->putBool($this->skin->isVerified()); } public function handle(PacketHandler $handler) : bool{ diff --git a/src/network/mcpe/protocol/ProtocolInfo.php b/src/network/mcpe/protocol/ProtocolInfo.php index f6541eef3..f01e3e4ae 100644 --- a/src/network/mcpe/protocol/ProtocolInfo.php +++ b/src/network/mcpe/protocol/ProtocolInfo.php @@ -41,11 +41,11 @@ final class ProtocolInfo{ */ /** Actual Minecraft: PE protocol version */ - public const CURRENT_PROTOCOL = 389; + public const CURRENT_PROTOCOL = 390; /** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */ - public const MINECRAFT_VERSION = 'v1.14.0'; + public const MINECRAFT_VERSION = 'v1.14.60'; /** Version number sent to clients in ping responses. */ - public const MINECRAFT_VERSION_NETWORK = '1.14.0'; + public const MINECRAFT_VERSION_NETWORK = '1.14.60'; public const LOGIN_PACKET = 0x01; public const PLAY_STATUS_PACKET = 0x02; diff --git a/src/network/mcpe/protocol/types/PersonaPieceTintColor.php b/src/network/mcpe/protocol/types/PersonaPieceTintColor.php new file mode 100644 index 000000000..05231d3f1 --- /dev/null +++ b/src/network/mcpe/protocol/types/PersonaPieceTintColor.php @@ -0,0 +1,55 @@ +pieceType = $pieceType; + $this->colors = $colors; + } + + public function getPieceType() : string{ + return $this->pieceType; + } + + /** + * @return string[] + */ + public function getColors() : array{ + return $this->colors; + } +} diff --git a/src/network/mcpe/protocol/types/PersonaSkinPiece.php b/src/network/mcpe/protocol/types/PersonaSkinPiece.php new file mode 100644 index 000000000..bb22a1350 --- /dev/null +++ b/src/network/mcpe/protocol/types/PersonaSkinPiece.php @@ -0,0 +1,77 @@ +pieceId = $pieceId; + $this->pieceType = $pieceType; + $this->packId = $packId; + $this->isDefaultPiece = $isDefaultPiece; + $this->productId = $productId; + } + + public function getPieceId() : string{ + return $this->pieceId; + } + + public function getPieceType() : string{ + return $this->pieceType; + } + + public function getPackId() : string{ + return $this->packId; + } + + public function isDefaultPiece() : bool{ + return $this->isDefaultPiece; + } + + public function getProductId() : string{ + return $this->productId; + } +} \ No newline at end of file diff --git a/src/network/mcpe/protocol/types/SkinData.php b/src/network/mcpe/protocol/types/SkinData.php index e7ca29312..1393748b4 100644 --- a/src/network/mcpe/protocol/types/SkinData.php +++ b/src/network/mcpe/protocol/types/SkinData.php @@ -27,6 +27,9 @@ use pocketmine\utils\UUID; class SkinData{ + public const ARM_SIZE_SLIM = "slim"; + public const ARM_SIZE_WIDE = "wide"; + /** @var string */ private $skinId; /** @var string */ @@ -51,11 +54,23 @@ class SkinData{ private $capeId; /** @var string */ private $fullSkinId; + /** @var string */ + private $armSize; + /** @var string */ + private $skinColor; + /** @var PersonaSkinPiece[] */ + private $personaPieces; + /** @var PersonaPieceTintColor[] */ + private $pieceTintColors; + /** @var bool */ + private $isVerified; /** - * @param SkinAnimation[] $animations + * @param SkinAnimation[] $animations + * @param PersonaSkinPiece[] $personaPieces + * @param PersonaPieceTintColor[] $pieceTintColors */ - 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 = "", ?string $fullSkinId = null){ + 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 = "", ?string $fullSkinId = null, string $armSize = self::ARM_SIZE_WIDE, string $skinColor = "", array $personaPieces = [], array $pieceTintColors = [], bool $isVerified = false){ $this->skinId = $skinId; $this->resourcePatch = $resourcePatch; $this->skinImage = $skinImage; @@ -69,6 +84,11 @@ class SkinData{ $this->capeId = $capeId; //this has to be unique or the client will do stupid things $this->fullSkinId = $fullSkinId ?? UUID::fromRandom()->toString(); + $this->armSize = $armSize; + $this->skinColor = $skinColor; + $this->personaPieces = $personaPieces; + $this->pieceTintColors = $pieceTintColors; + $this->isVerified = $isVerified; } public function getSkinId() : string{ @@ -121,4 +141,37 @@ class SkinData{ public function getFullSkinId() : string{ return $this->fullSkinId; } + + public function getArmSize() : string{ + return $this->armSize; + } + + public function getSkinColor() : string{ + return $this->skinColor; + } + + /** + * @return PersonaSkinPiece[] + */ + public function getPersonaPieces() : array{ + return $this->personaPieces; + } + + /** + * @return PersonaPieceTintColor[] + */ + public function getPieceTintColors() : array{ + return $this->pieceTintColors; + } + + public function isVerified() : bool{ + return $this->isVerified; + } + + /** + * @internal + */ + public function setVerified(bool $verified) : void{ + $this->isVerified = $verified; + } } diff --git a/src/network/mcpe/protocol/types/login/ClientData.php b/src/network/mcpe/protocol/types/login/ClientData.php index d4cdbd09a..b2d9bbc57 100644 --- a/src/network/mcpe/protocol/types/login/ClientData.php +++ b/src/network/mcpe/protocol/types/login/ClientData.php @@ -34,6 +34,12 @@ final class ClientData{ */ public $AnimatedImageData; + /** + * @var string + * @required + */ + public $ArmSize; + /** * @var string * @required @@ -118,12 +124,24 @@ final class ClientData{ */ public $LanguageCode; + /** + * @var ClientDataPersonaSkinPiece[] + * @required + */ + public $PersonaPieces; + /** * @var bool * @required */ public $PersonaSkin; + /** + * @var ClientDataPersonaPieceTintColor[] + * @required + */ + public $PieceTintColors; + /** * @var string * @required @@ -163,6 +181,12 @@ final class ClientData{ */ public $SkinAnimationData; + /** + * @var string + * @required + */ + public $SkinColor; + /** * @var string * @required diff --git a/src/network/mcpe/protocol/types/login/ClientDataPersonaPieceTintColor.php b/src/network/mcpe/protocol/types/login/ClientDataPersonaPieceTintColor.php new file mode 100644 index 000000000..676e480cb --- /dev/null +++ b/src/network/mcpe/protocol/types/login/ClientDataPersonaPieceTintColor.php @@ -0,0 +1,41 @@ +getBool(); $capeId = $this->getString(); $fullSkinId = $this->getString(); + $armSize = $this->getString(); + $skinColor = $this->getString(); + $personaPieceCount = $this->getLInt(); + $personaPieces = []; + for($i = 0; $i < $personaPieceCount; ++$i){ + $personaPieces[] = new PersonaSkinPiece( + $pieceId = $this->getString(), + $pieceType = $this->getString(), + $packId = $this->getString(), + $isDefaultPiece = $this->getBool(), + $productId = $this->getString() + ); + } + $pieceTintColorCount = $this->getLInt(); + $pieceTintColors = []; + for($i = 0; $i < $pieceTintColorCount; ++$i){ + $pieceType = $this->getString(); + $colorCount = $this->getLInt(); + $colors = []; + for($j = 0; $j < $colorCount; ++$j){ + $colors[] = $this->getString(); + } + $pieceTintColors[] = new PersonaPieceTintColor( + $pieceType, + $colors + ); + } - return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId, $fullSkinId); + return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId, $fullSkinId, $armSize, $skinColor, $personaPieces, $pieceTintColors); } public function putSkin(SkinData $skin): void{ @@ -139,6 +168,24 @@ class NetworkBinaryStream extends BinaryStream{ $this->putBool($skin->isPersonaCapeOnClassic()); $this->putString($skin->getCapeId()); $this->putString($skin->getFullSkinId()); + $this->putString($skin->getArmSize()); + $this->putString($skin->getSkinColor()); + $this->putLInt(count($skin->getPersonaPieces())); + foreach($skin->getPersonaPieces() as $piece){ + $this->putString($piece->getPieceId()); + $this->putString($piece->getPieceType()); + $this->putString($piece->getPackId()); + $this->putBool($piece->isDefaultPiece()); + $this->putString($piece->getProductId()); + } + $this->putLInt(count($skin->getPieceTintColors())); + foreach($skin->getPieceTintColors() as $tint){ + $this->putString($tint->getPieceType()); + $this->putLInt(count($tint->getColors())); + foreach($tint->getColors() as $color){ + $this->putString($color); + } + } } private function getSkinImage() : SkinImage{