mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-06 11:57:10 +00:00
Improved error handling for loading broken entity / tile data
This commit is contained in:
parent
eb0cf52d81
commit
5c7125f190
@ -23,8 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\tile;
|
||||
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
@ -112,21 +113,25 @@ final class TileFactory{
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @throws NbtDataException
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
public function createFromData(World $world, CompoundTag $nbt) : ?Tile{
|
||||
$type = $nbt->getString(Tile::TAG_ID, "");
|
||||
if(!isset($this->knownTiles[$type])){
|
||||
return null;
|
||||
try{
|
||||
$type = $nbt->getString(Tile::TAG_ID, "");
|
||||
if(!isset($this->knownTiles[$type])){
|
||||
return null;
|
||||
}
|
||||
$class = $this->knownTiles[$type];
|
||||
assert(is_a($class, Tile::class, true));
|
||||
/**
|
||||
* @var Tile $tile
|
||||
* @see Tile::__construct()
|
||||
*/
|
||||
$tile = new $class($world, new Vector3($nbt->getInt(Tile::TAG_X), $nbt->getInt(Tile::TAG_Y), $nbt->getInt(Tile::TAG_Z)));
|
||||
$tile->readSaveData($nbt);
|
||||
}catch(NbtException $e){
|
||||
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
|
||||
}
|
||||
$class = $this->knownTiles[$type];
|
||||
assert(is_a($class, Tile::class, true));
|
||||
/**
|
||||
* @var Tile $tile
|
||||
* @see Tile::__construct()
|
||||
*/
|
||||
$tile = new $class($world, new Vector3($nbt->getInt(Tile::TAG_X), $nbt->getInt(Tile::TAG_Y), $nbt->getInt(Tile::TAG_Z)));
|
||||
$tile->readSaveData($nbt);
|
||||
|
||||
return $tile;
|
||||
}
|
||||
|
28
src/data/SavedDataLoadingException.php
Normal file
28
src/data/SavedDataLoadingException.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\data;
|
||||
|
||||
final class SavedDataLoadingException extends \RuntimeException{
|
||||
|
||||
}
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NBT;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
@ -38,34 +39,40 @@ final class EntityDataHelper{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
public static function parseLocation(CompoundTag $nbt, World $world) : Location{
|
||||
$pos = self::parseVec3($nbt, "Pos", false);
|
||||
|
||||
$yawPitch = $nbt->getTag("Rotation");
|
||||
if(!($yawPitch instanceof ListTag) or $yawPitch->getTagType() !== NBT::TAG_Float){
|
||||
throw new \UnexpectedValueException("'Rotation' should be a List<Float>");
|
||||
throw new SavedDataLoadingException("'Rotation' should be a List<Float>");
|
||||
}
|
||||
/** @var FloatTag[] $values */
|
||||
$values = $yawPitch->getValue();
|
||||
if(count($values) !== 2){
|
||||
throw new \UnexpectedValueException("Expected exactly 2 entries for 'Rotation'");
|
||||
throw new SavedDataLoadingException("Expected exactly 2 entries for 'Rotation'");
|
||||
}
|
||||
|
||||
return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
public static function parseVec3(CompoundTag $nbt, string $tagName, bool $optional) : Vector3{
|
||||
$pos = $nbt->getTag($tagName);
|
||||
if($pos === null and $optional){
|
||||
return new Vector3(0, 0, 0);
|
||||
}
|
||||
if(!($pos instanceof ListTag) or ($pos->getTagType() !== NBT::TAG_Double && $pos->getTagType() !== NBT::TAG_Float)){
|
||||
throw new \UnexpectedValueException("'$tagName' should be a List<Double> or List<Float>");
|
||||
throw new SavedDataLoadingException("'$tagName' should be a List<Double> or List<Float>");
|
||||
}
|
||||
/** @var DoubleTag[]|FloatTag[] $values */
|
||||
$values = $pos->getValue();
|
||||
if(count($values) !== 3){
|
||||
throw new \UnexpectedValueException("Expected exactly 3 entries in '$tagName' tag");
|
||||
throw new SavedDataLoadingException("Expected exactly 3 entries in '$tagName' tag");
|
||||
}
|
||||
return new Vector3($values[0]->getValue(), $values[1]->getValue(), $values[2]->getValue());
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\block\BlockFactory;
|
||||
use pocketmine\data\bedrock\EntityLegacyIds;
|
||||
use pocketmine\data\bedrock\PotionTypeIdMap;
|
||||
use pocketmine\data\bedrock\PotionTypeIds;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\object\ExperienceOrb;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
use pocketmine\entity\object\ItemEntity;
|
||||
@ -45,7 +46,7 @@ use pocketmine\entity\projectile\SplashPotion;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\NbtException;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
@ -113,12 +114,12 @@ final class EntityFactory{
|
||||
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
|
||||
$itemTag = $nbt->getCompoundTag("Item");
|
||||
if($itemTag === null){
|
||||
throw new \UnexpectedValueException("Expected \"Item\" NBT tag not found");
|
||||
throw new SavedDataLoadingException("Expected \"Item\" NBT tag not found");
|
||||
}
|
||||
|
||||
$item = Item::nbtDeserialize($itemTag);
|
||||
if($item->isNull()){
|
||||
throw new \UnexpectedValueException("Item is invalid");
|
||||
throw new SavedDataLoadingException("Item is invalid");
|
||||
}
|
||||
return new ItemEntity(EntityDataHelper::parseLocation($nbt, $world), $item, $nbt);
|
||||
}, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM);
|
||||
@ -126,7 +127,7 @@ final class EntityFactory{
|
||||
$this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{
|
||||
$motive = PaintingMotive::getMotiveByName($nbt->getString("Motive"));
|
||||
if($motive === null){
|
||||
throw new \UnexpectedValueException("Unknown painting motive");
|
||||
throw new SavedDataLoadingException("Unknown painting motive");
|
||||
}
|
||||
$blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ"));
|
||||
if(($directionTag = $nbt->getTag("Direction")) instanceof ByteTag){
|
||||
@ -134,7 +135,7 @@ final class EntityFactory{
|
||||
}elseif(($facingTag = $nbt->getTag("Facing")) instanceof ByteTag){
|
||||
$facing = Painting::DATA_TO_FACING[$facingTag->getValue()] ?? Facing::NORTH;
|
||||
}else{
|
||||
throw new \UnexpectedValueException("Missing facing info");
|
||||
throw new SavedDataLoadingException("Missing facing info");
|
||||
}
|
||||
|
||||
return new Painting(EntityDataHelper::parseLocation($nbt, $world), $blockIn, $facing, $motive, $nbt);
|
||||
@ -151,7 +152,7 @@ final class EntityFactory{
|
||||
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
|
||||
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER));
|
||||
if($potionType === null){
|
||||
throw new \UnexpectedValueException("No such potion type");
|
||||
throw new SavedDataLoadingException("No such potion type");
|
||||
}
|
||||
return new SplashPotion(EntityDataHelper::parseLocation($nbt, $world), null, $potionType, $nbt);
|
||||
}, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION);
|
||||
@ -222,25 +223,28 @@ final class EntityFactory{
|
||||
/**
|
||||
* Creates an entity from data stored on a chunk.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws NbtDataException
|
||||
* @throws SavedDataLoadingException
|
||||
* @internal
|
||||
*/
|
||||
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
|
||||
$saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier");
|
||||
$func = null;
|
||||
if($saveId instanceof StringTag){
|
||||
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
|
||||
}elseif($saveId instanceof IntTag){ //legacy MCPE format
|
||||
$func = $this->creationFuncs[$saveId->getValue() & 0xff] ?? null;
|
||||
}
|
||||
if($func === null){
|
||||
return null;
|
||||
}
|
||||
/** @var Entity $entity */
|
||||
$entity = $func($world, $nbt);
|
||||
try{
|
||||
$saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier");
|
||||
$func = null;
|
||||
if($saveId instanceof StringTag){
|
||||
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
|
||||
}elseif($saveId instanceof IntTag){ //legacy MCPE format
|
||||
$func = $this->creationFuncs[$saveId->getValue() & 0xff] ?? null;
|
||||
}
|
||||
if($func === null){
|
||||
return null;
|
||||
}
|
||||
/** @var Entity $entity */
|
||||
$entity = $func($world, $nbt);
|
||||
|
||||
return $entity;
|
||||
return $entity;
|
||||
}catch(NbtException $e){
|
||||
throw new SavedDataLoadingException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function injectSaveId(string $class, CompoundTag $saveData) : void{
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\animation\TotemUseAnimation;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\entity\effect\VanillaEffects;
|
||||
@ -104,12 +105,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
|
||||
/**
|
||||
* @throws InvalidSkinException
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
public static function parseSkinNBT(CompoundTag $nbt) : Skin{
|
||||
$skinTag = $nbt->getCompoundTag("Skin");
|
||||
if($skinTag === null){
|
||||
throw new \UnexpectedValueException("Missing skin data");
|
||||
throw new SavedDataLoadingException("Missing skin data");
|
||||
}
|
||||
return new Skin( //this throws if the skin is invalid
|
||||
$skinTag->getString("Name"),
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\block\BlockBreakInfo;
|
||||
use pocketmine\block\BlockToolType;
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\data\bedrock\EnchantmentIdMap;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\enchantment\EnchantmentInstance;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -671,6 +672,8 @@ class Item implements \JsonSerializable{
|
||||
|
||||
/**
|
||||
* Deserializes an Item from an NBT CompoundTag
|
||||
* @throws NbtException
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
public static function nbtDeserialize(CompoundTag $tag) : Item{
|
||||
if($tag->getTag("id") === null or $tag->getTag("Count") === null){
|
||||
@ -692,7 +695,7 @@ class Item implements \JsonSerializable{
|
||||
}
|
||||
$item->setCount($count);
|
||||
}else{
|
||||
throw new \InvalidArgumentException("Item CompoundTag ID must be an instance of StringTag or ShortTag, " . get_class($idTag) . " given");
|
||||
throw new SavedDataLoadingException("Item CompoundTag ID must be an instance of StringTag or ShortTag, " . get_class($idTag) . " given");
|
||||
}
|
||||
|
||||
$itemNBT = $tag->getCompoundTag("tag");
|
||||
|
@ -36,6 +36,7 @@ use pocketmine\block\tile\TileFactory;
|
||||
use pocketmine\block\UnknownBlock;
|
||||
use pocketmine\block\VanillaBlocks;
|
||||
use pocketmine\data\bedrock\BiomeIds;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\Location;
|
||||
@ -57,7 +58,6 @@ use pocketmine\item\LegacyStringToItemParser;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NbtDataException;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
|
||||
@ -2472,7 +2472,7 @@ class World implements ChunkManager{
|
||||
foreach($chunkData->getEntityNBT() as $k => $nbt){
|
||||
try{
|
||||
$entity = $entityFactory->createFromData($this, $nbt);
|
||||
}catch(NbtDataException $e){
|
||||
}catch(SavedDataLoadingException $e){
|
||||
$logger->error("Bad entity data at list position $k: " . $e->getMessage());
|
||||
$logger->logException($e);
|
||||
continue;
|
||||
@ -2500,7 +2500,7 @@ class World implements ChunkManager{
|
||||
foreach($chunkData->getTileNBT() as $k => $nbt){
|
||||
try{
|
||||
$tile = $tileFactory->createFromData($this, $nbt);
|
||||
}catch(NbtDataException $e){
|
||||
}catch(SavedDataLoadingException $e){
|
||||
$logger->error("Bad tile entity data at list position $k: " . $e->getMessage());
|
||||
$logger->logException($e);
|
||||
continue;
|
||||
|
Loading…
x
Reference in New Issue
Block a user