diff --git a/src/Server.php b/src/Server.php index 567cfb3e3..eac08dea9 100644 --- a/src/Server.php +++ b/src/Server.php @@ -33,7 +33,6 @@ use pocketmine\command\ConsoleCommandSender; use pocketmine\command\PluginIdentifiableCommand; use pocketmine\command\SimpleCommandMap; use pocketmine\crafting\CraftingManager; -use pocketmine\entity\EntityFactory; use pocketmine\event\HandlerListManager; use pocketmine\event\player\PlayerDataSaveEvent; use pocketmine\event\server\CommandEvent; @@ -981,7 +980,6 @@ class Server{ $this->commandMap = new SimpleCommandMap($this); - EntityFactory::init(); Enchantment::init(); Biome::init(); diff --git a/src/block/TNT.php b/src/block/TNT.php index b42b06d5a..b20b09016 100644 --- a/src/block/TNT.php +++ b/src/block/TNT.php @@ -97,7 +97,7 @@ class TNT extends Opaque{ $nbt->setShort("Fuse", $fuse); /** @var PrimedTNT $tnt */ - $tnt = EntityFactory::create(PrimedTNT::class, $this->pos->getWorldNonNull(), $nbt); + $tnt = EntityFactory::getInstance()->create(PrimedTNT::class, $this->pos->getWorldNonNull(), $nbt); $tnt->spawnToAll(); } diff --git a/src/block/utils/FallableTrait.php b/src/block/utils/FallableTrait.php index 1d22c123e..c1a081703 100644 --- a/src/block/utils/FallableTrait.php +++ b/src/block/utils/FallableTrait.php @@ -56,7 +56,7 @@ trait FallableTrait{ $nbt->setByte("Data", $this->getMeta()); /** @var FallingBlock $fall */ - $fall = EntityFactory::create(FallingBlock::class, $pos->getWorldNonNull(), $nbt); + $fall = EntityFactory::getInstance()->create(FallingBlock::class, $pos->getWorldNonNull(), $nbt); $fall->spawnToAll(); } } diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 0d3dce992..84fafbdcf 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -490,7 +490,7 @@ abstract class Entity{ public function saveNBT() : CompoundTag{ $nbt = new CompoundTag(); if(!($this instanceof Player)){ - $nbt->setString("id", EntityFactory::getSaveId(get_class($this))); + $nbt->setString("id", EntityFactory::getInstance()->getSaveId(get_class($this))); if($this->getNameTag() !== ""){ $nbt->setString("CustomName", $this->getNameTag()); diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index 198fff621..a8119ed5d 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -43,6 +43,7 @@ use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; +use pocketmine\utils\SingletonTrait; use pocketmine\utils\Utils; use pocketmine\world\World; use function array_keys; @@ -60,52 +61,47 @@ use function reset; * create(MyEntity::class) instead of `new MyEntity()` if you want to allow this. */ final class EntityFactory{ + use SingletonTrait; /** @var int */ private static $entityCount = 1; + /** * @var string[] base class => currently used class for construction * @phpstan-var array, class-string> */ - private static $classMapping = []; + private $classMapping = []; /** * @var string[] * @phpstan-var array> */ - private static $knownEntities = []; + private $knownEntities = []; /** * @var string[][] * @phpstan-var array, list> */ - private static $saveNames = []; + private $saveNames = []; - private function __construct(){ - //NOOP - } - - /** - * Called on server startup to register default entity types. - */ - public static function init() : void{ + public function __construct(){ //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 - self::register(Arrow::class, ['Arrow', 'minecraft:arrow'], EntityLegacyIds::ARROW); - self::register(Egg::class, ['Egg', 'minecraft:egg'], EntityLegacyIds::EGG); - self::register(EnderPearl::class, ['ThrownEnderpearl', 'minecraft:ender_pearl'], EntityLegacyIds::ENDER_PEARL); - self::register(ExperienceBottle::class, ['ThrownExpBottle', 'minecraft:xp_bottle'], EntityLegacyIds::XP_BOTTLE); - self::register(ExperienceOrb::class, ['XPOrb', 'minecraft:xp_orb'], EntityLegacyIds::XP_ORB); - self::register(FallingBlock::class, ['FallingSand', 'minecraft:falling_block'], EntityLegacyIds::FALLING_BLOCK); - self::register(ItemEntity::class, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM); - self::register(Painting::class, ['Painting', 'minecraft:painting'], EntityLegacyIds::PAINTING); - self::register(PrimedTNT::class, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt'], EntityLegacyIds::TNT); - self::register(Snowball::class, ['Snowball', 'minecraft:snowball'], EntityLegacyIds::SNOWBALL); - self::register(SplashPotion::class, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION); - self::register(Squid::class, ['Squid', 'minecraft:squid'], EntityLegacyIds::SQUID); - self::register(Villager::class, ['Villager', 'minecraft:villager'], EntityLegacyIds::VILLAGER); - self::register(Zombie::class, ['Zombie', 'minecraft:zombie'], EntityLegacyIds::ZOMBIE); + $this->register(Arrow::class, ['Arrow', 'minecraft:arrow'], EntityLegacyIds::ARROW); + $this->register(Egg::class, ['Egg', 'minecraft:egg'], EntityLegacyIds::EGG); + $this->register(EnderPearl::class, ['ThrownEnderpearl', 'minecraft:ender_pearl'], EntityLegacyIds::ENDER_PEARL); + $this->register(ExperienceBottle::class, ['ThrownExpBottle', 'minecraft:xp_bottle'], EntityLegacyIds::XP_BOTTLE); + $this->register(ExperienceOrb::class, ['XPOrb', 'minecraft:xp_orb'], EntityLegacyIds::XP_ORB); + $this->register(FallingBlock::class, ['FallingSand', 'minecraft:falling_block'], EntityLegacyIds::FALLING_BLOCK); + $this->register(ItemEntity::class, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM); + $this->register(Painting::class, ['Painting', 'minecraft:painting'], EntityLegacyIds::PAINTING); + $this->register(PrimedTNT::class, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt'], EntityLegacyIds::TNT); + $this->register(Snowball::class, ['Snowball', 'minecraft:snowball'], EntityLegacyIds::SNOWBALL); + $this->register(SplashPotion::class, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION); + $this->register(Squid::class, ['Squid', 'minecraft:squid'], EntityLegacyIds::SQUID); + $this->register(Villager::class, ['Villager', 'minecraft:villager'], EntityLegacyIds::VILLAGER); + $this->register(Zombie::class, ['Zombie', 'minecraft:zombie'], EntityLegacyIds::ZOMBIE); - self::register(Human::class, ['Human']); + $this->register(Human::class, ['Human']); Attribute::init(); PaintingMotive::init(); @@ -124,10 +120,10 @@ final class EntityFactory{ * * @throws \InvalidArgumentException */ - public static function register(string $className, array $saveNames, ?int $legacyMcpeSaveId = null) : void{ + public function register(string $className, array $saveNames, ?int $legacyMcpeSaveId = null) : void{ Utils::testValidInstance($className, Entity::class); - self::$classMapping[$className] = $className; + $this->classMapping[$className] = $className; $shortName = (new \ReflectionClass($className))->getShortName(); if(!in_array($shortName, $saveNames, true)){ @@ -135,13 +131,13 @@ final class EntityFactory{ } foreach($saveNames as $name){ - self::$knownEntities[$name] = $className; + $this->knownEntities[$name] = $className; } if($legacyMcpeSaveId !== null){ - self::$knownEntities[$legacyMcpeSaveId] = $className; + $this->knownEntities[$legacyMcpeSaveId] = $className; } - self::$saveNames[$className] = $saveNames; + $this->saveNames[$className] = $saveNames; } /** @@ -157,13 +153,13 @@ final class EntityFactory{ * * @throws \InvalidArgumentException */ - public static function override(string $baseClass, string $newClass) : void{ - if(!isset(self::$classMapping[$baseClass])){ + public function override(string $baseClass, string $newClass) : void{ + if(!isset($this->classMapping[$baseClass])){ throw new \InvalidArgumentException("Class $baseClass is not a registered entity"); } Utils::testValidInstance($newClass, $baseClass); - self::$classMapping[$baseClass] = $newClass; + $this->classMapping[$baseClass] = $newClass; } /** @@ -172,8 +168,8 @@ final class EntityFactory{ * @return string[] * @return class-string[] */ - public static function getKnownTypes() : array{ - return array_keys(self::$classMapping); + public function getKnownTypes() : array{ + return array_keys($this->classMapping); } /** @@ -199,9 +195,9 @@ final class EntityFactory{ * * @throws \InvalidArgumentException if the class doesn't exist or is not registered */ - public static function create(string $baseClass, World $world, CompoundTag $nbt, ...$args) : Entity{ - if(isset(self::$classMapping[$baseClass])){ - $class = self::$classMapping[$baseClass]; + public function create(string $baseClass, World $world, CompoundTag $nbt, ...$args) : Entity{ + if(isset($this->classMapping[$baseClass])){ + $class = $this->classMapping[$baseClass]; assert(is_a($class, $baseClass, true)); /** * @var Entity $entity @@ -222,18 +218,18 @@ final class EntityFactory{ * @throws \RuntimeException * @internal */ - public static function createFromData(World $world, CompoundTag $nbt) : ?Entity{ + public function createFromData(World $world, CompoundTag $nbt) : ?Entity{ $saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier"); $baseClass = null; if($saveId instanceof StringTag){ - $baseClass = self::$knownEntities[$saveId->getValue()] ?? null; + $baseClass = $this->knownEntities[$saveId->getValue()] ?? null; }elseif($saveId instanceof IntTag){ //legacy MCPE format - $baseClass = self::$knownEntities[$saveId->getValue() & 0xff] ?? null; + $baseClass = $this->knownEntities[$saveId->getValue() & 0xff] ?? null; } if($baseClass === null){ return null; } - $class = self::$classMapping[$baseClass]; + $class = $this->classMapping[$baseClass]; assert(is_a($class, $baseClass, true)); /** * @var Entity $entity @@ -247,9 +243,9 @@ final class EntityFactory{ /** * @phpstan-param class-string $class */ - public static function getSaveId(string $class) : string{ - if(isset(self::$saveNames[$class])){ - return reset(self::$saveNames[$class]); + public function getSaveId(string $class) : string{ + if(isset($this->saveNames[$class])){ + return reset($this->saveNames[$class]); } throw new \InvalidArgumentException("Entity $class is not registered"); } diff --git a/src/item/Bow.php b/src/item/Bow.php index 1700f1381..673c696b7 100644 --- a/src/item/Bow.php +++ b/src/item/Bow.php @@ -64,7 +64,7 @@ class Bow extends Tool{ $baseForce = min((($p ** 2) + $p * 2) / 3, 1); /** @var ArrowEntity $entity */ - $entity = EntityFactory::create(ArrowEntity::class, $location->getWorld(), $nbt, $player, $baseForce >= 1); + $entity = EntityFactory::getInstance()->create(ArrowEntity::class, $location->getWorld(), $nbt, $player, $baseForce >= 1); $infinity = $this->hasEnchantment(Enchantment::INFINITY()); if($infinity){ diff --git a/src/item/ItemFactory.php b/src/item/ItemFactory.php index 4d6884ff0..2f54c4ec6 100644 --- a/src/item/ItemFactory.php +++ b/src/item/ItemFactory.php @@ -257,7 +257,7 @@ class ItemFactory{ $this->register(new SplashPotion(ItemIds::SPLASH_POTION, $type, "Splash Potion")); } - foreach(EntityFactory::getKnownTypes() as $className){ + foreach(EntityFactory::getInstance()->getKnownTypes() as $className){ /** @var Living|string $className */ if(is_a($className, Living::class, true) and $className::NETWORK_ID !== -1){ $this->register(new SpawnEgg(ItemIds::SPAWN_EGG, $className::NETWORK_ID, "Spawn Egg", $className)); diff --git a/src/item/PaintingItem.php b/src/item/PaintingItem.php index 54d6d7ffa..1bd726510 100644 --- a/src/item/PaintingItem.php +++ b/src/item/PaintingItem.php @@ -94,7 +94,7 @@ class PaintingItem extends Item{ $nbt->setInt("TileZ", $clickedPos->getFloorZ()); /** @var Painting $entity */ - $entity = EntityFactory::create(Painting::class, $replacePos->getWorldNonNull(), $nbt); + $entity = EntityFactory::getInstance()->create(Painting::class, $replacePos->getWorldNonNull(), $nbt); $this->pop(); $entity->spawnToAll(); diff --git a/src/item/ProjectileItem.php b/src/item/ProjectileItem.php index 27e7b403e..027b6b86e 100644 --- a/src/item/ProjectileItem.php +++ b/src/item/ProjectileItem.php @@ -60,7 +60,7 @@ abstract class ProjectileItem extends Item{ Utils::testValidInstance($class, Throwable::class); /** @var Throwable $projectile */ - $projectile = EntityFactory::create($class, $location->getWorld(), $nbt, $player); + $projectile = EntityFactory::getInstance()->create($class, $location->getWorld(), $nbt, $player); $projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce())); $projectileEv = new ProjectileLaunchEvent($projectile); diff --git a/src/item/SpawnEgg.php b/src/item/SpawnEgg.php index 098283914..d74325526 100644 --- a/src/item/SpawnEgg.php +++ b/src/item/SpawnEgg.php @@ -58,7 +58,7 @@ class SpawnEgg extends Item{ $nbt->setString("CustomName", $this->getCustomName()); } - $entity = EntityFactory::create($this->entityClass, $player->getWorld(), $nbt); + $entity = EntityFactory::getInstance()->create($this->entityClass, $player->getWorld(), $nbt); $this->pop(); $entity->spawnToAll(); //TODO: what if the entity was marked for deletion? diff --git a/src/world/World.php b/src/world/World.php index 0dbd7ad92..a1ccf69be 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1397,7 +1397,7 @@ class World implements ChunkManager{ $nbt->setTag("Item", $item->nbtSerialize()); /** @var ItemEntity $itemEntity */ - $itemEntity = EntityFactory::create(ItemEntity::class, $this, $nbt); + $itemEntity = EntityFactory::getInstance()->create(ItemEntity::class, $this, $nbt); $itemEntity->spawnToAll(); return $itemEntity; @@ -1422,7 +1422,7 @@ class World implements ChunkManager{ $nbt->setShort(ExperienceOrb::TAG_VALUE_PC, $split); /** @var ExperienceOrb $orb */ - $orb = EntityFactory::create(ExperienceOrb::class, $this, $nbt); + $orb = EntityFactory::getInstance()->create(ExperienceOrb::class, $this, $nbt); $orb->spawnToAll(); $orbs[] = $orb; } diff --git a/src/world/format/Chunk.php b/src/world/format/Chunk.php index 09c329b61..bbfa16971 100644 --- a/src/world/format/Chunk.php +++ b/src/world/format/Chunk.php @@ -507,9 +507,10 @@ class Chunk{ if($this->NBTentities !== null){ $this->dirtyFlags |= self::DIRTY_FLAG_ENTITIES; $world->timings->syncChunkLoadEntitiesTimer->startTiming(); + $entityFactory = EntityFactory::getInstance(); foreach($this->NBTentities as $nbt){ try{ - $entity = EntityFactory::createFromData($world, $nbt); + $entity = $entityFactory->createFromData($world, $nbt); if(!($entity instanceof Entity)){ $saveIdTag = $nbt->getTag("id") ?? $nbt->getTag("identifier"); $saveId = "";