Convert EntityFactory to singleton

This commit is contained in:
Dylan K. Taylor 2020-04-24 22:43:02 +01:00
parent a75241ef03
commit 7d9df6af6f
12 changed files with 54 additions and 59 deletions

View File

@ -33,7 +33,6 @@ use pocketmine\command\ConsoleCommandSender;
use pocketmine\command\PluginIdentifiableCommand; use pocketmine\command\PluginIdentifiableCommand;
use pocketmine\command\SimpleCommandMap; use pocketmine\command\SimpleCommandMap;
use pocketmine\crafting\CraftingManager; use pocketmine\crafting\CraftingManager;
use pocketmine\entity\EntityFactory;
use pocketmine\event\HandlerListManager; use pocketmine\event\HandlerListManager;
use pocketmine\event\player\PlayerDataSaveEvent; use pocketmine\event\player\PlayerDataSaveEvent;
use pocketmine\event\server\CommandEvent; use pocketmine\event\server\CommandEvent;
@ -981,7 +980,6 @@ class Server{
$this->commandMap = new SimpleCommandMap($this); $this->commandMap = new SimpleCommandMap($this);
EntityFactory::init();
Enchantment::init(); Enchantment::init();
Biome::init(); Biome::init();

View File

@ -97,7 +97,7 @@ class TNT extends Opaque{
$nbt->setShort("Fuse", $fuse); $nbt->setShort("Fuse", $fuse);
/** @var PrimedTNT $tnt */ /** @var PrimedTNT $tnt */
$tnt = EntityFactory::create(PrimedTNT::class, $this->pos->getWorldNonNull(), $nbt); $tnt = EntityFactory::getInstance()->create(PrimedTNT::class, $this->pos->getWorldNonNull(), $nbt);
$tnt->spawnToAll(); $tnt->spawnToAll();
} }

View File

@ -56,7 +56,7 @@ trait FallableTrait{
$nbt->setByte("Data", $this->getMeta()); $nbt->setByte("Data", $this->getMeta());
/** @var FallingBlock $fall */ /** @var FallingBlock $fall */
$fall = EntityFactory::create(FallingBlock::class, $pos->getWorldNonNull(), $nbt); $fall = EntityFactory::getInstance()->create(FallingBlock::class, $pos->getWorldNonNull(), $nbt);
$fall->spawnToAll(); $fall->spawnToAll();
} }
} }

View File

