mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 01:46:04 +00:00
Merge branch 'stable' into next-minor
This commit is contained in:
@ -56,6 +56,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\Sound;
|
||||
@ -69,8 +70,6 @@ use function deg2rad;
|
||||
use function floor;
|
||||
use function fmod;
|
||||
use function get_class;
|
||||
use function is_infinite;
|
||||
use function is_nan;
|
||||
use function lcg_value;
|
||||
use function sin;
|
||||
use function spl_object_id;
|
||||
@ -217,7 +216,15 @@ abstract class Entity{
|
||||
/** @var int|null */
|
||||
protected $targetId = null;
|
||||
|
||||
private bool $constructorCalled = false;
|
||||
|
||||
public function __construct(Location $location, ?CompoundTag $nbt = null){
|
||||
if($this->constructorCalled){
|
||||
throw new \LogicException("Attempted to call constructor for an Entity multiple times");
|
||||
}
|
||||
$this->constructorCalled = true;
|
||||
Utils::checkLocationNotInfOrNaN($location);
|
||||
|
||||
$this->timings = Timings::getEntityTimings($this);
|
||||
|
||||
$this->size = $this->getInitialSizeInfo();
|
||||
@ -226,11 +233,6 @@ abstract class Entity{
|
||||
$this->server = $location->getWorld()->getServer();
|
||||
|
||||
$this->location = $location->asLocation();
|
||||
assert(
|
||||
!is_nan($this->location->x) && !is_infinite($this->location->x) &&
|
||||
!is_nan($this->location->y) && !is_infinite($this->location->y) &&
|
||||
!is_nan($this->location->z) && !is_infinite($this->location->z)
|
||||
);
|
||||
|
||||
$this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
|
||||
$this->recalculateBoundingBox();
|
||||
@ -769,10 +771,6 @@ abstract class Entity{
|
||||
$this->server->broadcastPackets($this->hasSpawned, [MoveActorAbsolutePacket::create(
|
||||
$this->id,
|
||||
$this->getOffsetPosition($this->location),
|
||||
|
||||
//this looks very odd but is correct as of 1.5.0.7
|
||||
//for arrows this is actually x/y/z rotation
|
||||
//for mobs x and z are used for pitch and yaw, and y is used for headyaw
|
||||
$this->location->pitch,
|
||||
$this->location->yaw,
|
||||
$this->location->yaw,
|
||||
@ -1332,6 +1330,8 @@ abstract class Entity{
|
||||
}
|
||||
|
||||
public function setRotation(float $yaw, float $pitch) : void{
|
||||
Utils::checkFloatNotInfOrNaN("yaw", $yaw);
|
||||
Utils::checkFloatNotInfOrNaN("pitch", $pitch);
|
||||
$this->location->yaw = $yaw;
|
||||
$this->location->pitch = $pitch;
|
||||
$this->scheduleUpdate();
|
||||
@ -1357,6 +1357,7 @@ abstract class Entity{
|
||||
}
|
||||
|
||||
public function setMotion(Vector3 $motion) : bool{
|
||||
Utils::checkVector3NotInfOrNaN($motion);
|
||||
if(!$this->justCreated){
|
||||
$ev = new EntityMotionEvent($this, $motion);
|
||||
$ev->call();
|
||||
@ -1378,6 +1379,9 @@ abstract class Entity{
|
||||
* Adds the given values to the entity's motion vector.
|
||||
*/
|
||||
public function addMotion(float $x, float $y, float $z) : void{
|
||||
Utils::checkFloatNotInfOrNaN("x", $x);
|
||||
Utils::checkFloatNotInfOrNaN("y", $y);
|
||||
Utils::checkFloatNotInfOrNaN("z", $z);
|
||||
$this->motion = $this->motion->add($x, $y, $z);
|
||||
}
|
||||
|
||||
@ -1389,10 +1393,18 @@ abstract class Entity{
|
||||
* @param Vector3|Position|Location $pos
|
||||
*/
|
||||
public function teleport(Vector3 $pos, ?float $yaw = null, ?float $pitch = null) : bool{
|
||||
Utils::checkVector3NotInfOrNaN($pos);
|
||||
if($pos instanceof Location){
|
||||
$yaw = $yaw ?? $pos->yaw;
|
||||
$pitch = $pitch ?? $pos->pitch;
|
||||
}
|
||||
if($yaw !== null){
|
||||
Utils::checkFloatNotInfOrNaN("yaw", $yaw);
|
||||
}
|
||||
if($pitch !== null){
|
||||
Utils::checkFloatNotInfOrNaN("pitch", $pitch);
|
||||
}
|
||||
|
||||
$from = $this->location->asPosition();
|
||||
$to = Position::fromObject($pos, $pos instanceof Position ? $pos->getWorld() : $this->getWorld());
|
||||
$ev = new EntityTeleportEvent($this, $from, $to);
|
||||
|
@ -32,6 +32,8 @@ use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\world\World;
|
||||
use function count;
|
||||
use function is_infinite;
|
||||
use function is_nan;
|
||||
|
||||
final class EntityDataHelper{
|
||||
|
||||
@ -39,6 +41,18 @@ final class EntityDataHelper{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
private static function validateFloat(string $tagName, string $component, float $value) : void{
|
||||
if(is_infinite($value)){
|
||||
throw new SavedDataLoadingException("$component component of '$tagName' contains invalid infinite value");
|
||||
}
|
||||
if(is_nan($value)){
|
||||
throw new SavedDataLoadingException("$component component of '$tagName' contains invalid NaN value");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
@ -54,6 +68,8 @@ final class EntityDataHelper{
|
||||
if(count($values) !== 2){
|
||||
throw new SavedDataLoadingException("Expected exactly 2 entries for 'Rotation'");
|
||||
}
|
||||
self::validateFloat("Rotation", "yaw", $values[0]->getValue());
|
||||
self::validateFloat("Rotation", "pitch", $values[1]->getValue());
|
||||
|
||||
return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue());
|
||||
}
|
||||
@ -74,6 +90,15 @@ final class EntityDataHelper{
|
||||
if(count($values) !== 3){
|
||||
throw new SavedDataLoadingException("Expected exactly 3 entries in '$tagName' tag");
|
||||
}
|
||||
return new Vector3($values[0]->getValue(), $values[1]->getValue(), $values[2]->getValue());
|
||||
|
||||
$x = $values[0]->getValue();
|
||||
$y = $values[1]->getValue();
|
||||
$z = $values[2]->getValue();
|
||||
|
||||
self::validateFloat($tagName, "x", $x);
|
||||
self::validateFloat($tagName, "y", $y);
|
||||
self::validateFloat($tagName, "z", $z);
|
||||
|
||||
return new Vector3($x, $y, $z);
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,11 @@ use DaveRandom\CallbackValidator\CallbackType;
|
||||
use DaveRandom\CallbackValidator\ParameterType;
|
||||
use DaveRandom\CallbackValidator\ReturnType;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\data\bedrock\EntityLegacyIds;
|
||||
use pocketmine\data\bedrock\EntityLegacyIds as LegacyIds;
|
||||
use pocketmine\data\bedrock\PotionTypeIdMap;
|
||||
use pocketmine\data\bedrock\PotionTypeIds;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\EntityDataHelper as Helper;
|
||||
use pocketmine\entity\object\ExperienceOrb;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
use pocketmine\entity\object\ItemEntity;
|
||||
@ -81,20 +82,20 @@ 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(EntityDataHelper::parseLocation($nbt, $world), null, $nbt->getByte(Arrow::TAG_CRIT, 0) === 1, $nbt);
|
||||
}, ['Arrow', 'minecraft:arrow'], EntityLegacyIds::ARROW);
|
||||
return new Arrow(Helper::parseLocation($nbt, $world), null, $nbt->getByte(Arrow::TAG_CRIT, 0) === 1, $nbt);
|
||||
}, ['Arrow', 'minecraft:arrow'], LegacyIds::ARROW);
|
||||
|
||||
$this->register(Egg::class, function(World $world, CompoundTag $nbt) : Egg{
|
||||
return new Egg(EntityDataHelper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['Egg', 'minecraft:egg'], EntityLegacyIds::EGG);
|
||||
return new Egg(Helper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['Egg', 'minecraft:egg'], LegacyIds::EGG);
|
||||
|
||||
$this->register(EnderPearl::class, function(World $world, CompoundTag $nbt) : EnderPearl{
|
||||
return new EnderPearl(EntityDataHelper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['ThrownEnderpearl', 'minecraft:ender_pearl'], EntityLegacyIds::ENDER_PEARL);
|
||||
return new EnderPearl(Helper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['ThrownEnderpearl', 'minecraft:ender_pearl'], LegacyIds::ENDER_PEARL);
|
||||
|
||||
$this->register(ExperienceBottle::class, function(World $world, CompoundTag $nbt) : ExperienceBottle{
|
||||
return new ExperienceBottle(EntityDataHelper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['ThrownExpBottle', 'minecraft:xp_bottle'], EntityLegacyIds::XP_BOTTLE);
|
||||
return new ExperienceBottle(Helper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['ThrownExpBottle', 'minecraft:xp_bottle'], LegacyIds::XP_BOTTLE);
|
||||
|
||||
$this->register(ExperienceOrb::class, function(World $world, CompoundTag $nbt) : ExperienceOrb{
|
||||
$value = 1;
|
||||
@ -104,12 +105,12 @@ final class EntityFactory{
|
||||
$value = $valuePeTag->getValue();
|
||||
}
|
||||
|
||||
return new ExperienceOrb(EntityDataHelper::parseLocation($nbt, $world), $value, $nbt);
|
||||
}, ['XPOrb', 'minecraft:xp_orb'], EntityLegacyIds::XP_ORB);
|
||||
return new ExperienceOrb(Helper::parseLocation($nbt, $world), $value, $nbt);
|
||||
}, ['XPOrb', 'minecraft:xp_orb'], LegacyIds::XP_ORB);
|
||||
|
||||
$this->register(FallingBlock::class, function(World $world, CompoundTag $nbt) : FallingBlock{
|
||||
return new FallingBlock(EntityDataHelper::parseLocation($nbt, $world), FallingBlock::parseBlockNBT(BlockFactory::getInstance(), $nbt), $nbt);
|
||||
}, ['FallingSand', 'minecraft:falling_block'], EntityLegacyIds::FALLING_BLOCK);
|
||||
return new FallingBlock(Helper::parseLocation($nbt, $world), FallingBlock::parseBlockNBT(BlockFactory::getInstance(), $nbt), $nbt);
|
||||
}, ['FallingSand', 'minecraft:falling_block'], LegacyIds::FALLING_BLOCK);
|
||||
|
||||
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
|
||||
$itemTag = $nbt->getCompoundTag("Item");
|
||||
@ -121,8 +122,8 @@ final class EntityFactory{
|
||||
if($item->isNull()){
|
||||
throw new SavedDataLoadingException("Item is invalid");
|
||||
}
|
||||
return new ItemEntity(EntityDataHelper::parseLocation($nbt, $world), $item, $nbt);
|
||||
}, ['Item', 'minecraft:item'], EntityLegacyIds::ITEM);
|
||||
return new ItemEntity(Helper::parseLocation($nbt, $world), $item, $nbt);
|
||||
}, ['Item', 'minecraft:item'], LegacyIds::ITEM);
|
||||
|
||||
$this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{
|
||||
$motive = PaintingMotive::getMotiveByName($nbt->getString("Motive"));
|
||||
@ -138,39 +139,39 @@ final class EntityFactory{
|
||||
throw new SavedDataLoadingException("Missing facing info");
|
||||
}
|
||||
|
||||
return new Painting(EntityDataHelper::parseLocation($nbt, $world), $blockIn, $facing, $motive, $nbt);
|
||||
}, ['Painting', 'minecraft:painting'], EntityLegacyIds::PAINTING);
|
||||
return new Painting(Helper::parseLocation($nbt, $world), $blockIn, $facing, $motive, $nbt);
|
||||
}, ['Painting', 'minecraft:painting'], LegacyIds::PAINTING);
|
||||
|
||||
$this->register(PrimedTNT::class, function(World $world, CompoundTag $nbt) : PrimedTNT{
|
||||
return new PrimedTNT(EntityDataHelper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt'], EntityLegacyIds::TNT);
|
||||
return new PrimedTNT(Helper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt'], LegacyIds::TNT);
|
||||
|
||||
$this->register(Snowball::class, function(World $world, CompoundTag $nbt) : Snowball{
|
||||
return new Snowball(EntityDataHelper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['Snowball', 'minecraft:snowball'], EntityLegacyIds::SNOWBALL);
|
||||
return new Snowball(Helper::parseLocation($nbt, $world), null, $nbt);
|
||||
}, ['Snowball', 'minecraft:snowball'], LegacyIds::SNOWBALL);
|
||||
|
||||
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
|
||||
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER));
|
||||
if($potionType === null){
|
||||
throw new SavedDataLoadingException("No such potion type");
|
||||
}
|
||||
return new SplashPotion(EntityDataHelper::parseLocation($nbt, $world), null, $potionType, $nbt);
|
||||
}, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], EntityLegacyIds::SPLASH_POTION);
|
||||
return new SplashPotion(Helper::parseLocation($nbt, $world), null, $potionType, $nbt);
|
||||
}, ['ThrownPotion', 'minecraft:potion', 'thrownpotion'], LegacyIds::SPLASH_POTION);
|
||||
|
||||
$this->register(Squid::class, function(World $world, CompoundTag $nbt) : Squid{
|
||||
return new Squid(EntityDataHelper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['Squid', 'minecraft:squid'], EntityLegacyIds::SQUID);
|
||||
return new Squid(Helper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['Squid', 'minecraft:squid'], LegacyIds::SQUID);
|
||||
|
||||
$this->register(Villager::class, function(World $world, CompoundTag $nbt) : Villager{
|
||||
return new Villager(EntityDataHelper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['Villager', 'minecraft:villager'], EntityLegacyIds::VILLAGER);
|
||||
return new Villager(Helper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['Villager', 'minecraft:villager'], LegacyIds::VILLAGER);
|
||||
|
||||
$this->register(Zombie::class, function(World $world, CompoundTag $nbt) : Zombie{
|
||||
return new Zombie(EntityDataHelper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['Zombie', 'minecraft:zombie'], EntityLegacyIds::ZOMBIE);
|
||||
return new Zombie(Helper::parseLocation($nbt, $world), $nbt);
|
||||
}, ['Zombie', 'minecraft:zombie'], LegacyIds::ZOMBIE);
|
||||
|
||||
$this->register(Human::class, function(World $world, CompoundTag $nbt) : Human{
|
||||
return new Human(EntityDataHelper::parseLocation($nbt, $world), Human::parseSkinNBT($nbt), $nbt);
|
||||
return new Human(Helper::parseLocation($nbt, $world), Human::parseSkinNBT($nbt), $nbt);
|
||||
}, ['Human']);
|
||||
|
||||
PaintingMotive::init();
|
||||
|
Reference in New Issue
Block a user