mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 03:06:55 +00:00
Merge branch 'stable' into next-minor
This commit is contained in:
@ -100,7 +100,7 @@ class Fire extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->getSide(Facing::DOWN)->isSolid() && !$this->hasAdjacentFlammableBlocks()){
|
||||
if($this->getSide(Facing::DOWN)->isTransparent() && !$this->hasAdjacentFlammableBlocks()){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::AIR());
|
||||
}else{
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(30, 40));
|
||||
@ -130,7 +130,7 @@ class Fire extends Flowable{
|
||||
}
|
||||
}elseif(!$this->hasAdjacentFlammableBlocks()){
|
||||
$canSpread = false;
|
||||
if(!$down->isSolid() || $this->age > 3){
|
||||
if($down->isTransparent() || $this->age > 3){
|
||||
$result = VanillaBlocks::AIR();
|
||||
}
|
||||
}
|
||||
@ -181,14 +181,16 @@ class Fire extends Flowable{
|
||||
if(!$ev->isCancelled()){
|
||||
$block->onIncinerate();
|
||||
|
||||
$spreadedFire = false;
|
||||
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
|
||||
$fire = clone $this;
|
||||
$fire->age = min(self::MAX_AGE, $fire->age + (mt_rand(0, 4) >> 2));
|
||||
$spreadedFire = $this->spreadBlock($block, $fire);
|
||||
}
|
||||
if(!$spreadedFire){
|
||||
$this->position->getWorld()->setBlock($block->position, VanillaBlocks::AIR());
|
||||
if($this->position->getWorld()->getBlock($block->getPosition())->isSameState($block)){
|
||||
$spreadedFire = false;
|
||||
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
|
||||
$fire = clone $this;
|
||||
$fire->age = min(self::MAX_AGE, $fire->age + (mt_rand(0, 4) >> 2));
|
||||
$spreadedFire = $this->spreadBlock($block, $fire);
|
||||
}
|
||||
if(!$spreadedFire){
|
||||
$this->position->getWorld()->setBlock($block->position, VanillaBlocks::AIR());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function is_infinite;
|
||||
use function is_nan;
|
||||
use function lcg_value;
|
||||
|
||||
class ItemFrame extends Flowable{
|
||||
@ -111,6 +113,9 @@ class ItemFrame extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setItemDropChance(float $itemDropChance) : self{
|
||||
if($itemDropChance < 0.0 || $itemDropChance > 1.0 || is_nan($itemDropChance) || is_infinite($itemDropChance)){
|
||||
throw new \InvalidArgumentException("Drop chance must be in range 0-1");
|
||||
}
|
||||
$this->itemDropChance = $itemDropChance;
|
||||
return $this;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class Mycelium extends Opaque{
|
||||
$y = mt_rand($this->position->y - 2, $this->position->y + 2);
|
||||
$z = mt_rand($this->position->z - 1, $this->position->z + 1);
|
||||
$block = $this->position->getWorld()->getBlockAt($x, $y, $z);
|
||||
if($block->getId() === BlockLegacyIds::DIRT){
|
||||
if($block instanceof Dirt && !$block->isCoarse()){
|
||||
if($block->getSide(Facing::UP) instanceof Transparent){
|
||||
$ev = new BlockSpreadEvent($block, $this, VanillaBlocks::MYCELIUM());
|
||||
$ev->call();
|
||||
|
@ -91,7 +91,7 @@ class ShulkerBox extends Opaque{
|
||||
$shulker = $this->position->getWorld()->getTile($this->position);
|
||||
if($shulker instanceof TileShulkerBox){
|
||||
if(
|
||||
$this->getSide($this->facing)->getId() !== BlockLegacyIds::AIR ||
|
||||
$this->getSide($this->facing)->isSolid() ||
|
||||
!$shulker->canOpenWith($item->getCustomName())
|
||||
){
|
||||
return true;
|
||||
|
@ -151,9 +151,11 @@ class SweetBerryBush extends Flowable{
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
//TODO: in MCPE, this only triggers if moving while inside the bush block - we don't have the system to deal
|
||||
//with that reliably right now
|
||||
if($this->age >= self::STAGE_BUSH_NO_BERRIES && $entity instanceof Living){
|
||||
$entity->resetFallDistance();
|
||||
|
||||
//TODO: in MCPE, this only triggers if moving while inside the bush block - we don't have the system to deal
|
||||
//with that reliably right now
|
||||
$entity->attack(new EntityDamageByBlockEvent($this, $entity, EntityDamageByBlockEvent::CAUSE_CONTACT, 1));
|
||||
}
|
||||
return true;
|
||||
|
@ -41,11 +41,13 @@ interface CommandSender extends Permissible{
|
||||
/**
|
||||
* Returns the line height of the command-sender's screen. Used for determining sizes for command output pagination
|
||||
* such as in the /help command.
|
||||
* @phpstan-return positive-int
|
||||
*/
|
||||
public function getScreenLineHeight() : int;
|
||||
|
||||
/**
|
||||
* Sets the line height used for command output pagination for this command sender. `null` will reset it to default.
|
||||
* @phpstan-param positive-int|null $height
|
||||
*/
|
||||
public function setScreenLineHeight(?int $height) : void;
|
||||
}
|
||||
|
@ -39,7 +39,10 @@ class ConsoleCommandSender implements CommandSender{
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
/** @var int|null */
|
||||
/**
|
||||
* @var int|null
|
||||
* @phpstan-var positive-int|null
|
||||
*/
|
||||
protected $lineHeight = null;
|
||||
/** @var Language */
|
||||
private $language;
|
||||
|
@ -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();
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\Block;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class BlockTeleportEvent extends BlockEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
@ -44,6 +45,7 @@ class BlockTeleportEvent extends BlockEvent implements Cancellable{
|
||||
}
|
||||
|
||||
public function setTo(Vector3 $to) : void{
|
||||
Utils::checkVector3NotInfOrNaN($to);
|
||||
$this->to = $to;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\event\entity;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
/**
|
||||
@ -54,6 +55,7 @@ class EntityTeleportEvent extends EntityEvent implements Cancellable{
|
||||
}
|
||||
|
||||
public function setTo(Position $to) : void{
|
||||
Utils::checkVector3NotInfOrNaN($to);
|
||||
$this->to = $to;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\entity\Location;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\event\CancellableTrait;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class PlayerMoveEvent extends PlayerEvent implements Cancellable{
|
||||
use CancellableTrait;
|
||||
@ -51,6 +52,7 @@ class PlayerMoveEvent extends PlayerEvent implements Cancellable{
|
||||
}
|
||||
|
||||
public function setTo(Location $to) : void{
|
||||
Utils::checkLocationNotInfOrNaN($to);
|
||||
$this->to = $to;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\event\player;
|
||||
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
/**
|
||||
@ -46,6 +47,7 @@ class PlayerRespawnEvent extends PlayerEvent{
|
||||
if(!$position->isValid()){
|
||||
throw new \InvalidArgumentException("Spawn position must reference a valid and loaded World");
|
||||
}
|
||||
Utils::checkVector3NotInfOrNaN($position);
|
||||
$this->position = $position;
|
||||
}
|
||||
}
|
||||
|
@ -232,6 +232,9 @@ class TypeConverter{
|
||||
$compound = null;
|
||||
}
|
||||
}
|
||||
if($meta < 0 || $meta >= 0x7fff){ //this meta value may have been restored from the NBT
|
||||
throw new TypeConversionException("Item meta must be in range 0 ... " . 0x7fff . " (received $meta)");
|
||||
}
|
||||
|
||||
try{
|
||||
return ItemFactory::getInstance()->get(
|
||||
|
@ -253,6 +253,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
protected bool $blockCollision = true;
|
||||
protected bool $flying = false;
|
||||
|
||||
/** @phpstan-var positive-int|null */
|
||||
protected ?int $lineHeight = null;
|
||||
protected string $locale = "en_US";
|
||||
|
||||
|
@ -71,6 +71,7 @@ class DiskResourceProvider implements ResourceProvider{
|
||||
public function getResources() : array{
|
||||
$resources = [];
|
||||
if(is_dir($this->file)){
|
||||
/** @var \SplFileInfo $resource */
|
||||
foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->file)) as $resource){
|
||||
if($resource->isFile()){
|
||||
$path = str_replace(DIRECTORY_SEPARATOR, "/", substr((string) $resource, strlen($this->file)));
|
||||
|
@ -156,7 +156,7 @@ class PluginDescription{
|
||||
$k = $v;
|
||||
$v = "*";
|
||||
}
|
||||
$this->extensions[$k] = array_map('strval', is_array($v) ? $v : [$v]);
|
||||
$this->extensions[(string) $k] = array_map('strval', is_array($v) ? $v : [$v]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use function preg_match;
|
||||
|
||||
trait EnumTrait{
|
||||
use RegistryTrait;
|
||||
use NotCloneable;
|
||||
@ -70,9 +68,7 @@ trait EnumTrait{
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function __construct(string $enumName){
|
||||
if(preg_match('/^\D[A-Za-z\d_]+$/u', $enumName, $matches) === 0){
|
||||
throw new \InvalidArgumentException("Invalid enum member name \"$enumName\", should only contain letters, numbers and underscores, and must not start with a number");
|
||||
}
|
||||
self::verifyName($enumName);
|
||||
$this->enumName = $enumName;
|
||||
if(self::$nextId === null){
|
||||
self::$nextId = Process::pid(); //this provides enough base entropy to prevent hardcoding
|
||||
|
@ -26,17 +26,25 @@ namespace pocketmine\utils;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function mb_strtoupper;
|
||||
use function preg_match;
|
||||
|
||||
trait RegistryTrait{
|
||||
/** @var object[] */
|
||||
private static $members = null;
|
||||
|
||||
private static function verifyName(string $name) : void{
|
||||
if(preg_match('/^(?!\d)[A-Za-z\d_]+$/u', $name) === 0){
|
||||
throw new \InvalidArgumentException("Invalid member name \"$name\", should only contain letters, numbers and underscores, and must not start with a number");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given object to the registry.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private static function _registryRegister(string $name, object $member) : void{
|
||||
self::verifyName($name);
|
||||
$upperName = mb_strtoupper($name);
|
||||
if(isset(self::$members[$upperName])){
|
||||
throw new \InvalidArgumentException("\"$upperName\" is already reserved");
|
||||
|
@ -28,7 +28,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use DaveRandom\CallbackValidator\CallbackType;
|
||||
use pocketmine\entity\Location;
|
||||
use pocketmine\errorhandler\ErrorTypeToStringMap;
|
||||
use pocketmine\math\Vector3;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use function array_combine;
|
||||
@ -57,7 +59,9 @@ use function interface_exists;
|
||||
use function is_a;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_infinite;
|
||||
use function is_int;
|
||||
use function is_nan;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function mb_check_encoding;
|
||||
@ -602,4 +606,27 @@ final class Utils{
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function checkFloatNotInfOrNaN(string $name, float $float) : void{
|
||||
if(is_nan($float)){
|
||||
throw new \InvalidArgumentException("$name cannot be NaN");
|
||||
}
|
||||
if(is_infinite($float)){
|
||||
throw new \InvalidArgumentException("$name cannot be infinite");
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkVector3NotInfOrNaN(Vector3 $vector3) : void{
|
||||
if($vector3 instanceof Location){ //location could be masquerading as vector3
|
||||
self::checkFloatNotInfOrNaN("yaw", $vector3->yaw);
|
||||
self::checkFloatNotInfOrNaN("pitch", $vector3->pitch);
|
||||
}
|
||||
self::checkFloatNotInfOrNaN("x", $vector3->x);
|
||||
self::checkFloatNotInfOrNaN("y", $vector3->y);
|
||||
self::checkFloatNotInfOrNaN("z", $vector3->z);
|
||||
}
|
||||
|
||||
public static function checkLocationNotInfOrNaN(Location $location) : void{
|
||||
self::checkVector3NotInfOrNaN($location);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user