Friendly BC skins (persona not supported)

This commit is contained in:
Stephen 2019-11-10 21:04:38 -05:00
parent 635bb08fb9
commit e2fc7cdf88
5 changed files with 42 additions and 135 deletions

View File

@ -1917,22 +1917,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->uuid = UUID::fromString($packet->clientUUID); $this->uuid = UUID::fromString($packet->clientUUID);
$this->rawUUID = $this->uuid->toBinary(); $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( $skin = new Skin(
$packet->clientData["SkinId"], $packet->clientData["SkinId"],
base64_decode($packet->clientData["SkinData"] ?? ""),
base64_decode($packet->clientData["CapeData"] ?? ""),
base64_decode($packet->clientData["SkinResourcePatch"] ?? ""), base64_decode($packet->clientData["SkinResourcePatch"] ?? ""),
new SerializedImage($packet->clientData["SkinImageHeight"], $packet->clientData["SkinImageWidth"], base64_decode($packet->clientData["SkinData"] ?? "")), base64_decode($packet->clientData["SkinGeometryData"] ?? "")
$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"] ?? ""
); );
if(!$skin->isValid()){ if(!$skin->isValid()){

View File

@ -120,16 +120,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
protected static function deserializeSkinNBT(CompoundTag $skinTag) : Skin{ protected static function deserializeSkinNBT(CompoundTag $skinTag) : Skin{
$skin = new Skin( $skin = new Skin(
$skinTag->getString("Name"), $skinTag->getString("Name"),
$skinTag->getString("SkinResourcePatch", ""), $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)
new SerializedImage($skinTag->getInt("SkinImageHeight"), $skinTag->getInt("SkinImageWidth"), $skinTag->getByteArray("Data")), $skinTag->getByteArray("CapeData", ""),
"", //TODO: animations $skinTag->getString("GeometryName", ""),
new SerializedImage($skinTag->getInt("CapeImageHeight"), $skinTag->getInt("CapeImageWidth"), $skinTag->getByteArray("CapeData")), $skinTag->getByteArray("GeometryData", "")
$skinTag->getByteArray("GeometryData", ""),
$skinTag->getByteArray("AnimationData", ""),
$skinTag->getByte("PremiumSkin") === 1,
$skinTag->getByte("PersonaSkin") === 1,
$skinTag->getByte("CapeOnClassic") === 1,
$skinTag->getString("CapeId", "")
); );
$skin->validate(); $skin->validate();
return $skin; return $skin;
@ -839,19 +833,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
if($this->skin !== null){ if($this->skin !== null){
$this->namedtag->setTag(new CompoundTag("Skin", [ $this->namedtag->setTag(new CompoundTag("Skin", [
new StringTag("Name", $this->skin->getSkinId()), new StringTag("Name", $this->skin->getSkinId()),
new StringTag("SkinResourcePatch", $this->skin->getSkinResourcePatch()), new ByteArrayTag("Data", $this->skin->getSkinData()),
new ByteArrayTag("Data", $this->skin->getSkinData()->getData()), new ByteArrayTag("CapeData", $this->skin->getCapeData()),
new IntTag("SkinImageHeight", $this->skin->getSkinData()->getHeight()), new StringTag("GeometryName", $this->skin->getGeometryName()),
new IntTag("SkinImageWidth", $this->skin->getSkinData()->getWidth()), new ByteArrayTag("GeometryData", $this->skin->getGeometryData())
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())
])); ]));
} }
} }

View File

