Merge branch 'stable' into next-minor

This commit is contained in:
Dylan K. Taylor
2022-03-22 15:49:58 +00:00
31 changed files with 765 additions and 363 deletions

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();