From 559504225a46f5b9555dbcb02c12987605929a74 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 24 Apr 2017 09:50:55 +0100 Subject: [PATCH 1/3] Throw an exception before calling base entity constructor if skin is not set or invalid, close #835 (#855) --- src/pocketmine/entity/Human.php | 17 +++++++++++++++-- src/pocketmine/level/format/Chunk.php | 13 ++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 5d8db656c..3ff95d79c 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -27,6 +27,7 @@ use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerInventory; use pocketmine\item\Item as ItemItem; +use pocketmine\level\Level; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; @@ -61,13 +62,21 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ public $eyeHeight = 1.62; protected $skinId; - protected $skin; + protected $skin = null; protected $foodTickTimer = 0; protected $totalXp = 0; protected $xpSeed; + public function __construct(Level $level, CompoundTag $nbt){ + 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; } @@ -95,6 +104,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ * @param string $skinId */ public function setSkin($str, $skinId){ + if(!Player::isValidSkin($str)){ + throw new \InvalidStateException("Specified skin is not valid, must be 8KiB or 16KiB"); + } + $this->skin = $str; $this->skinId = $skinId; } @@ -483,7 +496,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ if($player !== $this and !isset($this->hasSpawned[$player->getLoaderId()])){ $this->hasSpawned[$player->getLoaderId()] = $player; - if(strlen($this->skin) < 64 * 32 * 4){ + if(!Player::isValidSkin($this->skin)){ throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set"); } diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 00db9a89c..636ad41a2 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -697,9 +697,16 @@ class Chunk{ continue; //Fixes entities allocated in wrong chunks. } - if(($entity = Entity::createEntity($nbt["id"], $level, $nbt)) instanceof Entity){ - $entity->spawnToAll(); - }else{ + try{ + $entity = Entity::createEntity($nbt["id"], $level, $nbt); + if($entity instanceof Entity){ + $entity->spawnToAll(); + }else{ + $changed = true; + continue; + } + }catch(\Throwable $t){ + $level->getServer()->getLogger()->logException($t); $changed = true; continue; } From 0e7f364a41954c7517a704a837b7eb3893516889 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 24 Apr 2017 11:55:33 +0100 Subject: [PATCH 2/3] Fixed chunk object memory leak when chunks are changed, close #419 If a player quit the server in the 1-second between a chunk changing and a fresh chunk-order requesting a resend of that chunk, the player wouldn't know they were using that chunk and did not unregister themselves, causing the subject chunks to always remain loaded. --- src/pocketmine/Player.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 781b82eeb..b5e461731 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -4088,7 +4088,9 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade } public function onChunkChanged(Chunk $chunk){ - unset($this->usedChunks[Level::chunkHash($chunk->getX(), $chunk->getZ())]); + if(isset($this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())])){ + $this->usedChunks[$hash] = false; + } } public function onChunkLoaded(Chunk $chunk){ From d682fdfdf0b4fff5a79018a8f63f496be37c4ed8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 24 Apr 2017 13:31:05 +0100 Subject: [PATCH 3/3] Food and exhaustion should not apply in creative, close #860 --- src/pocketmine/Player.php | 14 ++++++++++++++ src/pocketmine/entity/Human.php | 10 +++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index b5e461731..27f71bef3 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1706,6 +1706,20 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade return true; } + public function doFoodTick(int $tickDiff = 1){ + if($this->isSurvival()){ + parent::doFoodTick($tickDiff); + } + } + + public function exhaust(float $amount, int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{ + if($this->isSurvival()){ + return parent::exhaust($amount, $cause); + } + + return 0.0; + } + public function checkNetwork(){ if(!$this->isOnline()){ return; diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 3ff95d79c..9797ead87 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -374,12 +374,18 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ public function entityBaseTick($tickDiff = 1){ $hasUpdate = parent::entityBaseTick($tickDiff); + $this->doFoodTick($tickDiff); + + return $hasUpdate; + } + + public function doFoodTick(int $tickDiff = 1){ if($this->isAlive()){ $food = $this->getFood(); $health = $this->getHealth(); $difficulty = $this->server->getDifficulty(); - $this->foodTickTimer++; + $this->foodTickTimer += $tickDiff; if($this->foodTickTimer >= 80){ $this->foodTickTimer = 0; } @@ -412,8 +418,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } } } - - return $hasUpdate; } public function getName(){