mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-24 20:34:02 +00:00
Introduce EntityFactory
This contains all of the static stuff that was previously embedded in the Entity static root. This solves a bunch of problems like circular dependencies between parent and child classes, encapsulating logic and reducing the size of the enormous Entity.php.
This commit is contained in:
parent
b1cef8509a
commit
7d827a1c65
@ -34,6 +34,7 @@ use pocketmine\command\ConsoleCommandSender;
|
||||
use pocketmine\command\PluginIdentifiableCommand;
|
||||
use pocketmine\command\SimpleCommandMap;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\event\HandlerList;
|
||||
use pocketmine\event\level\LevelInitEvent;
|
||||
@ -1695,7 +1696,7 @@ class Server{
|
||||
|
||||
$this->commandMap = new SimpleCommandMap($this);
|
||||
|
||||
Entity::init();
|
||||
EntityFactory::init();
|
||||
Tile::init();
|
||||
BlockFactory::init();
|
||||
BlockFactory::registerStaticRuntimeIdMappings();
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
@ -34,12 +34,12 @@ abstract class Fallable extends Solid{
|
||||
if($down->getId() === self::AIR or $down instanceof Liquid or $down instanceof Fire){
|
||||
$this->level->setBlock($this, BlockFactory::get(Block::AIR));
|
||||
|
||||
$nbt = Entity::createBaseNBT($this->add(0.5, 0, 0.5));
|
||||
$nbt = EntityFactory::createBaseNBT($this->add(0.5, 0, 0.5));
|
||||
$nbt->setInt("TileID", $this->getId());
|
||||
$nbt->setByte("Data", $this->getDamage());
|
||||
|
||||
/** @var FallingBlock $fall */
|
||||
$fall = Entity::create(FallingBlock::class, $this->getLevel(), $nbt);
|
||||
$fall = EntityFactory::create(FallingBlock::class, $this->getLevel(), $nbt);
|
||||
$fall->spawnToAll();
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\object\PrimedTNT;
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\item\FlintSteel;
|
||||
@ -75,11 +76,11 @@ class TNT extends Solid{
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR));
|
||||
|
||||
$mot = (new Random())->nextSignedFloat() * M_PI * 2;
|
||||
$nbt = Entity::createBaseNBT($this->add(0.5, 0, 0.5), new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02));
|
||||
$nbt = EntityFactory::createBaseNBT($this->add(0.5, 0, 0.5), new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02));
|
||||
$nbt->setShort("Fuse", $fuse);
|
||||
|
||||
/** @var PrimedTNT $tnt */
|
||||
$tnt = Entity::create(PrimedTNT::class, $this->getLevel(), $nbt);
|
||||
$tnt = EntityFactory::create(PrimedTNT::class, $this->getLevel(), $nbt);
|
||||
$tnt->spawnToAll();
|
||||
}
|
||||
|
||||
|
@ -28,18 +28,6 @@ namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Water;
|
||||
use pocketmine\entity\object\ExperienceOrb;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
use pocketmine\entity\object\ItemEntity;
|
||||
use pocketmine\entity\object\Painting;
|
||||
use pocketmine\entity\object\PaintingMotive;
|
||||
use pocketmine\entity\object\PrimedTNT;
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\entity\projectile\Egg;
|
||||
use pocketmine\entity\projectile\EnderPearl;
|
||||
use pocketmine\entity\projectile\ExperienceBottle;
|
||||
use pocketmine\entity\projectile\Snowball;
|
||||
use pocketmine\entity\projectile\SplashPotion;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityDespawnEvent;
|
||||
use pocketmine\event\entity\EntityLevelChangeEvent;
|
||||
@ -61,7 +49,6 @@ 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;
|
||||
@ -76,23 +63,17 @@ use pocketmine\plugin\Plugin;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\Utils;
|
||||
use function abs;
|
||||
use function array_keys;
|
||||
use function assert;
|
||||
use function cos;
|
||||
use function count;
|
||||
use function current;
|
||||
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;
|
||||
use function lcg_value;
|
||||
use function reset;
|
||||
use function sin;
|
||||
use const M_PI_2;
|
||||
|
||||
@ -279,194 +260,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_FLAG_OVER_SCAFFOLDING = 69;
|
||||
public const DATA_FLAG_FALL_THROUGH_SCAFFOLDING = 70;
|
||||
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
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.
|
||||
*/
|
||||
public static function init() : void{
|
||||
//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, 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']);
|
||||
|
||||
self::register(Human::class, true);
|
||||
|
||||
Attribute::init();
|
||||
Effect::init();
|
||||
PaintingMotive::init();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an entity with the specified type, level and NBT, with optional additional arguments to pass to the
|
||||
* entity's constructor.
|
||||
*
|
||||
* TODO: make this NBT-independent
|
||||
*
|
||||
* @param string $baseClass
|
||||
* @param Level $level
|
||||
* @param CompoundTag $nbt
|
||||
* @param mixed ...$args
|
||||
*
|
||||
* @return Entity instanceof $baseClass
|
||||
* @throws \InvalidArgumentException if the class doesn't exist or is not registered
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an entity type into the index.
|
||||
*
|
||||
* @param string $className Class that extends Entity
|
||||
* @param bool $force Force registration even if the entity does not have a valid network ID
|
||||
* @param string[] $saveNames An array of save names which this entity might be saved under. Defaults to the short name of the class itself if empty.
|
||||
*
|
||||
* NOTE: The first save name in the $saveNames array will be used when saving the entity to disk. The reflection
|
||||
* name of the class will be appended to the end and only used if no other save names are specified.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function register(string $className, bool $force = false, array $saveNames = []) : void{
|
||||
Utils::testValidInstance($className, Entity::class);
|
||||
|
||||
/** @var Entity $className */
|
||||
if($className::NETWORK_ID !== -1){
|
||||
self::$knownEntities[$className::NETWORK_ID] = $className;
|
||||
}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)){
|
||||
$saveNames[] = $shortName;
|
||||
}
|
||||
|
||||
foreach($saveNames as $name){
|
||||
self::$knownEntities[$name] = $className;
|
||||
}
|
||||
|
||||
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 getKnownTypes() : array{
|
||||
return array_keys(self::$classMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which creates minimal NBT needed to spawn an entity.
|
||||
*
|
||||
* @param Vector3 $pos
|
||||
* @param Vector3|null $motion
|
||||
* @param float $yaw
|
||||
* @param float $pitch
|
||||
*
|
||||
* @return CompoundTag
|
||||
*/
|
||||
public static function createBaseNBT(Vector3 $pos, ?Vector3 $motion = null, float $yaw = 0.0, float $pitch = 0.0) : CompoundTag{
|
||||
return new CompoundTag("", [
|
||||
new ListTag("Pos", [
|
||||
new DoubleTag("", $pos->x),
|
||||
new DoubleTag("", $pos->y),
|
||||
new DoubleTag("", $pos->z)
|
||||
]),
|
||||
new ListTag("Motion", [
|
||||
new DoubleTag("", $motion ? $motion->x : 0.0),
|
||||
new DoubleTag("", $motion ? $motion->y : 0.0),
|
||||
new DoubleTag("", $motion ? $motion->z : 0.0)
|
||||
]),
|
||||
new ListTag("Rotation", [
|
||||
new FloatTag("", $yaw),
|
||||
new FloatTag("", $pitch)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Player[]
|
||||
@ -591,7 +384,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->eyeHeight = $this->height / 2 + 0.1;
|
||||
}
|
||||
|
||||
$this->id = Entity::$entityCount++;
|
||||
$this->id = EntityFactory::nextRuntimeId();
|
||||
$this->server = $level->getServer();
|
||||
|
||||
/** @var float[] $pos */
|
||||
@ -924,23 +717,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->savedWithChunk = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the short save name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSaveId() : string{
|
||||
if(!isset(self::$saveNames[static::class])){
|
||||
throw new \InvalidStateException("Entity is not registered");
|
||||
}
|
||||
reset(self::$saveNames[static::class]);
|
||||
return current(self::$saveNames[static::class]);
|
||||
}
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = new CompoundTag();
|
||||
if(!($this instanceof Player)){
|
||||
$nbt->setString("id", $this->getSaveId());
|
||||
$nbt->setString("id", EntityFactory::getSaveId(get_class($this)));
|
||||
|
||||
if($this->getNameTag() !== ""){
|
||||
$nbt->setString("CustomName", $this->getNameTag());
|
||||
|
265
src/pocketmine/entity/EntityFactory.php
Normal file
265
src/pocketmine/entity/EntityFactory.php
Normal file
@ -0,0 +1,265 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\entity\object\ExperienceOrb;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
use pocketmine\entity\object\ItemEntity;
|
||||
use pocketmine\entity\object\Painting;
|
||||
use pocketmine\entity\object\PaintingMotive;
|
||||
use pocketmine\entity\object\PrimedTNT;
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\entity\projectile\Egg;
|
||||
use pocketmine\entity\projectile\EnderPearl;
|
||||
use pocketmine\entity\projectile\ExperienceBottle;
|
||||
use pocketmine\entity\projectile\Snowball;
|
||||
use pocketmine\entity\projectile\SplashPotion;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3;
|
||||
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\utils\Utils;
|
||||
use function array_keys;
|
||||
use function assert;
|
||||
use function in_array;
|
||||
use function is_a;
|
||||
use function reset;
|
||||
|
||||
final class EntityFactory{
|
||||
|
||||
private static $entityCount = 1;
|
||||
/** @var string[] base class => currently used class for construction */
|
||||
private static $classMapping = [];
|
||||
/** @var Entity[] */
|
||||
private static $knownEntities = [];
|
||||
/** @var string[][] */
|
||||
private static $saveNames = [];
|
||||
|
||||
private 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
|
||||
//TODO: index them by version to allow proper multi-save compatibility
|
||||
|
||||
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']);
|
||||
|
||||
self::register(Human::class, true);
|
||||
|
||||
Attribute::init();
|
||||
Effect::init();
|
||||
PaintingMotive::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an entity type into the index.
|
||||
*
|
||||
* @param string $className Class that extends Entity
|
||||
* @param bool $force Force registration even if the entity does not have a valid network ID
|
||||
* @param string[] $saveNames An array of save names which this entity might be saved under. Defaults to the short name of the class itself if empty.
|
||||
*
|
||||
* NOTE: The first save name in the $saveNames array will be used when saving the entity to disk. The reflection
|
||||
* name of the class will be appended to the end and only used if no other save names are specified.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function register(string $className, bool $force = false, array $saveNames = []) : void{
|
||||
Utils::testValidInstance($className, Entity::class);
|
||||
|
||||
/** @var Entity $className */
|
||||
if($className::NETWORK_ID !== -1){
|
||||
self::$knownEntities[$className::NETWORK_ID] = $className;
|
||||
}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)){
|
||||
$saveNames[] = $shortName;
|
||||
}
|
||||
|
||||
foreach($saveNames as $name){
|
||||
self::$knownEntities[$name] = $className;
|
||||
}
|
||||
|
||||
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 getKnownTypes() : array{
|
||||
return array_keys(self::$classMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new runtime entity ID for a new entity.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function nextRuntimeId() : int{
|
||||
return self::$entityCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity with the specified type, level and NBT, with optional additional arguments to pass to the
|
||||
* entity's constructor.
|
||||
*
|
||||
* TODO: make this NBT-independent
|
||||
*
|
||||
* @param string $baseClass
|
||||
* @param Level $level
|
||||
* @param CompoundTag $nbt
|
||||
* @param mixed ...$args
|
||||
*
|
||||
* @return Entity instanceof $baseClass
|
||||
* @throws \InvalidArgumentException if the class doesn't exist or is not registered
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static function getSaveId(string $class) : string{
|
||||
if(isset(self::$saveNames[$class])){
|
||||
return reset(self::$saveNames[$class]);
|
||||
}
|
||||
throw new \InvalidArgumentException("Entity $class is not registered");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function which creates minimal NBT needed to spawn an entity.
|
||||
*
|
||||
* @param Vector3 $pos
|
||||
* @param Vector3|null $motion
|
||||
* @param float $yaw
|
||||
* @param float $pitch
|
||||
*
|
||||
* @return CompoundTag
|
||||
*/
|
||||
public static function createBaseNBT(Vector3 $pos, ?Vector3 $motion = null, float $yaw = 0.0, float $pitch = 0.0) : CompoundTag{
|
||||
return new CompoundTag("", [
|
||||
new ListTag("Pos", [
|
||||
new DoubleTag("", $pos->x),
|
||||
new DoubleTag("", $pos->y),
|
||||
new DoubleTag("", $pos->z)
|
||||
]),
|
||||
new ListTag("Motion", [
|
||||
new DoubleTag("", $motion ? $motion->x : 0.0),
|
||||
new DoubleTag("", $motion ? $motion->y : 0.0),
|
||||
new DoubleTag("", $motion ? $motion->z : 0.0)
|
||||
]),
|
||||
new ListTag("Rotation", [
|
||||
new FloatTag("", $yaw),
|
||||
new FloatTag("", $pitch)
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\projectile\Arrow as ArrowEntity;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\event\entity\EntityShootBowEvent;
|
||||
@ -53,7 +53,7 @@ class Bow extends Tool{
|
||||
return false;
|
||||
}
|
||||
|
||||
$nbt = Entity::createBaseNBT(
|
||||
$nbt = EntityFactory::createBaseNBT(
|
||||
$player->add(0, $player->getEyeHeight(), 0),
|
||||
$player->getDirectionVector(),
|
||||
($player->yaw > 180 ? 360 : 0) - $player->yaw,
|
||||
@ -66,7 +66,7 @@ class Bow extends Tool{
|
||||
$force = min((($p ** 2) + $p * 2) / 3, 1) * 2;
|
||||
|
||||
/** @var ArrowEntity $entity */
|
||||
$entity = Entity::create(ArrowEntity::class, $player->getLevel(), $nbt, $player, $force == 2);
|
||||
$entity = EntityFactory::create(ArrowEntity::class, $player->getLevel(), $nbt, $player, $force == 2);
|
||||
|
||||
$infinity = $this->hasEnchantment(Enchantment::INFINITY);
|
||||
if($infinity){
|
||||
|
@ -25,7 +25,7 @@ namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\tile\Skull;
|
||||
@ -194,7 +194,7 @@ class ItemFactory{
|
||||
//TODO: ENDER_EYE
|
||||
self::registerItem(new Item(Item::GLISTERING_MELON, 0, "Glistering Melon"));
|
||||
|
||||
foreach(Entity::getKnownTypes() as $className){
|
||||
foreach(EntityFactory::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, $className, "Spawn Egg"));
|
||||
|
@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\object\Painting;
|
||||
use pocketmine\entity\object\PaintingMotive;
|
||||
use pocketmine\math\Facing;
|
||||
@ -86,7 +86,7 @@ class PaintingItem extends Item{
|
||||
return false;
|
||||
}
|
||||
|
||||
$nbt = Entity::createBaseNBT($blockReplace, null, $direction * 90, 0);
|
||||
$nbt = EntityFactory::createBaseNBT($blockReplace, null, $direction * 90, 0);
|
||||
$nbt->setByte("Direction", $direction);
|
||||
$nbt->setString("Motive", $motive->getName());
|
||||
$nbt->setInt("TileX", $blockClicked->getFloorX());
|
||||
@ -94,7 +94,7 @@ class PaintingItem extends Item{
|
||||
$nbt->setInt("TileZ", $blockClicked->getFloorZ());
|
||||
|
||||
/** @var Painting $entity */
|
||||
$entity = Entity::create(Painting::class, $blockReplace->getLevel(), $nbt);
|
||||
$entity = EntityFactory::create(Painting::class, $blockReplace->getLevel(), $nbt);
|
||||
$this->pop();
|
||||
$entity->spawnToAll();
|
||||
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\EntityIds;
|
||||
use pocketmine\entity\projectile\Throwable;
|
||||
use pocketmine\event\entity\ProjectileLaunchEvent;
|
||||
@ -54,14 +54,14 @@ abstract class ProjectileItem extends Item{
|
||||
}
|
||||
|
||||
public function onClickAir(Player $player, Vector3 $directionVector) : bool{
|
||||
$nbt = Entity::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch);
|
||||
$nbt = EntityFactory::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch);
|
||||
$this->addExtraTags($nbt);
|
||||
|
||||
$class = $this->getProjectileEntityClass();
|
||||
Utils::testValidInstance($class, Throwable::class);
|
||||
|
||||
/** @var Throwable $projectile */
|
||||
$projectile = Entity::create($class, $player->getLevel(), $nbt, $player);
|
||||
$projectile = EntityFactory::create($class, $player->getLevel(), $nbt, $player);
|
||||
$projectile->setMotion($projectile->getMotion()->multiply($this->getThrowForce()));
|
||||
|
||||
$projectileEv = new ProjectileLaunchEvent($projectile);
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
@ -50,13 +51,13 @@ class SpawnEgg extends Item{
|
||||
}
|
||||
|
||||
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);
|
||||
$nbt = EntityFactory::createBaseNBT($blockReplace->add(0.5, 0, 0.5), null, lcg_value() * 360, 0);
|
||||
|
||||
if($this->hasCustomName()){
|
||||
$nbt->setString("CustomName", $this->getCustomName());
|
||||
}
|
||||
|
||||
$entity = Entity::create($this->entityClass, $player->getLevel(), $nbt);
|
||||
$entity = EntityFactory::create($this->entityClass, $player->getLevel(), $nbt);
|
||||
$this->pop();
|
||||
$entity->spawnToAll();
|
||||
return true;
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\block\UnknownBlock;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\object\ExperienceOrb;
|
||||
use pocketmine\entity\object\ItemEntity;
|
||||
use pocketmine\event\block\BlockBreakEvent;
|
||||
@ -1630,13 +1631,13 @@ class Level implements ChunkManager, Metadatable{
|
||||
$itemTag->setName("Item");
|
||||
|
||||
if(!$item->isNull()){
|
||||
$nbt = Entity::createBaseNBT($source, $motion, lcg_value() * 360, 0);
|
||||
$nbt = EntityFactory::createBaseNBT($source, $motion, lcg_value() * 360, 0);
|
||||
$nbt->setShort("Health", 5);
|
||||
$nbt->setShort("PickupDelay", $delay);
|
||||
$nbt->setTag($itemTag);
|
||||
|
||||
/** @var ItemEntity $itemEntity */
|
||||
$itemEntity = Entity::create(ItemEntity::class, $this, $nbt);
|
||||
$itemEntity = EntityFactory::create(ItemEntity::class, $this, $nbt);
|
||||
$itemEntity->spawnToAll();
|
||||
return $itemEntity;
|
||||
}
|
||||
@ -1656,7 +1657,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$orbs = [];
|
||||
|
||||
foreach(ExperienceOrb::splitIntoOrbSizes($amount) as $split){
|
||||
$nbt = Entity::createBaseNBT(
|
||||
$nbt = EntityFactory::createBaseNBT(
|
||||
$pos,
|
||||
$this->temporalVector->setComponents((lcg_value() * 0.2 - 0.1) * 2, lcg_value() * 0.4, (lcg_value() * 0.2 - 0.1) * 2),
|
||||
lcg_value() * 360,
|
||||
@ -1665,7 +1666,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$nbt->setShort(ExperienceOrb::TAG_VALUE_PC, $split);
|
||||
|
||||
/** @var ExperienceOrb $orb */
|
||||
$orb = Entity::create(ExperienceOrb::class, $this, $nbt);
|
||||
$orb = EntityFactory::create(ExperienceOrb::class, $this, $nbt);
|
||||
$orb->spawnToAll();
|
||||
$orbs[] = $orb;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ namespace pocketmine\level\format;
|
||||
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\Player;
|
||||
@ -601,7 +602,7 @@ class Chunk{
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof CompoundTag){
|
||||
try{
|
||||
$entity = Entity::createFromData($level, $nbt);
|
||||
$entity = EntityFactory::createFromData($level, $nbt);
|
||||
if(!($entity instanceof Entity)){
|
||||
$changed = true;
|
||||
continue;
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\particle;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityFactory;
|
||||
use pocketmine\entity\Skin;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
@ -80,7 +81,7 @@ class FloatingTextParticle extends Particle{
|
||||
$p = [];
|
||||
|
||||
if($this->entityId === null){
|
||||
$this->entityId = Entity::$entityCount++;
|
||||
$this->entityId = EntityFactory::nextRuntimeId();
|
||||
}else{
|
||||
$pk0 = new RemoveEntityPacket();
|
||||
$pk0->entityUniqueId = $this->entityId;
|
||||
|
Loading…
x
Reference in New Issue
Block a user