mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 09:56:06 +00:00
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:
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user