Merge branch 'stable'

# Conflicts:
#	resources/vanilla
#	src/pocketmine/entity/Human.php
This commit is contained in:
Dylan K. Taylor 2019-05-17 17:43:30 +01:00
commit 64f7f558a4
3 changed files with 61 additions and 33 deletions

View File

@ -43,7 +43,6 @@ use pocketmine\item\FoodSource;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\Totem; use pocketmine\item\Totem;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
@ -67,6 +66,7 @@ use function array_merge;
use function array_rand; use function array_rand;
use function array_values; use function array_values;
use function ceil; use function ceil;
use function in_array;
use function max; use function max;
use function min; use function min;
use function random_int; use function random_int;
@ -104,18 +104,36 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
public function __construct(World $world, CompoundTag $nbt){ public function __construct(World $world, CompoundTag $nbt){
if($this->skin === null){ if($this->skin === null){
$skinTag = $nbt->getCompoundTag("Skin"); $skinTag = $nbt->getCompoundTag("Skin");
if($skinTag === null or !self::isValidSkin($skinTag->hasTag("Data", ByteArrayTag::class) ? if($skinTag === null){
$skinTag->getByteArray("Data") :
$skinTag->getString("Data", "")
)){
throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set"); throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set");
} }
$this->skin = self::deserializeSkinNBT($skinTag); //this throws if the skin is invalid
} }
parent::__construct($world, $nbt); parent::__construct($world, $nbt);
} }
/** /**
* @param CompoundTag $skinTag
*
* @return Skin
* @throws \InvalidArgumentException
*/
protected static function deserializeSkinNBT(CompoundTag $skinTag) : Skin{
$skin = new Skin(
$skinTag->getString("Name"),
$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)
$skinTag->getByteArray("CapeData", ""),
$skinTag->getString("GeometryName", ""),
$skinTag->getByteArray("GeometryData", "")
);
$skin->validate();
return $skin;
}
/**
* @deprecated
*
* Checks the length of a supplied skin bitmap and returns whether the length is valid. * Checks the length of a supplied skin bitmap and returns whether the length is valid.
* *
* @param string $skin * @param string $skin
@ -123,7 +141,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* @return bool * @return bool
*/ */
public static function isValidSkin(string $skin) : bool{ public static function isValidSkin(string $skin) : bool{
return strlen($skin) === 64 * 64 * 4 or strlen($skin) === 64 * 32 * 4 or strlen($skin) === 128 * 128 * 4; return in_array(strlen($skin), Skin::ACCEPTED_SKIN_SIZES, true);
} }
/** /**
@ -155,10 +173,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
* @param Skin $skin * @param Skin $skin
*/ */
public function setSkin(Skin $skin) : void{ public function setSkin(Skin $skin) : void{
if(!$skin->isValid()){ $skin->validate();
throw new \InvalidStateException("Specified skin is not valid, must be 8KiB or 16KiB");
}
$this->skin = $skin; $this->skin = $skin;
$this->skin->debloatGeometryData(); $this->skin->debloatGeometryData();
} }
@ -590,17 +605,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
$this->setNameTag($nbt->getString("NameTag")); $this->setNameTag($nbt->getString("NameTag"));
} }
$skin = $nbt->getCompoundTag("Skin");
if($skin !== null){
$this->setSkin(new Skin(
$skin->getString("Name"),
$skin->hasTag("Data", StringTag::class) ? $skin->getString("Data") : $skin->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM)
$skin->getByteArray("CapeData", ""),
$skin->getString("GeometryName", ""),
$skin->getByteArray("GeometryData", "")
));
}
$this->uuid = UUID::fromData((string) $this->getId(), $this->skin->getSkinData(), $this->getNameTag()); $this->uuid = UUID::fromData((string) $this->getId(), $this->skin->getSkinData(), $this->getNameTag());
} }
@ -849,9 +853,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
} }
protected function sendSpawnPacket(Player $player) : void{ protected function sendSpawnPacket(Player $player) : void{
if(!$this->skin->isValid()){ $this->skin->validate();
throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set");
}
if(!($this instanceof Player)){ if(!($this instanceof Player)){
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */ /* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */

View File

@ -23,11 +23,18 @@ declare(strict_types=1);
namespace pocketmine\entity; namespace pocketmine\entity;
use function implode;
use function in_array;
use function json_decode; use function json_decode;
use function json_encode; use function json_encode;
use function strlen; use function strlen;
class Skin{ class Skin{
public const ACCEPTED_SKIN_SIZES = [
64 * 32 * 4,
64 * 64 * 4,
128 * 128 * 4
];
/** @var string */ /** @var string */
private $skinId; private $skinId;
@ -48,12 +55,34 @@ class Skin{
$this->geometryData = $geometryData; $this->geometryData = $geometryData;
} }
/**
* @deprecated
* @return bool
*/
public function isValid() : bool{ public function isValid() : bool{
return ( try{
$this->skinId !== "" and $this->validate();
(($s = strlen($this->skinData)) === 16384 or $s === 8192 or $s === 65536) and return true;
($this->capeData === "" or strlen($this->capeData) === 8192) }catch(\InvalidArgumentException $e){
); return false;
}
}
/**
* @throws \InvalidArgumentException
*/
public function validate() : void{
if($this->skinId === ""){
throw new \InvalidArgumentException("Skin ID must not be empty");
}
$len = strlen($this->skinData);
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) . ")");
}
if($this->capeData !== "" and strlen($this->capeData) !== 8192){
throw new \InvalidArgumentException("Invalid cape data size " . strlen($this->capeData) . " bytes (must be exactly 8192 bytes)");
}
//TODO: validate geometry
} }
/** /**

View File

@ -70,10 +70,7 @@ class PlayerChangeSkinEvent extends PlayerEvent implements Cancellable{
* @throws \InvalidArgumentException if the specified skin is not valid * @throws \InvalidArgumentException if the specified skin is not valid
*/ */
public function setNewSkin(Skin $skin) : void{ public function setNewSkin(Skin $skin) : void{
if(!$skin->isValid()){ $skin->validate();
throw new \InvalidArgumentException("Skin format is invalid");
}
$this->newSkin = $skin; $this->newSkin = $skin;
} }
} }