Revamp Entity construction

This is a similar refactor to the one I recently did for tiles.

- Entity::createEntity() is removed. In its place are Entity::create() (runtime creation, use where you'd use a constructor, accepts a ::class parameter, throws exceptions on unknown entities) and Entity::createFromData() (internal, used to restore entities from chunks, swallows unknown entities and returns null).
- Entity::registerEntity() is renamed to Entity::register().
- Added Entity::override() to allow overriding factory classes without touching save IDs. This allows more cleanly extending & overriding entities. This method only allows overriding registered Entity classes with children of that class, which makes code using the factory much more sane and allows to provide safety guarantees which make the code less nasty.
- Entity::getKnownEntityTypes() is renamed to Entity::getKnownTypes().
- ProjectileItem::getProjectileEntityType() now returns a ::class constant instead of a stringy ID.
- Cleaned up a bunch of nasty code, particularly in Bow.
This commit is contained in:
Dylan K. Taylor 2019-01-06 23:33:36 +00:00
parent 3ae722867c
commit b1cef8509a
16 changed files with 224 additions and 169 deletions

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\entity\object\FallingBlock;
use pocketmine\math\Facing;
abstract class Fallable extends Solid{
@ -37,11 +38,9 @@ abstract class Fallable extends Solid{
$nbt->setInt("TileID", $this->getId());
$nbt->setByte("Data", $this->getDamage());
$fall = Entity::createEntity("FallingSand", $this->getLevel(), $nbt);
if($fall !== null){
$fall->spawnToAll();
}
/** @var FallingBlock $fall */
$fall = Entity::create(FallingBlock::class, $this->getLevel(), $nbt);
$fall->spawnToAll();
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\entity\object\PrimedTNT;
use pocketmine\entity\projectile\Arrow;
use pocketmine\item\FlintSteel;
use pocketmine\item\Item;
@ -77,11 +78,9 @@ class TNT extends Solid{
$nbt = Entity::createBaseNBT($this->add(0.5, 0, 0.5), new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02));
$nbt->setShort("Fuse", $fuse);
$tnt = Entity::createEntity("PrimedTNT", $this->getLevel(), $nbt);
if($tnt !== null){
$tnt->spawnToAll();
}
/** @var PrimedTNT $tnt */
$tnt = Entity::create(PrimedTNT::class, $this->getLevel(), $nbt);
$tnt->spawnToAll();
}
public function getFlameEncouragement() : int{

View File

@ -61,6 +61,7 @@ use pocketmine\metadata\MetadataValue;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\AddEntityPacket;
@ -77,7 +78,7 @@ use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
use pocketmine\utils\Utils;
use function abs;
use function array_unique;
use function array_keys;
use function assert;
use function cos;
use function count;
@ -86,6 +87,7 @@ use function deg2rad;
use function floor;
use function get_class;
use function in_array;
use function is_a;
use function is_array;
use function is_infinite;
use function is_nan;
@ -282,6 +284,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
private static $knownEntities = [];
/** @var string[][] */
private static $saveNames = [];
/** @var string[] base class => currently used class for construction */
private static $classMapping = [];
/**
* Called on server startup to register default entity types.
@ -290,22 +294,22 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
//define legacy save IDs first - use them for saving for maximum compatibility with Minecraft PC
//TODO: index them by version to allow proper multi-save compatibility
Entity::registerEntity(Arrow::class, false, ['Arrow', 'minecraft:arrow']);
Entity::registerEntity(Egg::class, false, ['Egg', 'minecraft:egg']);
Entity::registerEntity(EnderPearl::class, false, ['ThrownEnderpearl', 'minecraft:ender_pearl']);
Entity::registerEntity(ExperienceBottle::class, false, ['ThrownExpBottle', 'minecraft:xp_bottle']);
Entity::registerEntity(ExperienceOrb::class, false, ['XPOrb', 'minecraft:xp_orb']);
Entity::registerEntity(FallingBlock::class, false, ['FallingSand', 'minecraft:falling_block']);
Entity::registerEntity(ItemEntity::class, false, ['Item', 'minecraft:item']);
Entity::registerEntity(Painting::class, false, ['Painting', 'minecraft:painting']);
Entity::registerEntity(PrimedTNT::class, false, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt']);
Entity::registerEntity(Snowball::class, false, ['Snowball', 'minecraft:snowball']);
Entity::registerEntity(SplashPotion::class, false, ['ThrownPotion', 'minecraft:potion', 'thrownpotion']);
Entity::registerEntity(Squid::class, false, ['Squid', 'minecraft:squid']);
Entity::registerEntity(Villager::class, false, ['Villager', 'minecraft:villager']);
Entity::registerEntity(Zombie::class, false, ['Zombie', 'minecraft:zombie']);
self::register(Arrow::class, false, ['Arrow', 'minecraft:arrow']);
self::register(Egg::class, false, ['Egg', 'minecraft:egg']);
self::register(EnderPearl::class, false, ['ThrownEnderpearl', 'minecraft:ender_pearl']);
self::register(ExperienceBottle::class, false, ['ThrownExpBottle', 'minecraft:xp_bottle']);
self::register(ExperienceOrb::class, false, ['XPOrb', 'minecraft:xp_orb']);
self::register(FallingBlock::class, false, ['FallingSand', 'minecraft:falling_block']);
self::register(ItemEntity::class, false, ['Item', 'minecraft:item']);
self::register(Painting::class, false, ['Painting', 'minecraft:painting']);
self::register(PrimedTNT::class, false, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt']);
self::register(Snowball::class, false, ['Snowball', 'minecraft:snowball']);
self::register(SplashPotion::class, false, ['ThrownPotion', 'minecraft:potion', 'thrownpotion']);
self::register(Squid::class, false, ['Squid', 'minecraft:squid']);
self::register(Villager::class, false, ['Villager', 'minecraft:villager']);
self::register(Zombie::class, false, ['Zombie', 'minecraft:zombie']);
Entity::registerEntity(Human::class, true);
self::register(Human::class, true);
Attribute::init();
Effect::init();
@ -315,23 +319,62 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
/**
* Creates an entity with the specified type, level and NBT, with optional additional arguments to pass to the
* entity's constructor
* entity's constructor.
*
* @param int|string $type
* TODO: make this NBT-independent
*
* @param string $baseClass
* @param Level $level
* @param CompoundTag $nbt
* @param mixed ...$args
*
* @return Entity|null
* @return Entity instanceof $baseClass
* @throws \InvalidArgumentException if the class doesn't exist or is not registered
*/
public static function createEntity($type, Level $level, CompoundTag $nbt, ...$args) : ?Entity{
if(isset(self::$knownEntities[$type])){
$class = self::$knownEntities[$type];
/** @see Entity::__construct() */
return new $class($level, $nbt, ...$args);
public static function create(string $baseClass, Level $level, CompoundTag $nbt, ...$args) : Entity{
if(isset(self::$classMapping[$baseClass])){
$class = self::$classMapping[$baseClass];
assert(is_a($class, $baseClass, true));
/**
* @var Entity $entity
* @see Entity::__construct()
*/
$entity = new $class($level, $nbt, ...$args);
return $entity;
}
return null;
throw new \InvalidArgumentException("Class $baseClass is not a registered entity");
}
/**
* Creates an entity from data stored on a chunk.
* @internal
*
* @param Level $level
* @param CompoundTag $nbt
*
* @return Entity|null
* @throws \RuntimeException
*/
public static function createFromData(Level $level, CompoundTag $nbt) : ?Entity{
$saveId = $nbt->getTag("id");
$baseClass = null;
if($saveId instanceof StringTag){
$baseClass = self::$knownEntities[$saveId->getValue()] ?? null;
}elseif($saveId instanceof IntTag){ //legacy MCPE format
$baseClass = self::$knownEntities[$saveId->getValue() & 0xff] ?? null;
}
if($baseClass === null){
return null;
}
$class = self::$classMapping[$baseClass];
assert(is_a($class, $baseClass, true));
/**
* @var Entity $entity
* @see Entity::__construct()
*/
$entity = new $class($level, $nbt);
return $entity;
}
/**
@ -346,7 +389,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
*
* @throws \InvalidArgumentException
*/
public static function registerEntity(string $className, bool $force = false, array $saveNames = []) : void{
public static function register(string $className, bool $force = false, array $saveNames = []) : void{
Utils::testValidInstance($className, Entity::class);
/** @var Entity $className */
@ -355,6 +398,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
}elseif(!$force){
throw new \InvalidArgumentException("Class $className does not declare a valid NETWORK_ID and not force-registering");
}
self::$classMapping[$className] = $className;
$shortName = (new \ReflectionClass($className))->getShortName();
if(!in_array($shortName, $saveNames, true)){
@ -368,13 +412,31 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
self::$saveNames[$className] = $saveNames;
}
/**
* Registers a class override for the given class. When a new entity is constructed using the factory, the new class
* will be used instead of the base class.
*
* @param string $baseClass Already-registered entity class to override
* @param string $newClass Class which extends the base class
*
* @throws \InvalidArgumentException
*/
public static function override(string $baseClass, string $newClass) : void{
if(!isset(self::$classMapping[$baseClass])){
throw new \InvalidArgumentException("Class $baseClass is not a registered entity");
}
Utils::testValidInstance($newClass, $baseClass);
self::$classMapping[$baseClass] = $newClass;
}
/**
* Returns an array of all registered entity classpaths.
*
* @return string[]
*/
public static function getKnownEntityTypes() : array{
return array_unique(self::$knownEntities);
public static function getKnownTypes() : array{
return array_keys(self::$classMapping);
}
/**

View File

@ -65,61 +65,56 @@ class Bow extends Tool{
$p = $diff / 20;
$force = min((($p ** 2) + $p * 2) / 3, 1) * 2;
/** @var ArrowEntity $entity */
$entity = Entity::create(ArrowEntity::class, $player->getLevel(), $nbt, $player, $force == 2);
$entity = Entity::createEntity("Arrow", $player->getLevel(), $nbt, $player, $force == 2);
if($entity instanceof Projectile){
$infinity = $this->hasEnchantment(Enchantment::INFINITY);
if($entity instanceof ArrowEntity){
if($infinity){
$entity->setPickupMode(ArrowEntity::PICKUP_CREATIVE);
}
if(($punchLevel = $this->getEnchantmentLevel(Enchantment::PUNCH)) > 0){
$entity->setPunchKnockback($punchLevel);
}
}
if(($powerLevel = $this->getEnchantmentLevel(Enchantment::POWER)) > 0){
$entity->setBaseDamage($entity->getBaseDamage() + (($powerLevel + 1) / 2));
}
if($this->hasEnchantment(Enchantment::FLAME)){
$entity->setOnFire(intdiv($entity->getFireTicks(), 20) + 100);
}
$ev = new EntityShootBowEvent($player, $this, $entity, $force);
$infinity = $this->hasEnchantment(Enchantment::INFINITY);
if($infinity){
$entity->setPickupMode(ArrowEntity::PICKUP_CREATIVE);
}
if(($punchLevel = $this->getEnchantmentLevel(Enchantment::PUNCH)) > 0){
$entity->setPunchKnockback($punchLevel);
}
if(($powerLevel = $this->getEnchantmentLevel(Enchantment::POWER)) > 0){
$entity->setBaseDamage($entity->getBaseDamage() + (($powerLevel + 1) / 2));
}
if($this->hasEnchantment(Enchantment::FLAME)){
$entity->setOnFire(intdiv($entity->getFireTicks(), 20) + 100);
}
$ev = new EntityShootBowEvent($player, $this, $entity, $force);
if($force < 0.1 or $diff < 5){
$ev->setCancelled();
}
if($force < 0.1 or $diff < 5){
$ev->setCancelled();
}
$ev->call();
$ev->call();
$entity = $ev->getProjectile(); //This might have been changed by plugins
$entity = $ev->getProjectile(); //This might have been changed by plugins
if($ev->isCancelled()){
$entity->flagForDespawn();
$player->getInventory()->sendContents($player);
}else{
$entity->setMotion($entity->getMotion()->multiply($ev->getForce()));
if($player->isSurvival()){
if(!$infinity){ //TODO: tipped arrows are still consumed when Infinity is applied
$player->getInventory()->removeItem(ItemFactory::get(Item::ARROW, 0, 1));
}
$this->applyDamage(1);
}
if($entity instanceof Projectile){
$projectileEv = new ProjectileLaunchEvent($entity);
$projectileEv->call();
if($projectileEv->isCancelled()){
$ev->getProjectile()->flagForDespawn();
}else{
$ev->getProjectile()->spawnToAll();
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW);
}
}else{
$entity->spawnToAll();
}
}
if($ev->isCancelled()){
$entity->flagForDespawn();
$player->getInventory()->sendContents($player);
}else{
$entity->spawnToAll();
$entity->setMotion($entity->getMotion()->multiply($ev->getForce()));
if($player->isSurvival()){
if(!$infinity){ //TODO: tipped arrows are still consumed when Infinity is applied
$player->getInventory()->removeItem(ItemFactory::get(Item::ARROW, 0, 1));
}
$this->applyDamage(1);
}
if($entity instanceof Projectile){
$projectileEv = new ProjectileLaunchEvent($entity);
$projectileEv->call();
if($projectileEv->isCancelled()){
$ev->getProjectile()->flagForDespawn();
}else{
$ev->getProjectile()->spawnToAll();
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW);
}
}else{
$entity->spawnToAll();
}
}
return true;

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\projectile\Egg as EggEntity;
class Egg extends ProjectileItem{
public function __construct(){
parent::__construct(self::EGG, 0, "Egg");
@ -32,8 +34,8 @@ class Egg extends ProjectileItem{
return 16;
}
public function getProjectileEntityType() : string{
return "Egg";
public function getProjectileEntityClass() : string{
return EggEntity::class;
}
public function getThrowForce() : float{

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\projectile\EnderPearl as EnderPearlEntity;
class EnderPearl extends ProjectileItem{
public function __construct(){
parent::__construct(self::ENDER_PEARL, 0, "Ender Pearl");
@ -32,8 +34,8 @@ class EnderPearl extends ProjectileItem{
return 16;
}
public function getProjectileEntityType() : string{
return "ThrownEnderpearl";
public function getProjectileEntityClass() : string{
return EnderPearlEntity::class;
}
public function getThrowForce() : float{

View File

@ -23,13 +23,15 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\projectile\ExperienceBottle as ExperienceBottleEntity;
class ExperienceBottle extends ProjectileItem{
public function __construct(){
parent::__construct(self::EXPERIENCE_BOTTLE, 0, "Bottle o' Enchanting");
}
public function getProjectileEntityType() : string{
return "ThrownExpBottle";
public function getProjectileEntityClass() : string{
return ExperienceBottleEntity::class;
}
public function getThrowForce() : float{

View File

@ -194,10 +194,10 @@ class ItemFactory{
//TODO: ENDER_EYE
self::registerItem(new Item(Item::GLISTERING_MELON, 0, "Glistering Melon"));
foreach(Entity::getKnownEntityTypes() as $className){
/** @var Living $className */
foreach(Entity::getKnownTypes() as $className){
/** @var Living|string $className */
if(is_a($className, Living::class, true) and $className::NETWORK_ID !== -1){
self::registerItem(new SpawnEgg(Item::SPAWN_EGG, $className::NETWORK_ID, "Spawn Egg"));
self::registerItem(new SpawnEgg(Item::SPAWN_EGG, $className::NETWORK_ID, $className, "Spawn Egg"));
}
}

View File

@ -93,16 +93,12 @@ class PaintingItem extends Item{
$nbt->setInt("TileY", $blockClicked->getFloorY());
$nbt->setInt("TileZ", $blockClicked->getFloorZ());
$entity = Entity::createEntity("Painting", $blockReplace->getLevel(), $nbt);
/** @var Painting $entity */
$entity = Entity::create(Painting::class, $blockReplace->getLevel(), $nbt);
$this->pop();
$entity->spawnToAll();
if($entity instanceof Entity){
$this->pop();
$entity->spawnToAll();
$player->getLevel()->broadcastLevelEvent($blockReplace->add(0.5, 0.5, 0.5), LevelEventPacket::EVENT_SOUND_ITEMFRAME_PLACE); //item frame and painting have the same sound
return true;
}
return false;
$player->getLevel()->broadcastLevelEvent($blockReplace->add(0.5, 0.5, 0.5), LevelEventPacket::EVENT_SOUND_ITEMFRAME_PLACE); //item frame and painting have the same sound
return true;
}
}

View File

@ -25,16 +25,22 @@ namespace pocketmine\item;
use pocketmine\entity\Entity;
use pocketmine\entity\EntityIds;
use pocketmine\entity\projectile\Projectile;
use pocketmine\entity\projectile\Throwable;
use pocketmine\event\entity\ProjectileLaunchEvent;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\Player;
use pocketmine\utils\Utils;
abstract class ProjectileItem extends Item{
abstract public function getProjectileEntityType() : string;
/**
* Returns the entity type that this projectile creates. This should return a ::class extending Throwable.
*
* @return string class extends Throwable
*/
abstract public function getProjectileEntityClass() : string;
abstract public function getThrowForce() : float;
@ -51,29 +57,25 @@ abstract class ProjectileItem extends Item{
$nbt = Entity::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch);
$this->addExtraTags($nbt);
$projectile = Entity::createEntity($this->getProjectileEntityType(), $player->getLevel(), $nbt, $player);
if($projectile !== null){
$projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce()));
$class = $this->getProjectileEntityClass();
Utils::testValidInstance($class, Throwable::class);
/** @var Throwable $projectile */
$projectile = Entity::create($class, $player->getLevel(), $nbt, $player);
$projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce()));
$projectileEv = new ProjectileLaunchEvent($projectile);
$projectileEv->call();
if($projectileEv->isCancelled()){
$projectile->flagForDespawn();
}else{
$projectile->spawnToAll();
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 0, EntityIds::PLAYER);
}
$this->pop();
if($projectile instanceof Projectile){
$projectileEv = new ProjectileLaunchEvent($projectile);
$projectileEv->call();
if($projectileEv->isCancelled()){
$projectile->flagForDespawn();
}else{
$projectile->spawnToAll();
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 0, EntityIds::PLAYER);
}
}elseif($projectile !== null){
$projectile->spawnToAll();
}else{
return false;
}
return true;
}
}

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\projectile\Snowball as SnowballEntity;
class Snowball extends ProjectileItem{
public function __construct(){
parent::__construct(self::SNOWBALL, 0, "Snowball");
@ -32,8 +34,8 @@ class Snowball extends ProjectileItem{
return 16;
}
public function getProjectileEntityType() : string{
return "Snowball";
public function getProjectileEntityClass() : string{
return SnowballEntity::class;
}
public function getThrowForce() : float{

View File

@ -27,10 +27,28 @@ use pocketmine\block\Block;
use pocketmine\entity\Entity;
use pocketmine\math\Vector3;
use pocketmine\Player;
use pocketmine\utils\Utils;
use function lcg_value;
class SpawnEgg extends Item{
/** @var string */
private $entityClass;
/**
* @param int $id
* @param int $variant
* @param string $entityClass instanceof Entity
* @param string $name
*
* @throws \InvalidArgumentException
*/
public function __construct(int $id, int $variant, string $entityClass, string $name = "Unknown"){
parent::__construct($id, $variant, $name);
Utils::testValidInstance($entityClass, Entity::class);
$this->entityClass = $entityClass;
}
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
$nbt = Entity::createBaseNBT($blockReplace->add(0.5, 0, 0.5), null, lcg_value() * 360, 0);
@ -38,14 +56,9 @@ class SpawnEgg extends Item{
$nbt->setString("CustomName", $this->getCustomName());
}
$entity = Entity::createEntity($this->meta, $player->getLevel(), $nbt);
if($entity instanceof Entity){
$this->pop();
$entity->spawnToAll();
return true;
}
return false;
$entity = Entity::create($this->entityClass, $player->getLevel(), $nbt);
$this->pop();
$entity->spawnToAll();
return true;
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\projectile\SplashPotion as SplashPotionEntity;
use pocketmine\nbt\tag\CompoundTag;
class SplashPotion extends ProjectileItem{
@ -35,8 +36,8 @@ class SplashPotion extends ProjectileItem{
return 1;
}
public function getProjectileEntityType() : string{
return "ThrownPotion";
public function getProjectileEntityClass() : string{
return SplashPotionEntity::class;
}
public function getThrowForce() : float{

View File

@ -1634,13 +1634,11 @@ class Level implements ChunkManager, Metadatable{
$nbt->setShort("Health", 5);
$nbt->setShort("PickupDelay", $delay);
$nbt->setTag($itemTag);
$itemEntity = Entity::createEntity("Item", $this, $nbt);
if($itemEntity instanceof ItemEntity){
$itemEntity->spawnToAll();
return $itemEntity;
}
/** @var ItemEntity $itemEntity */
$itemEntity = Entity::create(ItemEntity::class, $this, $nbt);
$itemEntity->spawnToAll();
return $itemEntity;
}
return null;
}
@ -1666,15 +1664,10 @@ class Level implements ChunkManager, Metadatable{
);
$nbt->setShort(ExperienceOrb::TAG_VALUE_PC, $split);
$orb = Entity::createEntity("XPOrb", $this, $nbt);
if($orb === null){
continue;
}
/** @var ExperienceOrb $orb */
$orb = Entity::create(ExperienceOrb::class, $this, $nbt);
$orb->spawnToAll();
if($orb instanceof ExperienceOrb){
$orbs[] = $orb;
}
$orbs[] = $orb;
}
return $orbs;

View File

@ -600,18 +600,13 @@ class Chunk{
$level->timings->syncChunkLoadEntitiesTimer->startTiming();
foreach($this->NBTentities as $nbt){
if($nbt instanceof CompoundTag){
if(!$nbt->hasTag("id")){ //allow mixed types (because of leveldb)
$changed = true;
continue;
}
try{
$entity = Entity::createEntity($nbt->getTag("id")->getValue(), $level, $nbt);
$entity = Entity::createFromData($level, $nbt);
if(!($entity instanceof Entity)){
$changed = true;
continue;
}
}catch(\Throwable $t){
}catch(\Exception $t){ //TODO: this shouldn't be here
$level->getServer()->getLogger()->logException($t);
$changed = true;
continue;

View File

@ -33,7 +33,6 @@ use pocketmine\level\format\io\LevelData;
use pocketmine\level\format\SubChunk;
use pocketmine\nbt\LittleEndianNBTStream;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\utils\Binary;
use pocketmine\utils\BinaryStream;
use function array_values;
@ -248,13 +247,6 @@ class LevelDB extends BaseLevelProvider{
$entities = $nbt->readMultiple($entityData);
}
/** @var CompoundTag $entityNBT */
foreach($entities as $entityNBT){
if($entityNBT->hasTag("id", IntTag::class)){
$entityNBT->setInt("id", $entityNBT->getInt("id") & 0xff); //remove type flags - TODO: use these instead of removing them)
}
}
/** @var CompoundTag[] $tiles */
$tiles = [];
if(($tileData = $this->db->get($index . self::TAG_BLOCK_ENTITY)) !== false and $tileData !== ""){