New skin API, add support for custom capes & custom geometry (#1416)

* Added support for changing skins ingame, custom capes & geometry
* Use PlayerSkinPacket for setting Human skin instead of PlayerList hack
This commit is contained in:
Dylan K. Taylor
2017-09-29 14:09:00 +01:00
committed by GitHub
parent c273a46537
commit e6cecabf3f
13 changed files with 302 additions and 120 deletions

View File

@ -37,6 +37,7 @@ use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
use pocketmine\Player;
use pocketmine\utils\UUID;
@ -60,8 +61,8 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
public $height = 1.8;
public $eyeHeight = 1.62;
protected $skinId;
protected $skin = "";
/** @var Skin */
protected $skin;
protected $foodTickTimer = 0;
@ -71,19 +72,22 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
protected $baseOffset = 1.62;
public function __construct(Level $level, CompoundTag $nbt){
if($this->skin === "" and (!isset($nbt->Skin) or !isset($nbt->Skin->Data) or !Player::isValidSkin($nbt->Skin->Data->getValue()))){
if($this->skin === null and (!isset($nbt->Skin) or !isset($nbt->Skin->Data) or !Player::isValidSkin($nbt->Skin->Data->getValue()))){
throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set");
}
parent::__construct($level, $nbt);
}
public function getSkinData(){
return $this->skin;
}
public function getSkinId(){
return $this->skinId;
/**
* Checks the length of a supplied skin bitmap and returns whether the length is valid.
*
* @param string $skin
*
* @return bool
*/
public static function isValidSkin(string $skin) : bool{
return strlen($skin) === 64 * 64 * 4 or strlen($skin) === 64 * 32 * 4;
}
/**
@ -101,16 +105,35 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
/**
* @param string $str
* @param string $skinId
* Returns a Skin object containing information about this human's skin.
* @return Skin
*/
public function setSkin(string $str, string $skinId){
if(!Player::isValidSkin($str)){
public function getSkin() : Skin{
return $this->skin;
}
/**
* Sets the human's skin. This will not send any update to viewers, you need to do that manually using
* {@link sendSkin}.
*
* @param Skin $skin
*/
public function setSkin(Skin $skin) : void{
if(!$skin->isValid()){
throw new \InvalidStateException("Specified skin is not valid, must be 8KiB or 16KiB");
}
$this->skin = $str;
$this->skinId = $skinId;
$this->skin = $skin;
}
/**
* @param Player[] $targets
*/
public function sendSkin(array $targets) : void{
$pk = new PlayerSkinPacket();
$pk->uuid = $this->getUniqueId();
$pk->skin = $this->skin;
$this->server->broadcastPacket($targets, $pk);
}
public function jump(){
@ -287,10 +310,13 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
}
if(isset($this->namedtag->Skin) and $this->namedtag->Skin instanceof CompoundTag){
$this->setSkin($this->namedtag->Skin["Data"], $this->namedtag->Skin["Name"]);
$this->setSkin(new Skin(
$this->namedtag->Skin["Name"],
$this->namedtag->Skin["Data"]
));
}
$this->uuid = UUID::fromData((string) $this->getId(), $this->getSkinData(), $this->getNameTag());
$this->uuid = UUID::fromData((string) $this->getId(), $this->skin->getSkinData(), $this->getNameTag());
}
protected function initEntity(){
@ -467,10 +493,11 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->namedtag->SelectedInventorySlot = new IntTag("SelectedInventorySlot", $this->inventory->getHeldItemIndex());
}
if(strlen($this->getSkinData()) > 0){
if($this->skin !== null){
$this->namedtag->Skin = new CompoundTag("Skin", [
new StringTag("Data", $this->getSkinData()),
new StringTag("Name", $this->getSkinId())
//TODO: save cape & geometry
new StringTag("Data", $this->skin->getSkinData()),
new StringTag("Name", $this->skin->getSkinId())
]);
}
}
@ -479,14 +506,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
if($player !== $this and !isset($this->hasSpawned[$player->getLoaderId()])){
$this->hasSpawned[$player->getLoaderId()] = $player;
if(!Player::isValidSkin($this->skin)){
if(!$this->skin->isValid()){
throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set");
}
if(!($this instanceof Player)){
$this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getName(), $this->skinId, $this->skin, [$player]);
}
$pk = new AddPlayerPacket();
$pk->uuid = $this->getUniqueId();
$pk->username = $this->getName();
@ -502,7 +525,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->inventory->sendArmorContents($player);
if(!($this instanceof Player)){
$this->server->removePlayerListData($this->getUniqueId(), [$player]);
$this->sendSkin([$player]);
}
}
}