EntityFactory now exclusively handles loading data from disk

this commit removes the ability to replace centrally registered entity classes in favour of using constructors directly.
In future commits I may introduce a dedicated factory interface which allows an _actual_ factory pattern (e.g. factory->createArrow(world, pos, shooter, isCritical) with proper static analysability) but for now it's peripheral to my intended objective.
The purpose of this change is to facilitate untangling of NBT from entity constructors so that they can be properly created without using NBT at all, and instead use nice APIs.

Spawn eggs now support arbitrary entity creation functions like EntityFactory does, allowing much more flexibility in what can be passed to an entity's constructor (e.g. a Plugin reference can be injected by use()ing it in a closure or via traditional DI.
This commit is contained in:
Dylan K. Taylor
2020-06-19 00:40:44 +01:00
parent 72a7fc68c1
commit 6a26c0bebf
15 changed files with 68 additions and 154 deletions

View File

@@ -62,8 +62,7 @@ class Bow extends Tool{
$p = $diff / 20;
$baseForce = min((($p ** 2) + $p * 2) / 3, 1);
/** @var ArrowEntity $entity */
$entity = EntityFactory::getInstance()->create(ArrowEntity::class, $location->getWorldNonNull(), $nbt, $player, $baseForce >= 1);
$entity = new ArrowEntity($location->getWorldNonNull(), $nbt, $player, $baseForce >= 1);
$infinity = $this->hasEnchantment(Enchantment::INFINITY());
if($infinity){

View File

@@ -36,15 +36,12 @@ class Egg extends ProjectileItem{
return 16;
}
protected function createEntity(EntityFactory $factory, Location $location, Vector3 $velocity, Player $thrower) : Throwable{
/** @var EggEntity $projectile */
$projectile = $factory->create(
EggEntity::class,
protected function createEntity(Location $location, Vector3 $velocity, Player $thrower) : Throwable{
return new EggEntity(
$location->getWorldNonNull(),
EntityFactory::createBaseNBT($location, $velocity, $location->yaw, $location->pitch),
$thrower
);
return $projectile;
}
public function getThrowForce() : float{

View File

@@ -36,15 +36,12 @@ class EnderPearl extends ProjectileItem{
return 16;
}
protected function createEntity(EntityFactory $factory, Location $location, Vector3 $velocity, Player $thrower) : Throwable{
/** @var EnderPearlEntity $projectile */
$projectile = $factory->create(
EnderPearlEntity::class,
protected function createEntity(Location $location, Vector3 $velocity, Player $thrower) : Throwable{
return new EnderPearlEntity(
$location->getWorldNonNull(),
EntityFactory::createBaseNBT($location, $velocity, $location->yaw, $location->pitch),
$thrower
);
return $projectile;
}
public function getThrowForce() : float{

View File

@@ -32,15 +32,12 @@ use pocketmine\player\Player;
class ExperienceBottle extends ProjectileItem{
protected function createEntity(EntityFactory $factory, Location $location, Vector3 $velocity, Player $thrower) : Throwable{
/** @var ExperienceBottleEntity $projectile */
$projectile = $factory->create(
ExperienceBottleEntity::class,
protected function createEntity(Location $location, Vector3 $velocity, Player $thrower) : Throwable{
return new ExperienceBottleEntity(
$location->getWorldNonNull(),
EntityFactory::createBaseNBT($location, $velocity, $location->yaw, $location->pitch),
$thrower
);
return $projectile;
}
public function getThrowForce() : float{

View File

@@ -29,12 +29,17 @@ use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\SkullType;
use pocketmine\block\utils\TreeType;
use pocketmine\block\VanillaBlocks;
use pocketmine\entity\Entity;
use pocketmine\entity\EntityFactory;
use pocketmine\entity\Living;
use pocketmine\entity\Squid;
use pocketmine\entity\Villager;
use pocketmine\entity\Zombie;
use pocketmine\inventory\ArmorInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds;
use pocketmine\utils\SingletonTrait;
use function is_a;
use pocketmine\world\World;
/**
* Manages Item instance creation and registration
@@ -50,6 +55,7 @@ class ItemFactory{
public function __construct(){
$this->registerArmorItems();
$this->registerSpawnEggs();
$this->registerTierToolItems();
$this->register(new Apple(ItemIds::APPLE, 0, "Apple"));
@@ -250,13 +256,6 @@ class ItemFactory{
$this->register(new SplashPotion(ItemIds::SPLASH_POTION, $type, "Splash Potion"));
}
foreach(EntityFactory::getInstance()->getKnownTypes() as $className){
/** @var Living|string $className */
if(is_a($className, Living::class, true) and $className::getNetworkTypeId() !== -1){
$this->register(new SpawnEgg(ItemIds::SPAWN_EGG, $className::getNetworkTypeId(), "Spawn Egg", $className));
}
}
foreach(TreeType::getAll() as $type){
$this->register(new Boat(ItemIds::BOAT, $type->getMagicNumber(), $type->getDisplayName() . " Boat", $type));
}
@@ -316,6 +315,25 @@ class ItemFactory{
//endregion
}
private function registerSpawnEggs() : void{
//TODO: the meta values should probably be hardcoded; they won't change, but the EntityLegacyIds might
$this->register(new class(ItemIds::SPAWN_EGG, EntityLegacyIds::ZOMBIE, "Zombie Spawn Egg") extends SpawnEgg{
protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Zombie($world, EntityFactory::createBaseNBT($pos, null, $yaw, $pitch));
}
});
$this->register(new class(ItemIds::SPAWN_EGG, EntityLegacyIds::SQUID, "Squid Spawn Egg") extends SpawnEgg{
public function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Squid($world, EntityFactory::createBaseNBT($pos, null, $yaw, $pitch));
}
});
$this->register(new class(ItemIds::SPAWN_EGG, EntityLegacyIds::VILLAGER, "Villager Spawn Egg") extends SpawnEgg{
public function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity{
return new Villager($world, EntityFactory::createBaseNBT($pos, null, $yaw, $pitch));
}
});
}
private function registerTierToolItems() : void{
$this->register(new Axe(ItemIds::DIAMOND_AXE, "Diamond Axe", ToolTier::DIAMOND()));
$this->register(new Axe(ItemIds::GOLDEN_AXE, "Golden Axe", ToolTier::GOLD()));

View File

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

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\EntityFactory;
use pocketmine\entity\Location;
use pocketmine\entity\projectile\Throwable;
use pocketmine\event\entity\ProjectileLaunchEvent;
@@ -35,12 +34,12 @@ abstract class ProjectileItem extends Item{
abstract public function getThrowForce() : float;
abstract protected function createEntity(EntityFactory $factory, Location $location, Vector3 $velocity, Player $thrower) : Throwable;
abstract protected function createEntity(Location $location, Vector3 $velocity, Player $thrower) : Throwable;
public function onClickAir(Player $player, Vector3 $directionVector) : ItemUseResult{
$location = $player->getLocation();
$projectile = $this->createEntity(EntityFactory::getInstance(), Location::fromObject($player->getEyePos(), $player->getWorld(), $location->yaw, $location->pitch), $directionVector, $player);
$projectile = $this->createEntity(Location::fromObject($player->getEyePos(), $player->getWorld(), $location->yaw, $location->pitch), $directionVector, $player);
$projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce()));
$projectileEv = new ProjectileLaunchEvent($projectile);

View File

@@ -36,15 +36,12 @@ class Snowball extends ProjectileItem{
return 16;
}
protected function createEntity(EntityFactory $factory, Location $location, Vector3 $velocity, Player $thrower) : Throwable{
/** @var SnowballEntity $projectile */
$projectile = $factory->create(
SnowballEntity::class,
protected function createEntity(Location $location, Vector3 $velocity, Player $thrower) : Throwable{
return new SnowballEntity(
$location->getWorldNonNull(),
EntityFactory::createBaseNBT($location, $velocity, $location->yaw, $location->pitch),
$thrower
);
return $projectile;
}
public function getThrowForce() : float{

View File

@@ -29,36 +29,19 @@ use pocketmine\entity\EntityFactory;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\utils\Utils;
use pocketmine\world\World;
use function lcg_value;
class SpawnEgg extends Item{
abstract class SpawnEgg extends Item{
/**
* @var string
* @phpstan-var class-string<Entity>
*/
private $entityClass;
/**
* @param string $entityClass instanceof Entity
* @phpstan-param class-string<Entity> $entityClass
*
* @throws \InvalidArgumentException
*/
public function __construct(int $id, int $variant, string $name, string $entityClass){
parent::__construct($id, $variant, $name);
Utils::testValidInstance($entityClass, Entity::class);
$this->entityClass = $entityClass;
}
abstract protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity;
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{
$nbt = EntityFactory::createBaseNBT($blockReplace->getPos()->add(0.5, 0, 0.5), null, lcg_value() * 360, 0);
$entity = $this->createEntity($player->getWorld(), $blockReplace->getPos()->add(0.5, 0, 0.5), lcg_value() * 360, 0);
if($this->hasCustomName()){
$nbt->setString("CustomName", $this->getCustomName());
$entity->setNameTag($this->getCustomName());
}
$entity = EntityFactory::getInstance()->create($this->entityClass, $player->getWorld(), $nbt);
$this->pop();
$entity->spawnToAll();
//TODO: what if the entity was marked for deletion?

View File

@@ -36,10 +36,8 @@ class SplashPotion extends ProjectileItem{
return 1;
}
protected function createEntity(EntityFactory $factory, Location $location, Vector3 $velocity, Player $thrower) : Throwable{
/** @var SplashPotionEntity $projectile */
$projectile = $factory->create(
SplashPotionEntity::class,
protected function createEntity(Location $location, Vector3 $velocity, Player $thrower) : Throwable{
$projectile = new SplashPotionEntity(
$location->getWorldNonNull(),
EntityFactory::createBaseNBT($location, $velocity, $location->yaw, $location->pitch),
$thrower