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