@ -41,38 +41,20 @@ class Skin{
/** @var string */ /** @var string */
private $skinId; private $skinId;
/** @var string */ /** @var string */
private $skinResourcePatch;
/** @var SerializedImage */
private $skinData; private $skinData;
/** @var SkinAnimation[] */ /** @var string */
private $animations = [];
/** @var SerializedImage */
private $capeData; private $capeData;
/** @var string */ /** @var string */
private $geometryName;
/** @var string */
private $geometryData; 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->skinId = $skinId;
$this->skinResourcePatch = $skinResourcePatch;
$this->skinData = $skinData; $this->skinData = $skinData;
$this->animations = $animations;
$this->capeData = $capeData; $this->capeData = $capeData;
$this->geometryName = $geometryName;
$this->geometryData = $geometryData; $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{ public static function convertToLegacyName(string $name) : string{
@ -99,14 +81,13 @@ class Skin{
if($this->skinId === ""){ if($this->skinId === ""){
throw new \InvalidArgumentException("Skin ID must not be empty"); throw new \InvalidArgumentException("Skin ID must not be empty");
} }
//Broken with Persona skins $len = strlen($this->skinData);
/*$len = strlen($this->skinData->getData());
if(!in_array($len, self::ACCEPTED_SKIN_SIZES, true)){ 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) . ")"); 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){ if($this->capeData !== "" and strlen($this->capeData) !== 8192){
throw new \InvalidArgumentException("Invalid cape data size " . strlen($this->capeData->getData()) . " bytes (must be exactly 8192 bytes)"); throw new \InvalidArgumentException("Invalid cape data size " . strlen($this->capeData) . " bytes (must be exactly 8192 bytes)");
}*/ }
//TODO: validate geometry //TODO: validate geometry
} }
@ -120,29 +101,22 @@ class Skin{
/** /**
* @return string * @return string
*/ */
public function getSkinResourcePatch() : string{ public function getSkinData() : string{
return $this->skinResourcePatch;
}
/**
* @return SerializedImage
*/
public function getSkinData() : SerializedImage{
return $this->skinData; return $this->skinData;
} }
/** /**
* @return SkinAnimation[] * @return string
*/ */
public function getAnimations() : array{ public function getCapeData() : string{
return $this->animations; return $this->capeData;
} }
/** /**
* @return SerializedImage * @return string
*/ */
public function getCapeData() : SerializedImage{ public function getGeometryName() : string{
return $this->capeData ?? new SerializedImage(0, 0, ""); return $this->geometryName;
} }
/** /**
@ -152,48 +126,6 @@ class Skin{
return $this->geometryData; 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. * Hack to cut down on network overhead due to skins, by un-pretty-printing geometry JSON.
* *

View File

@ -97,7 +97,7 @@ class FloatingTextParticle extends Particle{
$add = new PlayerListPacket(); $add = new PlayerListPacket();
$add->type = PlayerListPacket::TYPE_ADD; $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; $p[] = $add;
$pk = new AddPlayerPacket(); $pk = new AddPlayerPacket();

View File

@ -95,29 +95,29 @@ class NetworkBinaryStream extends BinaryStream{
$capeId = $this->getString(); $capeId = $this->getString();
$fullSkinId = $this->getString(); $fullSkinId = $this->getString();
return new Skin( return new Skin($skinId, $skinData->getData(), $capeData->getData(), $skinResourcePatch, $geometryData);
$skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId
);
} }
public function putSkin(Skin $skin){ public function putSkin(Skin $skin){
$this->putString($skin->getSkinId()); $this->putString($skin->getSkinId());
$this->putString($skin->getSkinResourcePatch()); $this->putString($skin->getGeometryName()); //resource patch
$this->putImage($skin->getSkinData()); $this->putImage(SerializedImage::fromLegacy($skin->getSkinData()));
$this->putLInt(count($skin->getAnimations())); /** @var SkinAnimation[] $animations */
foreach($skin->getAnimations() as $animation){ $animations = [];
$this->putLInt(count($animations));
foreach($animations as $animation){
$this->putImage($animation->getImage()); $this->putImage($animation->getImage());
$this->putLInt($animation->getType()); $this->putLInt($animation->getType());
$this->putLFloat($animation->getFrames()); $this->putLFloat($animation->getFrames());
} }
$this->putImage($skin->getCapeData()); $this->putImage(new SerializedImage(0, 0, $skin->getCapeData()));
$this->putString($skin->getGeometryData()); $this->putString($skin->getGeometryData());
$this->putString($skin->getAnimationData()); $this->putString(""); //animation data
$this->putBool($skin->getPremium()); $this->putBool(false); //isPremium
$this->putBool($skin->getPersona()); $this->putBool(false); //isPersona
$this->putBool($skin->getCapeOnClassic()); $this->putBool(false); //isCapeOnClassic
$this->putString($skin->getCapeId()); $this->putString(""); //capeId
$this->putString($skin->getFullSkinId()); $this->putString(""); //fullskinId
} }
public function getImage() : SerializedImage{ public function getImage() : SerializedImage{