Merge 'minor-next' into 'major-next'

Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/14565559872
This commit is contained in:
pmmp-admin-bot[bot] 2025-04-21 01:45:18 +00:00
commit d86943fa8c
7 changed files with 50 additions and 3 deletions

View File

@ -114,6 +114,13 @@ final class TileFactory{
$this->saveNames[$className] = reset($saveNames);
}
/**
* @phpstan-param class-string<Tile> $class
*/
public function isRegistered(string $class) : bool{
return isset($this->saveNames[$class]);
}
/**
* @internal
* @throws SavedDataLoadingException

View File

@ -224,6 +224,10 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return $this->cache[$stateId] ??= $this->serializeBlock(RuntimeBlockStateRegistry::getInstance()->fromStateId($stateId));
}
public function isRegistered(Block $block) : bool{
return isset($this->serializers[$block->getTypeId()]);
}
/**
* @phpstan-template TBlockType of Block
* @phpstan-param TBlockType $block

View File

@ -61,6 +61,7 @@ use pocketmine\player\Player;
use pocketmine\Server;
use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler;
use pocketmine\utils\Limits;
use pocketmine\utils\Utils;
use pocketmine\VersionInfo;
use pocketmine\world\format\Chunk;
@ -77,6 +78,7 @@ use function floatval;
use function floor;
use function fmod;
use function get_class;
use function min;
use function sin;
use function spl_object_id;
use const M_PI_2;
@ -701,9 +703,16 @@ abstract class Entity{
* @throws \InvalidArgumentException
*/
public function setFireTicks(int $fireTicks) : void{
if($fireTicks < 0 || $fireTicks > 0x7fff){
throw new \InvalidArgumentException("Fire ticks must be in range 0 ... " . 0x7fff . ", got $fireTicks");
if($fireTicks < 0){
throw new \InvalidArgumentException("Fire ticks cannot be negative");
}
//Since the max value is not externally obvious or intuitive, many plugins use this without being aware that
//reasonably large values are not accepted. We even have such usages within PM itself. It doesn't make sense
//to force all those calls to be aware of this limitation, as it's not a functional limit but a limitation of
//the Mojang save format. Truncating this to the max acceptable value is the next best thing we can do.
$fireTicks = min($fireTicks, Limits::INT16_MAX);
if(!$this->isFireProof()){
$this->fireTicks = $fireTicks;
$this->networkPropertiesDirty = true;

View File

@ -219,6 +219,13 @@ final class EntityFactory{
$this->saveNames[$className] = reset($saveNames);
}
/**
* @phpstan-param class-string<Entity> $class
*/
public function isRegistered(string $class) : bool{
return isset($this->saveNames[$class]);
}
/**
* Creates an entity from data stored on a chunk.
*

View File

@ -28,6 +28,7 @@ use pocketmine\item\enchantment\ItemEnchantmentTags as Tags;
use pocketmine\item\enchantment\VanillaEnchantments as Enchantments;
use pocketmine\item\Item;
use pocketmine\utils\SingletonTrait;
use pocketmine\utils\Utils;
use function array_filter;
use function array_values;
use function count;
@ -129,6 +130,7 @@ final class AvailableEnchantmentRegistry{
if(!$this->isRegistered($enchantment)){
throw new \LogicException("Cannot set primary item tags for non-registered enchantment");
}
Utils::validateArrayValueType($tags, fn(string $v) => 1);
$this->primaryItemTags[spl_object_id($enchantment)] = array_values($tags);
}
@ -152,6 +154,7 @@ final class AvailableEnchantmentRegistry{
if(!$this->isRegistered($enchantment)){
throw new \LogicException("Cannot set secondary item tags for non-registered enchantment");
}
Utils::validateArrayValueType($tags, fn(string $v) => 1);
$this->secondaryItemTags[spl_object_id($enchantment)] = array_values($tags);
}

View File

@ -584,7 +584,7 @@ final class Utils{
/**
* @phpstan-template TMemberType
* @phpstan-param array<mixed, TMemberType> $array
* @phpstan-param \Closure(TMemberType) : void $validator
* @phpstan-param \Closure(TMemberType) : mixed $validator
*/
public static function validateArrayValueType(array $array, \Closure $validator) : void{
foreach(Utils::promoteKeys($array) as $k => $v){

View File

@ -2028,6 +2028,15 @@ class World implements ChunkManager{
throw new WorldException("Cannot set a block in un-generated terrain");
}
//TODO: this computes state ID twice (we do it again in writeStateToWorld()). Not great for performance :(
$stateId = $block->getStateId();
if(!$this->blockStateRegistry->hasStateId($stateId)){
throw new \LogicException("Block state ID not known to RuntimeBlockStateRegistry (probably not registered)");
}
if(!GlobalBlockStateHandlers::getSerializer()->isRegistered($block)){
throw new \LogicException("Block not registered with GlobalBlockStateHandlers serializer");
}
$this->timings->setBlock->startTiming();
$this->unlockChunk($chunkX, $chunkZ, null);
@ -2750,6 +2759,11 @@ class World implements ChunkManager{
throw new AssumptionFailedError("Found two different entities sharing entity ID " . $entity->getId());
}
}
if(!EntityFactory::getInstance()->isRegistered($entity::class)){
//canSaveWithChunk is mutable, so that means it could be toggled after adding the entity and cause a crash
//later on. Better we just force all entities to have a save ID, even if it might not be needed.
throw new \LogicException("Entity " . $entity::class . " is not registered for a save ID in EntityFactory");
}
$pos = $entity->getPosition()->asVector3();
$this->entitiesByChunk[World::chunkHash($pos->getFloorX() >> Chunk::COORD_BIT_SIZE, $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE)][$entity->getId()] = $entity;
$this->entityLastKnownPositions[$entity->getId()] = $pos;
@ -2851,6 +2865,9 @@ class World implements ChunkManager{
if(!$this->isInWorld($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ())){
throw new \InvalidArgumentException("Tile position is outside the world bounds");
}
if(!TileFactory::getInstance()->isRegistered($tile::class)){
throw new \LogicException("Tile " . $tile::class . " is not registered for a save ID in TileFactory");
}
$chunkX = $pos->getFloorX() >> Chunk::COORD_BIT_SIZE;
$chunkZ = $pos->getFloorZ() >> Chunk::COORD_BIT_SIZE;