@ -490,7 +490,7 @@ abstract class Entity{
public function saveNBT() : CompoundTag{ public function saveNBT() : CompoundTag{
$nbt = new CompoundTag(); $nbt = new CompoundTag();
if(!($this instanceof Player)){ if(!($this instanceof Player)){
$nbt->setString("id", EntityFactory::getSaveId(get_class($this))); $nbt->setString("id", EntityFactory::getInstance()->getSaveId(get_class($this)));
if($this->getNameTag() !== ""){ if($this->getNameTag() !== ""){
$nbt->setString("CustomName", $this->getNameTag()); $nbt->setString("CustomName", $this->getNameTag());

View File

@ -43,6 +43,7 @@ use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use pocketmine\world\World; use pocketmine\world\World;
use function array_keys; use function array_keys;
@ -60,52 +61,47 @@ use function reset;
* create(MyEntity::class) instead of `new MyEntity()` if you want to allow this. * create(MyEntity::class) instead of `new MyEntity()` if you want to allow this.
*/ */
final class EntityFactory{ final class EntityFactory{
use SingletonTrait;
/** @var int */ /** @var int */
private static $entityCount = 1; private static $entityCount = 1;
/** /**
* @var string[] base class => currently used class for construction * @var string[] base class => currently used class for construction
* @phpstan-var array<class-string<Entity>, class-string<Entity>> * @phpstan-var array<class-string<Entity>, class-string<Entity>>
*/ */
private static $classMapping = []; private $classMapping = [];
/** /**
* @var string[] * @var string[]
* @phpstan-var array<int|string, class-string<Entity>> * @phpstan-var array<int|string, class-string<Entity>>
*/ */
private static $knownEntities = []; private $knownEntities = [];
/** /**
* @var string[][] * @var string[][]
* @phpstan-var array<class-string<Entity>, list<string>> * @phpstan-var array<class-string<Entity>, list<string>>
*/ */
private static $saveNames = []; private $saveNames = [];
private function __construct(){ public function __construct(){
//NOOP
}
/**
* Called on server startup to register default entity types.
*/
public static function init() : void{
//define legacy save IDs first - use them for saving for maximum compatibility with Minecraft PC //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 //TODO: index them by version to allow proper multi-save compatibility
self::register(Arrow::class, ['Arrow', 'minecraft:arrow'], EntityLegacyIds::ARROW); $this->register(Arrow::class, ['Arrow', 'minecraft:arrow'], EntityLegacyIds::ARROW);
self::register(Egg::class, ['Egg', 'minecraft:egg'], EntityLegacyIds::EGG); $this->register(Egg::class, ['Egg', 'minecraft:egg'], EntityLegacyIds::EGG);
self::register(EnderPearl::class, ['ThrownEnderpearl', 'minecraft:ender_pearl'], EntityLegacyIds::ENDER_PEARL); $this->register(EnderPearl::class, ['ThrownEnderpearl', 'minecraft:ender_pearl'], EntityLegacyIds::ENDER_PEARL);
self::register(ExperienceBottle::class, ['ThrownExpBottle', 'minecraft:xp_bottle'], EntityLegacyIds::XP_BOTTLE); $this->register(ExperienceBottle::class, ['ThrownExpBottle', 'minecraft:xp_bottle'], EntityLegacyIds::XP_BOTTLE);
self::register(ExperienceOrb::class, ['XPOrb', 'minecraft:xp_orb'], EntityLegacyIds::XP_ORB); $this->register(ExperienceOrb::class, ['XPOrb', 'minecraft:xp_orb'], EntityLegacyIds::XP_ORB);
self::register(FallingBlock::class, ['FallingSand', 'minecraft:falling_block'], EntityLegacyIds::FALLING_BLOCK); $this->register(FallingBlock::class, ['FallingSand', 'minecraft:falling_block'], EntityLegacyIds::FALLING_BLOCK);
self::register(ItemEntity::class, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM); $this->register(ItemEntity::class, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM);
self::register(Painting::class, ['Painting', 'minecraft:painting'], EntityLegacyIds::PAINTING); $this->register(Painting::class, ['Painting', 'minecraft:painting'], EntityLegacyIds::PAINTING);
self::register(PrimedTNT::class, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt'], EntityLegacyIds::TNT); $this->register(PrimedTNT::class, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt'], EntityLegacyIds::TNT);
self::register(Snowball::class, ['Snowball', 'minecraft:snowball'], EntityLegacyIds::SNOWBALL); $this->register(Snowball::class, ['Snowball', 'minecraft:snowball'], EntityLegacyIds::SNOWBALL);
self::register(SplashPotion::class, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION); $this->register(SplashPotion::class, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION);
self::register(Squid::class, ['Squid', 'minecraft:squid'], EntityLegacyIds::SQUID); $this->register(Squid::class, ['Squid', 'minecraft:squid'], EntityLegacyIds::SQUID);
self::register(Villager::class, ['Villager', 'minecraft:villager'], EntityLegacyIds::VILLAGER); $this->register(Villager::class, ['Villager', 'minecraft:villager'], EntityLegacyIds::VILLAGER);
self::register(Zombie::class, ['Zombie', 'minecraft:zombie'], EntityLegacyIds::ZOMBIE); $this->register(Zombie::class, ['Zombie', 'minecraft:zombie'], EntityLegacyIds::ZOMBIE);
self::register(Human::class, ['Human']); $this->register(Human::class, ['Human']);
Attribute::init(); Attribute::init();
PaintingMotive::init(); PaintingMotive::init();
@ -124,10 +120,10 @@ final class EntityFactory{
* *
* @throws \InvalidArgumentException * @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); Utils::testValidInstance($className, Entity::class);
self::$classMapping[$className] = $className; $this->classMapping[$className] = $className;
$shortName = (new \ReflectionClass($className))->getShortName(); $shortName = (new \ReflectionClass($className))->getShortName();
if(!in_array($shortName, $saveNames, true)){ if(!in_array($shortName, $saveNames, true)){
@ -135,13 +131,13 @@ final class EntityFactory{
} }
foreach($saveNames as $name){ foreach($saveNames as $name){
self::$knownEntities[$name] = $className; $this->knownEntities[$name] = $className;
} }
if($legacyMcpeSaveId !== null){ 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 * @throws \InvalidArgumentException
*/ */
public static function override(string $baseClass, string $newClass) : void{ public function override(string $baseClass, string $newClass) : void{
if(!isset(self::$classMapping[$baseClass])){ if(!isset($this->classMapping[$baseClass])){
throw new \InvalidArgumentException("Class $baseClass is not a registered entity"); throw new \InvalidArgumentException("Class $baseClass is not a registered entity");
} }
Utils::testValidInstance($newClass, $baseClass); Utils::testValidInstance($newClass, $baseClass);
self::$classMapping[$baseClass] = $newClass; $this->classMapping[$baseClass] = $newClass;
} }
/** /**
@ -172,8 +168,8 @@ final class EntityFactory{
* @return string[] * @return string[]
* @return class-string<Entity>[] * @return class-string<Entity>[]
*/ */
public static function getKnownTypes() : array{ public function getKnownTypes() : array{
return array_keys(self::$classMapping); return array_keys($this->classMapping);
} }
/** /**
@ -199,9 +195,9 @@ final class EntityFactory{
* *
* @throws \InvalidArgumentException if the class doesn't exist or is not registered * @throws \InvalidArgumentException if the class doesn't exist or is not registered
*/ */
public static function create(string $baseClass, World $world, CompoundTag $nbt, ...$args) : Entity{ public function create(string $baseClass, World $world, CompoundTag $nbt, ...$args) : Entity{
if(isset(self::$classMapping[$baseClass])){ if(isset($this->classMapping[$baseClass])){
$class = self::$classMapping[$baseClass]; $class = $this->classMapping[$baseClass];
assert(is_a($class, $baseClass, true)); assert(is_a($class, $baseClass, true));
/** /**
* @var Entity $entity * @var Entity $entity
@ -222,18 +218,18 @@ final class EntityFactory{
* @throws \RuntimeException * @throws \RuntimeException
* @internal * @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"); $saveId = $nbt->getTag("id") ?? $nbt->getTag("identifier");
$baseClass = null; $baseClass = null;
if($saveId instanceof StringTag){ if($saveId instanceof StringTag){
$baseClass = self::$knownEntities[$saveId->getValue()] ?? null; $baseClass = $this->knownEntities[$saveId->getValue()] ?? null;
}elseif($saveId instanceof IntTag){ //legacy MCPE format }elseif($saveId instanceof IntTag){ //legacy MCPE format
$baseClass = self::$knownEntities[$saveId->getValue() & 0xff] ?? null; $baseClass = $this->knownEntities[$saveId->getValue() & 0xff] ?? null;
} }
if($baseClass === null){ if($baseClass === null){
return null; return null;
} }
$class = self::$classMapping[$baseClass]; $class = $this->classMapping[$baseClass];
assert(is_a($class, $baseClass, true)); assert(is_a($class, $baseClass, true));
/** /**
* @var Entity $entity * @var Entity $entity
@ -247,9 +243,9 @@ final class EntityFactory{
/** /**
* @phpstan-param class-string<Entity> $class * @phpstan-param class-string<Entity> $class
*/ */
public static function getSaveId(string $class) : string{ public function getSaveId(string $class) : string{
if(isset(self::$saveNames[$class])){ if(isset($this->saveNames[$class])){
return reset(self::$saveNames[$class]); return reset($this->saveNames[$class]);
} }
throw new \InvalidArgumentException("Entity $class is not registered"); throw new \InvalidArgumentException("Entity $class is not registered");
} }

View File

@ -64,7 +64,7 @@ class Bow extends Tool{
$baseForce = min((($p ** 2) + $p * 2) / 3, 1); $baseForce = min((($p ** 2) + $p * 2) / 3, 1);
/** @var ArrowEntity $entity */ /** @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()); $infinity = $this->hasEnchantment(Enchantment::INFINITY());
if($infinity){ if($infinity){

View File

@ -257,7 +257,7 @@ class ItemFactory{
$this->register(new SplashPotion(ItemIds::SPLASH_POTION, $type, "Splash Potion")); $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 */ /** @var Living|string $className */
if(is_a($className, Living::class, true) and $className::NETWORK_ID !== -1){ 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)); $this->register(new SpawnEgg(ItemIds::SPAWN_EGG, $className::NETWORK_ID, "Spawn Egg", $className));

View File

@ -94,7 +94,7 @@ class PaintingItem extends Item{
$nbt->setInt("TileZ", $clickedPos->getFloorZ()); $nbt->setInt("TileZ", $clickedPos->getFloorZ());
/** @var Painting $entity */ /** @var Painting $entity */
$entity = EntityFactory::create(Painting::class, $replacePos->getWorldNonNull(), $nbt); $entity = EntityFactory::getInstance()->create(Painting::class, $replacePos->getWorldNonNull(), $nbt);
$this->pop(); $this->pop();
$entity->spawnToAll(); $entity->spawnToAll();

View File

@ -60,7 +60,7 @@ abstract class ProjectileItem extends Item{
Utils::testValidInstance($class, Throwable::class); Utils::testValidInstance($class, Throwable::class);
/** @var Throwable $projectile */ /** @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())); $projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce()));
$projectileEv = new ProjectileLaunchEvent($projectile); $projectileEv = new ProjectileLaunchEvent($projectile);

View File

@ -58,7 +58,7 @@ class SpawnEgg extends Item{
$nbt->setString("CustomName", $this->getCustomName()); $nbt->setString("CustomName", $this->getCustomName());
} }
$entity = EntityFactory::create($this->entityClass, $player->getWorld(), $nbt); $entity = EntityFactory::getInstance()->create($this->entityClass, $player->getWorld(), $nbt);
$this->pop(); $this->pop();
$entity->spawnToAll(); $entity->spawnToAll();
//TODO: what if the entity was marked for deletion? //TODO: what if the entity was marked for deletion?

View File

@ -1397,7 +1397,7 @@ class World implements ChunkManager{
$nbt->setTag("Item", $item->nbtSerialize()); $nbt->setTag("Item", $item->nbtSerialize());
/** @var ItemEntity $itemEntity */ /** @var ItemEntity $itemEntity */
$itemEntity = EntityFactory::create(ItemEntity::class, $this, $nbt); $itemEntity = EntityFactory::getInstance()->create(ItemEntity::class, $this, $nbt);
$itemEntity->spawnToAll(); $itemEntity->spawnToAll();
return $itemEntity; return $itemEntity;
@ -1422,7 +1422,7 @@ class World implements ChunkManager{
$nbt->setShort(ExperienceOrb::TAG_VALUE_PC, $split); $nbt->setShort(ExperienceOrb::TAG_VALUE_PC, $split);
/** @var ExperienceOrb $orb */ /** @var ExperienceOrb $orb */
$orb = EntityFactory::create(ExperienceOrb::class, $this, $nbt); $orb = EntityFactory::getInstance()->create(ExperienceOrb::class, $this, $nbt);
$orb->spawnToAll(); $orb->spawnToAll();
$orbs[] = $orb; $orbs[] = $orb;
} }

View File

@ -507,9 +507,10 @@ class Chunk{
if($this->NBTentities !== null){ if($this->NBTentities !== null){
$this->dirtyFlags |= self::DIRTY_FLAG_ENTITIES; $this->dirtyFlags |= self::DIRTY_FLAG_ENTITIES;
$world->timings->syncChunkLoadEntitiesTimer->startTiming(); $world->timings->syncChunkLoadEntitiesTimer->startTiming();
$entityFactory = EntityFactory::getInstance();
foreach($this->NBTentities as $nbt){ foreach($this->NBTentities as $nbt){
try{ try{
$entity = EntityFactory::createFromData($world, $nbt); $entity = $entityFactory->createFromData($world, $nbt);
if(!($entity instanceof Entity)){ if(!($entity instanceof Entity)){
$saveIdTag = $nbt->getTag("id") ?? $nbt->getTag("identifier"); $saveIdTag = $nbt->getTag("id") ?? $nbt->getTag("identifier");
$saveId = "<unknown>"; $saveId = "<unknown>";