diff --git a/src/pocketmine/block/Fallable.php b/src/pocketmine/block/Fallable.php index b9bc89571..32dac2a97 100644 --- a/src/pocketmine/block/Fallable.php +++ b/src/pocketmine/block/Fallable.php @@ -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(); } } diff --git a/src/pocketmine/block/TNT.php b/src/pocketmine/block/TNT.php index e7ed6f6cc..50b67f417 100644 --- a/src/pocketmine/block/TNT.php +++ b/src/pocketmine/block/TNT.php @@ -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{ diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index c5b100779..136b40bcb 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -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); } /** diff --git a/src/pocketmine/item/Bow.php b/src/pocketmine/item/Bow.php index 1e28f6012..5105434a5 100644 --- a/src/pocketmine/item/Bow.php +++ b/src/pocketmine/item/Bow.php @@ -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; diff --git a/src/pocketmine/item/Egg.php b/src/pocketmine/item/Egg.php index 9276342d4..bf6d15482 100644 --- a/src/pocketmine/item/Egg.php +++ b/src/pocketmine/item/Egg.php @@ -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{ diff --git a/src/pocketmine/item/EnderPearl.php b/src/pocketmine/item/EnderPearl.php index 5b375a391..8417c5806 100644 --- a/src/pocketmine/item/EnderPearl.php +++ b/src/pocketmine/item/EnderPearl.php @@ -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{ diff --git a/src/pocketmine/item/ExperienceBottle.php b/src/pocketmine/item/ExperienceBottle.php index 277957ec6..21d07cb42 100644 --- a/src/pocketmine/item/ExperienceBottle.php +++ b/src/pocketmine/item/ExperienceBottle.php @@ -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{ diff --git a/src/pocketmine/item/ItemFactory.php b/src/pocketmine/item/ItemFactory.php index 63e7134ce..0fbafa9b7 100644 --- a/src/pocketmine/item/ItemFactory.php +++ b/src/pocketmine/item/ItemFactory.php @@ -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")); } } diff --git a/src/pocketmine/item/PaintingItem.php b/src/pocketmine/item/PaintingItem.php index 3224b4923..69f833031 100644 --- a/src/pocketmine/item/PaintingItem.php +++ b/src/pocketmine/item/PaintingItem.php @@ -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; } } diff --git a/src/pocketmine/item/ProjectileItem.php b/src/pocketmine/item/ProjectileItem.php index 02092a9b2..3a6316a71 100644 --- a/src/pocketmine/item/ProjectileItem.php +++ b/src/pocketmine/item/ProjectileItem.php @@ -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; } } diff --git a/src/pocketmine/item/Snowball.php b/src/pocketmine/item/Snowball.php index c13f9abd7..6f46dfcb4 100644 --- a/src/pocketmine/item/Snowball.php +++ b/src/pocketmine/item/Snowball.php @@ -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{ diff --git a/src/pocketmine/item/SpawnEgg.php b/src/pocketmine/item/SpawnEgg.php index 5fa857d3a..8b25b8244 100644 --- a/src/pocketmine/item/SpawnEgg.php +++ b/src/pocketmine/item/SpawnEgg.php @@ -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; } } diff --git a/src/pocketmine/item/SplashPotion.php b/src/pocketmine/item/SplashPotion.php index 945b5f5e8..3f12c7b84 100644 --- a/src/pocketmine/item/SplashPotion.php +++ b/src/pocketmine/item/SplashPotion.php @@ -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{ diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index d53ef82a7..0ba50b794 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -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; diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 7cf615622..2800d745d 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -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; diff --git a/src/pocketmine/level/format/io/leveldb/LevelDB.php b/src/pocketmine/level/format/io/leveldb/LevelDB.php index 108b23682..18fd3f5ec 100644 --- a/src/pocketmine/level/format/io/leveldb/LevelDB.php +++ b/src/pocketmine/level/format/io/leveldb/LevelDB.php @@ -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 !== ""){