diff --git a/src/block/TNT.php b/src/block/TNT.php index 19a6b466ef..da57818a84 100644 --- a/src/block/TNT.php +++ b/src/block/TNT.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\entity\Entity; -use pocketmine\entity\EntityFactory; +use pocketmine\entity\Location; use pocketmine\entity\object\PrimedTNT; use pocketmine\entity\projectile\Arrow; use pocketmine\item\Durable; @@ -32,6 +32,7 @@ use pocketmine\item\enchantment\Enchantment; use pocketmine\item\FlintSteel; use pocketmine\item\Item; use pocketmine\math\Vector3; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\utils\Random; use function cos; @@ -93,10 +94,11 @@ class TNT extends Opaque{ $this->pos->getWorldNonNull()->setBlock($this->pos, VanillaBlocks::AIR()); $mot = (new Random())->nextSignedFloat() * M_PI * 2; - $nbt = EntityFactory::createBaseNBT($this->pos->add(0.5, 0, 0.5), new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02)); - $nbt->setShort("Fuse", $fuse); - $tnt = new PrimedTNT($this->pos->getWorldNonNull(), $nbt); + $tnt = new PrimedTNT(Location::fromObject($this->pos->add(0.5, 0, 0.5), $this->pos->getWorldNonNull()), new CompoundTag()); + $tnt->setFuse($fuse); + $tnt->setMotion(new Vector3(-sin($mot) * 0.02, 0.2, -cos($mot) * 0.02)); + $tnt->spawnToAll(); } diff --git a/src/block/utils/FallableTrait.php b/src/block/utils/FallableTrait.php index f17fdcfab2..fe88647672 100644 --- a/src/block/utils/FallableTrait.php +++ b/src/block/utils/FallableTrait.php @@ -23,13 +23,16 @@ declare(strict_types=1); namespace pocketmine\block\utils; +use pocketmine\block\Block; use pocketmine\block\BlockLegacyIds; use pocketmine\block\Fire; use pocketmine\block\Liquid; use pocketmine\block\VanillaBlocks; -use pocketmine\entity\EntityFactory; +use pocketmine\entity\Location; use pocketmine\entity\object\FallingBlock; use pocketmine\math\Facing; +use pocketmine\nbt\tag\CompoundTag; +use pocketmine\utils\AssumptionFailedError; use pocketmine\world\Position; /** @@ -51,11 +54,10 @@ trait FallableTrait{ if($down->getId() === BlockLegacyIds::AIR or $down instanceof Liquid or $down instanceof Fire){ $pos->getWorldNonNull()->setBlock($pos, VanillaBlocks::AIR()); - $nbt = EntityFactory::createBaseNBT($pos->add(0.5, 0, 0.5)); - $nbt->setInt("TileID", $this->getId()); - $nbt->setByte("Data", $this->getMeta()); + $block = $this; + if(!($block instanceof Block)) throw new AssumptionFailedError(__TRAIT__ . " should only be used by Blocks"); - $fall = new FallingBlock($pos->getWorldNonNull(), $nbt); + $fall = new FallingBlock(Location::fromObject($pos->add(0.5, 0, 0.5), $pos->getWorldNonNull()), $block, new CompoundTag()); $fall->spawnToAll(); } } diff --git a/src/entity/Entity.php b/src/entity/Entity.php index 883081bcef..b40977450c 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -213,7 +213,7 @@ abstract class Entity{ /** @var int|null */ protected $targetId = null; - public function __construct(World $world, CompoundTag $nbt){ + public function __construct(Location $location, CompoundTag $nbt){ $this->timings = Timings::getEntityTimings($this); $this->temporalVector = new Vector3(); @@ -223,14 +223,9 @@ abstract class Entity{ } $this->id = EntityFactory::nextRuntimeId(); - $this->server = $world->getServer(); + $this->server = $location->getWorldNonNull()->getServer(); - /** @var float[] $pos */ - $pos = $nbt->getListTag("Pos")->getAllValues(); - /** @var float[] $rotation */ - $rotation = $nbt->getListTag("Rotation")->getAllValues(); - - $this->location = new Location($pos[0], $pos[1], $pos[2], $rotation[0], $rotation[1], $world); + $this->location = $location->asLocation(); assert( !is_nan($this->location->x) and !is_infinite($this->location->x) and !is_nan($this->location->y) and !is_infinite($this->location->y) and diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index fc698b9f8b..d74b206acd 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -26,6 +26,7 @@ namespace pocketmine\entity; use DaveRandom\CallbackValidator\CallbackType; use DaveRandom\CallbackValidator\ParameterType; use DaveRandom\CallbackValidator\ReturnType; +use pocketmine\block\BlockFactory; use pocketmine\entity\object\ExperienceOrb; use pocketmine\entity\object\FallingBlock; use pocketmine\entity\object\ItemEntity; @@ -38,7 +39,11 @@ use pocketmine\entity\projectile\EnderPearl; use pocketmine\entity\projectile\ExperienceBottle; use pocketmine\entity\projectile\Snowball; use pocketmine\entity\projectile\SplashPotion; +use pocketmine\item\Item; +use pocketmine\math\Facing; use pocketmine\math\Vector3; +use pocketmine\nbt\NBT; +use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\DoubleTag; use pocketmine\nbt\tag\FloatTag; @@ -50,6 +55,7 @@ use pocketmine\utils\SingletonTrait; use pocketmine\utils\Utils; use pocketmine\world\World; use function array_keys; +use function count; use function in_array; use function reset; @@ -84,63 +90,85 @@ final class EntityFactory{ //TODO: index them by version to allow proper multi-save compatibility $this->register(Arrow::class, function(World $world, CompoundTag $nbt) : Arrow{ - return new Arrow($world, $nbt); + return new Arrow(self::parseLocation($nbt, $world), null, false, $nbt); //TODO: missing critical flag }, ['Arrow', 'minecraft:arrow'], EntityLegacyIds::ARROW); $this->register(Egg::class, function(World $world, CompoundTag $nbt) : Egg{ - return new Egg($world, $nbt); + return new Egg(self::parseLocation($nbt, $world), null, $nbt); }, ['Egg', 'minecraft:egg'], EntityLegacyIds::EGG); $this->register(EnderPearl::class, function(World $world, CompoundTag $nbt) : EnderPearl{ - return new EnderPearl($world, $nbt); + return new EnderPearl(self::parseLocation($nbt, $world), null, $nbt); }, ['ThrownEnderpearl', 'minecraft:ender_pearl'], EntityLegacyIds::ENDER_PEARL); $this->register(ExperienceBottle::class, function(World $world, CompoundTag $nbt) : ExperienceBottle{ - return new ExperienceBottle($world, $nbt); + return new ExperienceBottle(self::parseLocation($nbt, $world), null, $nbt); }, ['ThrownExpBottle', 'minecraft:xp_bottle'], EntityLegacyIds::XP_BOTTLE); $this->register(ExperienceOrb::class, function(World $world, CompoundTag $nbt) : ExperienceOrb{ - return new ExperienceOrb($world, $nbt); + return new ExperienceOrb(self::parseLocation($nbt, $world), $nbt); }, ['XPOrb', 'minecraft:xp_orb'], EntityLegacyIds::XP_ORB); $this->register(FallingBlock::class, function(World $world, CompoundTag $nbt) : FallingBlock{ - return new FallingBlock($world, $nbt); + return new FallingBlock(self::parseLocation($nbt, $world), FallingBlock::parseBlockNBT(BlockFactory::getInstance(), $nbt), $nbt); }, ['FallingSand', 'minecraft:falling_block'], EntityLegacyIds::FALLING_BLOCK); $this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{ - return new ItemEntity($world, $nbt); + $itemTag = $nbt->getCompoundTag("Item"); + if($itemTag === null){ + throw new \UnexpectedValueException("Expected \"Item\" NBT tag not found"); + } + + $item = Item::nbtDeserialize($itemTag); + if($item->isNull()){ + throw new \UnexpectedValueException("Item is invalid"); + } + return new ItemEntity(self::parseLocation($nbt, $world), $item, $nbt); }, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM); $this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{ - return new Painting($world, $nbt); + $motive = PaintingMotive::getMotiveByName($nbt->getString("Motive")); + if($motive === null){ + throw new \UnexpectedValueException("Unknown painting motive"); + } + $blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ")); + if($nbt->hasTag("Direction", ByteTag::class)){ + $facing = Painting::DATA_TO_FACING[$nbt->getByte("Direction")] ?? Facing::NORTH; + }elseif($nbt->hasTag("Facing", ByteTag::class)){ + $facing = Painting::DATA_TO_FACING[$nbt->getByte("Facing")] ?? Facing::NORTH; + }else{ + throw new \UnexpectedValueException("Missing facing info"); + } + + return new Painting(self::parseLocation($nbt, $world), $blockIn, $facing, $motive, $nbt); }, ['Painting', 'minecraft:painting'], EntityLegacyIds::PAINTING); $this->register(PrimedTNT::class, function(World $world, CompoundTag $nbt) : PrimedTNT{ - return new PrimedTNT($world, $nbt); + return new PrimedTNT(self::parseLocation($nbt, $world), $nbt); }, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt'], EntityLegacyIds::TNT); $this->register(Snowball::class, function(World $world, CompoundTag $nbt) : Snowball{ - return new Snowball($world, $nbt); + return new Snowball(self::parseLocation($nbt, $world), null, $nbt); }, ['Snowball', 'minecraft:snowball'], EntityLegacyIds::SNOWBALL); $this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{ - return new SplashPotion($world, $nbt); + return new SplashPotion(self::parseLocation($nbt, $world), null, $nbt); }, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION); $this->register(Squid::class, function(World $world, CompoundTag $nbt) : Squid{ - return new Squid($world, $nbt); + return new Squid(self::parseLocation($nbt, $world), $nbt); }, ['Squid', 'minecraft:squid'], EntityLegacyIds::SQUID); $this->register(Villager::class, function(World $world, CompoundTag $nbt) : Villager{ - return new Villager($world, $nbt); + return new Villager(self::parseLocation($nbt, $world), $nbt); }, ['Villager', 'minecraft:villager'], EntityLegacyIds::VILLAGER); $this->register(Zombie::class, function(World $world, CompoundTag $nbt) : Zombie{ - return new Zombie($world, $nbt); + return new Zombie(self::parseLocation($nbt, $world), $nbt); }, ['Zombie', 'minecraft:zombie'], EntityLegacyIds::ZOMBIE); $this->register(Human::class, function(World $world, CompoundTag $nbt) : Human{ - return new Human($world, $nbt); + return new Human(self::parseLocation($nbt, $world), Human::parseSkinNBT($nbt), $nbt); }, ['Human']); PaintingMotive::init(); @@ -247,6 +275,37 @@ final class EntityFactory{ throw new \InvalidArgumentException("Entity $class is not registered"); } + public static function parseLocation(CompoundTag $nbt, World $world) : Location{ + $pos = self::parseVec3($nbt, "Pos", false); + + $yawPitch = $nbt->getTag("Rotation"); + if(!($yawPitch instanceof ListTag) or $yawPitch->getTagType() !== NBT::TAG_Float){ + throw new \UnexpectedValueException("'Rotation' should be a List"); + } + $values = $yawPitch->getValue(); + if(count($values) !== 2){ + throw new \UnexpectedValueException("Expected exactly 2 entries for 'Rotation'"); + } + + return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue()); + } + + private static function parseVec3(CompoundTag $nbt, string $tagName, bool $optional) : Vector3{ + $pos = $nbt->getTag($tagName); + if($pos === null and $optional){ + return new Vector3(0, 0, 0); + } + if(!($pos instanceof ListTag) or $pos->getTagType() !== NBT::TAG_Double){ + throw new \UnexpectedValueException("'$tagName' should be a List"); + } + /** @var DoubleTag[] $values */ + $values = $pos->getValue(); + if(count($values) !== 3){ + throw new \UnexpectedValueException("Expected exactly 3 entries in '$tagName' tag"); + } + return new Vector3($values[0]->getValue(), $values[1]->getValue(), $values[2]->getValue()); + } + /** * Helper function which creates minimal NBT needed to spawn an entity. */ diff --git a/src/entity/Human.php b/src/entity/Human.php index 2e5e7756ed..77b37d618f 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -55,7 +55,6 @@ use pocketmine\player\Player; use pocketmine\utils\Limits; use pocketmine\uuid\UUID; use pocketmine\world\sound\TotemUseSound; -use pocketmine\world\World; use function array_filter; use function array_merge; use function array_values; @@ -94,22 +93,27 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ protected $baseOffset = 1.62; - public function __construct(World $world, CompoundTag $nbt){ - if($this->skin === null){ - $skinTag = $nbt->getCompoundTag("Skin"); - if($skinTag === null){ - throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set"); - } - $this->skin = new Skin( //this throws if the skin is invalid - $skinTag->getString("Name"), - $skinTag->hasTag("Data", StringTag::class) ? $skinTag->getString("Data") : $skinTag->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM) - $skinTag->getByteArray("CapeData", ""), - $skinTag->getString("GeometryName", ""), - $skinTag->getByteArray("GeometryData", "") - ); - } + public function __construct(Location $location, Skin $skin, CompoundTag $nbt){ + $this->skin = $skin; + parent::__construct($location, $nbt); + } - parent::__construct($world, $nbt); + /** + * @throws InvalidSkinException + * @throws \UnexpectedValueException + */ + public static function parseSkinNBT(CompoundTag $nbt) : Skin{ + $skinTag = $nbt->getCompoundTag("Skin"); + if($skinTag === null){ + throw new \UnexpectedValueException("Missing skin data"); + } + return new Skin( //this throws if the skin is invalid + $skinTag->getString("Name"), + $skinTag->hasTag("Data", StringTag::class) ? $skinTag->getString("Data") : $skinTag->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM) + $skinTag->getByteArray("CapeData", ""), + $skinTag->getString("GeometryName", ""), + $skinTag->getByteArray("GeometryData", "") + ); } public function getUniqueId() : UUID{ diff --git a/src/entity/object/FallingBlock.php b/src/entity/object/FallingBlock.php index 765d20b63b..fef9b434ac 100644 --- a/src/entity/object/FallingBlock.php +++ b/src/entity/object/FallingBlock.php @@ -27,6 +27,7 @@ use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\block\utils\Fallable; use pocketmine\entity\Entity; +use pocketmine\entity\Location; use pocketmine\event\entity\EntityBlockChangeEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\nbt\tag\ByteTag; @@ -36,7 +37,6 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use function abs; -use function get_class; class FallingBlock extends Entity{ @@ -55,9 +55,12 @@ class FallingBlock extends Entity{ public $canCollide = false; - protected function initEntity(CompoundTag $nbt) : void{ - parent::initEntity($nbt); + public function __construct(Location $location, Block $block, CompoundTag $nbt){ + $this->block = $block; + parent::__construct($location, $nbt); + } + public static function parseBlockNBT(BlockFactory $factory, CompoundTag $nbt) : Block{ $blockId = 0; //TODO: 1.8+ save format @@ -68,12 +71,12 @@ class FallingBlock extends Entity{ } if($blockId === 0){ - throw new \UnexpectedValueException("Invalid " . get_class($this) . " entity: block ID is 0 or missing"); + throw new \UnexpectedValueException("Missing block info from NBT"); } $damage = $nbt->getByte("Data", 0); - $this->block = BlockFactory::getInstance()->get($blockId, $damage); + return $factory->get($blockId, $damage); } public function canCollideWith(Entity $entity) : bool{ diff --git a/src/entity/object/ItemEntity.php b/src/entity/object/ItemEntity.php index d933e978d8..778a363fa0 100644 --- a/src/entity/object/ItemEntity.php +++ b/src/entity/object/ItemEntity.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\entity\object; use pocketmine\entity\Entity; +use pocketmine\entity\Location; use pocketmine\event\entity\ItemDespawnEvent; use pocketmine\event\entity\ItemSpawnEvent; use pocketmine\event\inventory\InventoryPickupItemEvent; @@ -33,7 +34,6 @@ use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\protocol\AddItemActorPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; use pocketmine\player\Player; -use function get_class; use function max; class ItemEntity extends Entity{ @@ -65,6 +65,14 @@ class ItemEntity extends Entity{ /** @var int */ protected $despawnDelay = self::DEFAULT_DESPAWN_DELAY; + public function __construct(Location $location, Item $item, CompoundTag $nbt){ + if($item->isNull()){ + throw new \InvalidArgumentException("Item entity must have a non-air item with a count of at least 1"); + } + $this->item = $item; + parent::__construct($location, $nbt); + } + protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); @@ -81,16 +89,6 @@ class ItemEntity extends Entity{ $this->owner = $nbt->getString("Owner", $this->owner); $this->thrower = $nbt->getString("Thrower", $this->thrower); - $itemTag = $nbt->getCompoundTag("Item"); - if($itemTag === null){ - throw new \UnexpectedValueException("Invalid " . get_class($this) . " entity: expected \"Item\" NBT tag not found"); - } - - $this->item = Item::nbtDeserialize($itemTag); - if($this->item->isNull()){ - throw new \UnexpectedValueException("Item for " . get_class($this) . " is invalid"); - } - (new ItemSpawnEvent($this))->call(); } diff --git a/src/entity/object/Painting.php b/src/entity/object/Painting.php index f2d9f21e68..ac69cd3a04 100644 --- a/src/entity/object/Painting.php +++ b/src/entity/object/Painting.php @@ -25,12 +25,12 @@ namespace pocketmine\entity\object; use pocketmine\block\VanillaBlocks; use pocketmine\entity\Entity; +use pocketmine\entity\Location; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; -use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\AddPaintingPacket; use pocketmine\network\mcpe\protocol\types\entity\EntityLegacyIds; @@ -42,7 +42,7 @@ use function ceil; class Painting extends Entity{ public static function getNetworkTypeId() : int{ return EntityLegacyIds::PAINTING; } - private const DATA_TO_FACING = [ + public const DATA_TO_FACING = [ 0 => Facing::SOUTH, 1 => Facing::WEST, 2 => Facing::NORTH, @@ -73,15 +73,11 @@ class Painting extends Entity{ /** @var string */ protected $motive; - public function __construct(World $world, CompoundTag $nbt){ - $this->motive = $nbt->getString("Motive"); - $this->blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ")); - if($nbt->hasTag("Direction", ByteTag::class)){ - $this->facing = self::DATA_TO_FACING[$nbt->getByte("Direction")] ?? Facing::NORTH; - }elseif($nbt->hasTag("Facing", ByteTag::class)){ - $this->facing = self::DATA_TO_FACING[$nbt->getByte("Facing")] ?? Facing::NORTH; - } - parent::__construct($world, $nbt); + public function __construct(Location $location, Vector3 $blockIn, int $facing, PaintingMotive $motive, CompoundTag $nbt){ + $this->motive = $motive->getName(); //TODO: use motive directly + $this->blockIn = $blockIn->asVector3(); + $this->facing = $facing; + parent::__construct($location, $nbt); } protected function initEntity(CompoundTag $nbt) : void{ diff --git a/src/entity/object/PrimedTNT.php b/src/entity/object/PrimedTNT.php index b38f8a68f3..1a22d030b4 100644 --- a/src/entity/object/PrimedTNT.php +++ b/src/entity/object/PrimedTNT.php @@ -53,6 +53,17 @@ class PrimedTNT extends Entity implements Explosive{ public $canCollide = false; + public function getFuse() : int{ + return $this->fuse; + } + + public function setFuse(int $fuse) : void{ + if($fuse < 0 or $fuse > 32767){ + throw new \InvalidArgumentException("Fuse must be in the range 0-32767"); + } + $this->fuse = $fuse; + } + public function attack(EntityDamageEvent $source) : void{ if($source->getCause() === EntityDamageEvent::CAUSE_VOID){ parent::attack($source); diff --git a/src/entity/projectile/Arrow.php b/src/entity/projectile/Arrow.php index 6609453ee9..a5b631cc53 100644 --- a/src/entity/projectile/Arrow.php +++ b/src/entity/projectile/Arrow.php @@ -26,6 +26,7 @@ namespace pocketmine\entity\projectile; use pocketmine\block\Block; use pocketmine\entity\animation\ArrowShakeAnimation; use pocketmine\entity\Entity; +use pocketmine\entity\Location; use pocketmine\event\entity\ProjectileHitEvent; use pocketmine\event\inventory\InventoryPickupArrowEvent; use pocketmine\item\VanillaItems; @@ -36,7 +37,6 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\player\Player; use pocketmine\world\sound\ArrowHitSound; -use pocketmine\world\World; use function mt_rand; use function sqrt; @@ -71,8 +71,8 @@ class Arrow extends Projectile{ /** @var bool */ protected $critical = false; - public function __construct(World $world, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){ - parent::__construct($world, $nbt, $shootingEntity); + public function __construct(Location $location, ?Entity $shootingEntity, bool $critical, CompoundTag $nbt){ + parent::__construct($location, $shootingEntity, $nbt); $this->setCritical($critical); } diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index 018d28a515..9f2dee5c8a 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -27,6 +27,7 @@ use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\entity\Entity; use pocketmine\entity\Living; +use pocketmine\entity\Location; use pocketmine\event\entity\EntityCombustByEntityEvent; use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; @@ -41,7 +42,6 @@ use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\timings\Timings; -use pocketmine\world\World; use function assert; use function atan2; use function ceil; @@ -57,8 +57,8 @@ abstract class Projectile extends Entity{ /** @var Block|null */ protected $blockHit; - public function __construct(World $world, CompoundTag $nbt, ?Entity $shootingEntity = null){ - parent::__construct($world, $nbt); + public function __construct(Location $location, ?Entity $shootingEntity, CompoundTag $nbt){ + parent::__construct($location, $nbt); if($shootingEntity !== null){ $this->setOwningEntity($shootingEntity); } diff --git a/src/item/Bow.php b/src/item/Bow.php index f5fe19e47a..d54d194d8c 100644 --- a/src/item/Bow.php +++ b/src/item/Bow.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\EntityFactory; +use pocketmine\entity\Location; use pocketmine\entity\projectile\Arrow as ArrowEntity; use pocketmine\entity\projectile\Projectile; use pocketmine\event\entity\EntityShootBowEvent; use pocketmine\event\entity\ProjectileLaunchEvent; use pocketmine\item\enchantment\Enchantment; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\world\sound\BowShootSound; use function intdiv; @@ -51,18 +52,18 @@ class Bow extends Tool{ } $location = $player->getLocation(); - $nbt = EntityFactory::createBaseNBT( - $player->getEyePos(), - $player->getDirectionVector(), - ($location->yaw > 180 ? 360 : 0) - $location->yaw, - -$location->pitch - ); $diff = $player->getItemUseDuration(); $p = $diff / 20; $baseForce = min((($p ** 2) + $p * 2) / 3, 1); - $entity = new ArrowEntity($location->getWorldNonNull(), $nbt, $player, $baseForce >= 1); + $entity = new ArrowEntity(Location::fromObject( + $player->getEyePos(), + $player->getWorld(), + ($location->yaw > 180 ? 360 : 0) - $location->yaw, + -$location->pitch + ), $player, $baseForce >= 1, new CompoundTag()); + $entity->setMotion($player->getDirectionVector()); $infinity = $this->hasEnchantment(Enchantment::INFINITY()); if($infinity){ diff --git a/src/item/Egg.php b/src/item/Egg.php index 6ad22172eb..a441f72913 100644 --- a/src/item/Egg.php +++ b/src/item/Egg.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\EntityFactory; use pocketmine\entity\Location; use pocketmine\entity\projectile\Egg as EggEntity; use pocketmine\entity\projectile\Throwable; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; class Egg extends ProjectileItem{ @@ -36,11 +36,7 @@ class Egg extends ProjectileItem{ } protected function createEntity(Location $location, Player $thrower) : Throwable{ - return new EggEntity( - $location->getWorldNonNull(), - EntityFactory::createBaseNBT($location, null, $location->yaw, $location->pitch), - $thrower - ); + return new EggEntity($location, $thrower, new CompoundTag()); } public function getThrowForce() : float{ diff --git a/src/item/EnderPearl.php b/src/item/EnderPearl.php index 4c865cbdca..502e52f355 100644 --- a/src/item/EnderPearl.php +++ b/src/item/EnderPearl.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\EntityFactory; use pocketmine\entity\Location; use pocketmine\entity\projectile\EnderPearl as EnderPearlEntity; use pocketmine\entity\projectile\Throwable; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; class EnderPearl extends ProjectileItem{ @@ -36,11 +36,7 @@ class EnderPearl extends ProjectileItem{ } protected function createEntity(Location $location, Player $thrower) : Throwable{ - return new EnderPearlEntity( - $location->getWorldNonNull(), - EntityFactory::createBaseNBT($location, null, $location->yaw, $location->pitch), - $thrower - ); + return new EnderPearlEntity($location, $thrower, new CompoundTag()); } public function getThrowForce() : float{ diff --git a/src/item/ExperienceBottle.php b/src/item/ExperienceBottle.php index 20af8b56a8..d9de8628f4 100644 --- a/src/item/ExperienceBottle.php +++ b/src/item/ExperienceBottle.php @@ -23,20 +23,16 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\EntityFactory; use pocketmine\entity\Location; use pocketmine\entity\projectile\ExperienceBottle as ExperienceBottleEntity; use pocketmine\entity\projectile\Throwable; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; class ExperienceBottle extends ProjectileItem{ protected function createEntity(Location $location, Player $thrower) : Throwable{ - return new ExperienceBottleEntity( - $location->getWorldNonNull(), - EntityFactory::createBaseNBT($location, null, $location->yaw, $location->pitch), - $thrower - ); + return new ExperienceBottleEntity($location, $thrower, new CompoundTag()); } public function getThrowForce() : float{ diff --git a/src/item/ItemFactory.php b/src/item/ItemFactory.php index 2c286bc57e..23b63f604f 100644 --- a/src/item/ItemFactory.php +++ b/src/item/ItemFactory.php @@ -30,7 +30,7 @@ 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\Location; use pocketmine\entity\Squid; use pocketmine\entity\Villager; use pocketmine\entity\Zombie; @@ -319,17 +319,17 @@ class ItemFactory{ //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)); + return new Zombie(Location::fromObject($pos, $world, $yaw, $pitch), new CompoundTag()); } }); $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)); + return new Squid(Location::fromObject($pos, $world, $yaw, $pitch), new CompoundTag()); } }); $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)); + return new Villager(Location::fromObject($pos, $world, $yaw, $pitch), new CompoundTag()); } }); } diff --git a/src/item/PaintingItem.php b/src/item/PaintingItem.php index 16de653354..c4442a8f94 100644 --- a/src/item/PaintingItem.php +++ b/src/item/PaintingItem.php @@ -24,11 +24,12 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\block\Block; -use pocketmine\entity\EntityFactory; +use pocketmine\entity\Location; use pocketmine\entity\object\Painting; use pocketmine\entity\object\PaintingMotive; use pocketmine\math\Facing; use pocketmine\math\Vector3; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\world\sound\PaintingPlaceSound; use function array_rand; @@ -72,28 +73,10 @@ class PaintingItem extends Item{ /** @var PaintingMotive $motive */ $motive = $motives[array_rand($motives)]; - static $directions = [ - Facing::SOUTH => 0, - Facing::WEST => 1, - Facing::NORTH => 2, - Facing::EAST => 3 - ]; - - $direction = $directions[$face] ?? -1; - if($direction === -1){ - return ItemUseResult::NONE(); - } - $replacePos = $blockReplace->getPos(); $clickedPos = $blockClicked->getPos(); - $nbt = EntityFactory::createBaseNBT($replacePos, null, $direction * 90, 0); - $nbt->setByte("Direction", $direction); - $nbt->setString("Motive", $motive->getName()); - $nbt->setInt("TileX", $clickedPos->getFloorX()); - $nbt->setInt("TileY", $clickedPos->getFloorY()); - $nbt->setInt("TileZ", $clickedPos->getFloorZ()); - $entity = new Painting($replacePos->getWorldNonNull(), $nbt); + $entity = new Painting(Location::fromObject($replacePos, $replacePos->getWorldNonNull()), $clickedPos, $face, $motive, new CompoundTag()); $this->pop(); $entity->spawnToAll(); diff --git a/src/item/Snowball.php b/src/item/Snowball.php index 5dd6856a2b..36ab539840 100644 --- a/src/item/Snowball.php +++ b/src/item/Snowball.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\EntityFactory; use pocketmine\entity\Location; use pocketmine\entity\projectile\Snowball as SnowballEntity; use pocketmine\entity\projectile\Throwable; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; class Snowball extends ProjectileItem{ @@ -36,11 +36,7 @@ class Snowball extends ProjectileItem{ } protected function createEntity(Location $location, Player $thrower) : Throwable{ - return new SnowballEntity( - $location->getWorldNonNull(), - EntityFactory::createBaseNBT($location, null, $location->yaw, $location->pitch), - $thrower - ); + return new SnowballEntity($location, $thrower, new CompoundTag()); } public function getThrowForce() : float{ diff --git a/src/item/SpawnEgg.php b/src/item/SpawnEgg.php index 330bd11286..9748bfe722 100644 --- a/src/item/SpawnEgg.php +++ b/src/item/SpawnEgg.php @@ -25,10 +25,8 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\entity\Entity; -use pocketmine\entity\EntityFactory; use pocketmine\math\Vector3; use pocketmine\player\Player; -use pocketmine\utils\Utils; use pocketmine\world\World; use function lcg_value; diff --git a/src/item/SplashPotion.php b/src/item/SplashPotion.php index 9ad5106bc2..f9662755a7 100644 --- a/src/item/SplashPotion.php +++ b/src/item/SplashPotion.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\EntityFactory; use pocketmine\entity\Location; use pocketmine\entity\projectile\SplashPotion as SplashPotionEntity; use pocketmine\entity\projectile\Throwable; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; class SplashPotion extends ProjectileItem{ @@ -36,12 +36,7 @@ class SplashPotion extends ProjectileItem{ } protected function createEntity(Location $location, Player $thrower) : Throwable{ - $projectile = new SplashPotionEntity( - $location->getWorldNonNull(), - EntityFactory::createBaseNBT($location, null, $location->yaw, $location->pitch), - $thrower - ); - + $projectile = new SplashPotionEntity($location, $thrower, new CompoundTag()); $projectile->setPotionId($this->meta); return $projectile; } diff --git a/src/player/Player.php b/src/player/Player.php index 6843d2c4d6..3a7695af6e 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -267,7 +267,6 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, $this->networkSession = $session; $this->playerInfo = $playerInfo; $this->authenticated = $authenticated; - $this->skin = $this->playerInfo->getSkin(); $this->username = $username; $this->displayName = $this->username; @@ -282,16 +281,11 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, $namedtag = $this->server->getOfflinePlayerData($this->username); //TODO: make this async - $spawnReset = false; - if($namedtag !== null and ($world = $this->server->getWorldManager()->getWorldByName($namedtag->getString("Level", ""))) !== null){ - /** @var float[] $pos */ - $pos = $namedtag->getListTag("Pos")->getAllValues(); - $spawn = new Vector3($pos[0], $pos[1], $pos[2]); + $spawn = EntityFactory::parseLocation($namedtag, $world); }else{ - $world = $this->server->getWorldManager()->getDefaultWorld(); //TODO: default world might be null - $spawn = $world->getSafeSpawn(); - $spawnReset = true; + $world = $this->server->getWorldManager()->getDefaultWorld(); + $spawn = Location::fromObject($world->getSafeSpawn(), $world); } //load the spawn chunk so we can see the terrain @@ -300,20 +294,13 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, $this->usedChunks[World::chunkHash($spawn->getFloorX() >> 4, $spawn->getFloorZ() >> 4)] = UsedChunkStatus::NEEDED(); if($namedtag === null){ - $namedtag = EntityFactory::createBaseNBT($spawn); + $namedtag = new CompoundTag(); $namedtag->setByte("OnGround", 1); //TODO: this hack is needed for new players in-air ticks - they don't get detected as on-ground until they move //TODO: old code had a TODO for SpawnForced - - }elseif($spawnReset){ - $namedtag->setTag("Pos", new ListTag([ - new DoubleTag($spawn->x), - new DoubleTag($spawn->y), - new DoubleTag($spawn->z) - ])); } - parent::__construct($world, $namedtag); + parent::__construct($spawn, $this->playerInfo->getSkin(), $namedtag); $ev = new PlayerLoginEvent($this, "Plugin reason"); $ev->call(); diff --git a/src/world/World.php b/src/world/World.php index d1a7b0c561..2154b94cca 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -34,7 +34,7 @@ use pocketmine\block\tile\Spawnable; use pocketmine\block\tile\Tile; use pocketmine\block\UnknownBlock; use pocketmine\entity\Entity; -use pocketmine\entity\EntityFactory; +use pocketmine\entity\Location; use pocketmine\entity\object\ExperienceOrb; use pocketmine\entity\object\ItemEntity; use pocketmine\event\block\BlockBreakEvent; @@ -52,6 +52,7 @@ use pocketmine\item\ItemUseResult; use pocketmine\item\LegacyStringToItemParser; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; +use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\BlockActorDataPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\UpdateBlockPacket; @@ -1385,16 +1386,13 @@ class World implements ChunkManager{ return null; } - $motion = $motion ?? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1); - $nbt = EntityFactory::createBaseNBT($source, $motion, lcg_value() * 360, 0); - $nbt->setShort("Health", 5); - $nbt->setShort("PickupDelay", $delay); - $nbt->setTag("Item", $item->nbtSerialize()); + $itemEntity = new ItemEntity(Location::fromObject($source, $this, lcg_value() * 360, 0), $item, new CompoundTag()); - $itemEntity = new ItemEntity($this, $nbt); + $itemEntity->setPickupDelay($delay); + $itemEntity->setMotion($motion ?? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1)); $itemEntity->spawnToAll(); - return $itemEntity; + return $itemEntity; } /** @@ -1407,16 +1405,12 @@ class World implements ChunkManager{ $orbs = []; foreach(ExperienceOrb::splitIntoOrbSizes($amount) as $split){ - $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, - 0 - ); - $nbt->setShort(ExperienceOrb::TAG_VALUE_PC, $split); + $orb = new ExperienceOrb(Location::fromObject($pos, $this,lcg_value() * 360, 0), new CompoundTag()); - $orb = new ExperienceOrb($this, $nbt); + $orb->setXpValue($split); + $orb->setMotion($this->temporalVector->setComponents((lcg_value() * 0.2 - 0.1) * 2, lcg_value() * 0.4, (lcg_value() * 0.2 - 0.1) * 2)); $orb->spawnToAll(); + $orbs[] = $orb; }