Further refactors to prepare for y=-64 lower limit

This commit is contained in:
Dylan K. Taylor 2021-03-18 00:08:16 +00:00
parent b844c4266d
commit eb9a68edee
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
17 changed files with 101 additions and 51 deletions

View File

@ -867,6 +867,7 @@ This version features substantial changes to the network system, improving coher
- `World->populateChunk()` has been split into `World->requestChunkPopulation()` and `World->orderChunkPopulation()`. - `World->populateChunk()` has been split into `World->requestChunkPopulation()` and `World->orderChunkPopulation()`.
- The following API methods have changed behaviour: - The following API methods have changed behaviour:
- `World->getChunk()` no longer tries to load chunks from disk. If the chunk is not already in memory, null is returned. (This behaviour now properly matches other `ChunkManager` implementations.) - `World->getChunk()` no longer tries to load chunks from disk. If the chunk is not already in memory, null is returned. (This behaviour now properly matches other `ChunkManager` implementations.)
- `World->getHighestBlockAt()` now returns `null` instead of `-1` if the target X/Z column contains no blocks.
- The following methods now throw `WorldException` when targeting ungenerated terrain: - The following methods now throw `WorldException` when targeting ungenerated terrain:
- `World->getSafeSpawn()` (previously it just silently returned the input position) - `World->getSafeSpawn()` (previously it just silently returned the input position)
- `World->getHighestBlockAt()` (previously it returned -1) - `World->getHighestBlockAt()` (previously it returned -1)

View File

@ -49,7 +49,7 @@ class ChorusFruit extends Food{
$origin = $consumer->getPosition(); $origin = $consumer->getPosition();
$minX = $origin->getFloorX() - 8; $minX = $origin->getFloorX() - 8;
$minY = min($origin->getFloorY(), $consumer->getWorld()->getWorldHeight()) - 8; $minY = min($origin->getFloorY(), $consumer->getWorld()->getMaxY()) - 8;
$minZ = $origin->getFloorZ() - 8; $minZ = $origin->getFloorZ() - 8;
$maxX = $minX + 16; $maxX = $minX + 16;

View File

@ -45,9 +45,14 @@ interface ChunkManager{
public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk) : void; public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk) : void;
/** /**
* Returns the height of the world * Returns the lowest buildable Y coordinate of the world
*/ */
public function getWorldHeight() : int; public function getMinY() : int;
/**
* Returns the highest buildable Y coordinate of the world
*/
public function getMaxY() : int;
/** /**
* Returns whether the specified coordinates are within the valid world boundaries, taking world format limitations * Returns whether the specified coordinates are within the valid world boundaries, taking world format limitations

View File

@ -35,13 +35,13 @@ class SimpleChunkManager implements ChunkManager{
protected $chunks = []; protected $chunks = [];
/** @var int */ /** @var int */
protected $worldHeight; private $minY;
/** @var int */
private $maxY;
/** public function __construct(int $minY, int $maxY){
* SimpleChunkManager constructor. $this->minY = $minY;
*/ $this->maxY = $maxY;
public function __construct(int $worldHeight = World::Y_MAX){
$this->worldHeight = $worldHeight;
} }
public function getBlockAt(int $x, int $y, int $z) : Block{ public function getBlockAt(int $x, int $y, int $z) : Block{
@ -71,14 +71,18 @@ class SimpleChunkManager implements ChunkManager{
$this->chunks = []; $this->chunks = [];
} }
public function getWorldHeight() : int{ public function getMinY() : int{
return $this->worldHeight; return $this->minY;
}
public function getMaxY() : int{
return $this->maxY;
} }
public function isInWorld(int $x, int $y, int $z) : bool{ public function isInWorld(int $x, int $y, int $z) : bool{
return ( return (
$x <= Limits::INT32_MAX and $x >= Limits::INT32_MIN and $x <= Limits::INT32_MAX and $x >= Limits::INT32_MIN and
$y < $this->worldHeight and $y >= 0 and $y < $this->maxY and $y >= $this->minY and
$z <= Limits::INT32_MAX and $z >= Limits::INT32_MIN $z <= Limits::INT32_MAX and $z >= Limits::INT32_MIN
); );
} }

View File

@ -174,7 +174,9 @@ class World implements ChunkManager{
private $providerGarbageCollectionTicker = 0; private $providerGarbageCollectionTicker = 0;
/** @var int */ /** @var int */
private $worldHeight; private $minY;
/** @var int */
private $maxY;
/** @var ChunkLoader[] */ /** @var ChunkLoader[] */
private $loaders = []; private $loaders = [];
@ -383,7 +385,8 @@ class World implements ChunkManager{
$this->displayName = $this->provider->getWorldData()->getName(); $this->displayName = $this->provider->getWorldData()->getName();
$this->logger = new \PrefixedLogger($server->getLogger(), "World: $this->displayName"); $this->logger = new \PrefixedLogger($server->getLogger(), "World: $this->displayName");
$this->worldHeight = $this->provider->getWorldHeight(); $this->minY = $this->provider->getWorldMinY();
$this->maxY = $this->provider->getWorldMaxY();
$this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.level.preparing", [$this->displayName])); $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.level.preparing", [$this->displayName]));
$this->generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator(), true); $this->generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator(), true);
@ -1335,7 +1338,7 @@ class World implements ChunkManager{
public function isInWorld(int $x, int $y, int $z) : bool{ public function isInWorld(int $x, int $y, int $z) : bool{
return ( return (
$x <= Limits::INT32_MAX and $x >= Limits::INT32_MIN and $x <= Limits::INT32_MAX and $x >= Limits::INT32_MIN and
$y < $this->worldHeight and $y >= 0 and $y < $this->maxY and $y >= $this->minY and
$z <= Limits::INT32_MAX and $z >= Limits::INT32_MIN $z <= Limits::INT32_MAX and $z >= Limits::INT32_MIN
); );
} }
@ -2089,10 +2092,10 @@ class World implements ChunkManager{
/** /**
* Gets the highest block Y value at a specific $x and $z * Gets the highest block Y value at a specific $x and $z
* *
* @return int 0-255, or -1 if the column is empty * @return int|null 0-255, or null if the column is empty
* @throws WorldException if the terrain is not generated * @throws WorldException if the terrain is not generated
*/ */
public function getHighestBlockAt(int $x, int $z) : int{ public function getHighestBlockAt(int $x, int $z) : ?int{
if(($chunk = $this->loadChunk($x >> 4, $z >> 4)) !== null){ if(($chunk = $this->loadChunk($x >> 4, $z >> 4)) !== null){
return $chunk->getHighestBlockAt($x & 0x0f, $z & 0x0f); return $chunk->getHighestBlockAt($x & 0x0f, $z & 0x0f);
} }
@ -2481,7 +2484,7 @@ class World implements ChunkManager{
$spawn = $this->getSpawnLocation(); $spawn = $this->getSpawnLocation();
} }
$max = $this->worldHeight; $max = $this->maxY;
$v = $spawn->floor(); $v = $spawn->floor();
$chunk = $this->getOrLoadChunkAtPosition($v); $chunk = $this->getOrLoadChunkAtPosition($v);
if($chunk === null){ if($chunk === null){
@ -2491,7 +2494,7 @@ class World implements ChunkManager{
$z = (int) $v->z; $z = (int) $v->z;
$y = (int) min($max - 2, $v->y); $y = (int) min($max - 2, $v->y);
$wasAir = $this->getBlockAt($x, $y - 1, $z)->getId() === BlockLegacyIds::AIR; //TODO: bad hack, clean up $wasAir = $this->getBlockAt($x, $y - 1, $z)->getId() === BlockLegacyIds::AIR; //TODO: bad hack, clean up
for(; $y > 0; --$y){ for(; $y > $this->minY; --$y){
if($this->getBlockAt($x, $y, $z)->isFullCube()){ if($this->getBlockAt($x, $y, $z)->isFullCube()){
if($wasAir){ if($wasAir){
$y++; $y++;
@ -2502,7 +2505,7 @@ class World implements ChunkManager{
} }
} }
for(; $y >= 0 and $y < $max; ++$y){ for(; $y >= $this->minY and $y < $max; ++$y){
if(!$this->getBlockAt($x, $y + 1, $z)->isFullCube()){ if(!$this->getBlockAt($x, $y + 1, $z)->isFullCube()){
if(!$this->getBlockAt($x, $y, $z)->isFullCube()){ if(!$this->getBlockAt($x, $y, $z)->isFullCube()){
return new Position($spawn->x, $y === (int) $spawn->y ? $spawn->y : $y, $spawn->z, $this); return new Position($spawn->x, $y === (int) $spawn->y ? $spawn->y : $y, $spawn->z, $this);
@ -2575,8 +2578,12 @@ class World implements ChunkManager{
return $this->provider->getWorldData()->getSeed(); return $this->provider->getWorldData()->getSeed();
} }
public function getWorldHeight() : int{ public function getMinY() : int{
return $this->worldHeight; return $this->minY;
}
public function getMaxY() : int{
return $this->maxY;
} }
public function getDifficulty() : int{ public function getDifficulty() : int{

View File

@ -131,17 +131,17 @@ class Chunk{
* @param int $x 0-15 * @param int $x 0-15
* @param int $z 0-15 * @param int $z 0-15
* *
* @return int 0-255, or -1 if there are no blocks in the column * @return int|null 0-255, or null if there are no blocks in the column
*/ */
public function getHighestBlockAt(int $x, int $z) : int{ public function getHighestBlockAt(int $x, int $z) : ?int{
for($y = $this->subChunks->count() - 1; $y >= 0; --$y){ for($y = $this->subChunks->count() - 1; $y >= 0; --$y){
$height = $this->getSubChunk($y)->getHighestBlockAt($x, $z) | ($y << 4); $height = $this->getSubChunk($y)->getHighestBlockAt($x, $z);
if($height !== -1){ if($height !== null){
return $height; return $height | ($y << 4);
} }
} }
return -1; return null;
} }
/** /**

View File

@ -96,9 +96,9 @@ class SubChunk{
return $this->blockLayers; return $this->blockLayers;
} }
public function getHighestBlockAt(int $x, int $z) : int{ public function getHighestBlockAt(int $x, int $z) : ?int{
if(count($this->blockLayers) === 0){ if(count($this->blockLayers) === 0){
return -1; return null;
} }
for($y = 15; $y >= 0; --$y){ for($y = 15; $y >= 0; --$y){
if($this->blockLayers[0]->get($x, $y, $z) !== $this->emptyBlockId){ if($this->blockLayers[0]->get($x, $y, $z) !== $this->emptyBlockId){
@ -106,7 +106,7 @@ class SubChunk{
} }
} }
return -1; //highest block not in this subchunk return null; //highest block not in this subchunk
} }
public function getBlockSkyLightArray() : LightArray{ public function getBlockSkyLightArray() : LightArray{

View File

@ -36,10 +36,15 @@ interface WorldProvider{
*/ */
public function __construct(string $path); public function __construct(string $path);
/**
* Returns the lowest buildable Y coordinate of this world
*/
public function getWorldMinY() : int;
/** /**
* Gets the build height limit of this world * Gets the build height limit of this world
*/ */
public function getWorldHeight() : int; public function getWorldMaxY() : int;
public function getPath() : string; public function getPath() : string;

View File

@ -132,7 +132,11 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
return new BedrockWorldData($this->getPath() . DIRECTORY_SEPARATOR . "level.dat"); return new BedrockWorldData($this->getPath() . DIRECTORY_SEPARATOR . "level.dat");
} }
public function getWorldHeight() : int{ public function getWorldMinY() : int{
return 0;
}
public function getWorldMaxY() : int{
return 256; return 256;
} }

View File

@ -51,7 +51,11 @@ class Anvil extends RegionWorldProvider{
return 19133; return 19133;
} }
public function getWorldHeight() : int{ public function getWorldMinY() : int{
return 0;
}
public function getWorldMaxY() : int{
//TODO: add world height options //TODO: add world height options
return 256; return 256;
} }

View File

@ -106,7 +106,11 @@ class McRegion extends RegionWorldProvider{
return 19132; return 19132;
} }
public function getWorldHeight() : int{ public function getWorldMinY() : int{
return 0;
}
public function getWorldMaxY() : int{
//TODO: add world height options //TODO: add world height options
return 128; return 128;
} }

View File

@ -50,7 +50,11 @@ class PMAnvil extends RegionWorldProvider{
return -1; //Not a PC format, only PocketMine-MP return -1; //Not a PC format, only PocketMine-MP
} }
public function getWorldHeight() : int{ public function getWorldMinY() : int{
return 0;
}
public function getWorldMaxY() : int{
return 256; return 256;
} }
} }

View File

@ -42,7 +42,9 @@ class GeneratorRegisterTask extends AsyncTask{
/** @var int */ /** @var int */
public $worldId; public $worldId;
/** @var int */ /** @var int */
public $worldHeight = World::Y_MAX; public $worldMinY;
/** @var int */
public $worldMaxY;
/** /**
* @param mixed[] $generatorSettings * @param mixed[] $generatorSettings
@ -54,7 +56,8 @@ class GeneratorRegisterTask extends AsyncTask{
$this->settings = igbinary_serialize($generatorSettings); $this->settings = igbinary_serialize($generatorSettings);
$this->seed = $world->getSeed(); $this->seed = $world->getSeed();
$this->worldId = $world->getId(); $this->worldId = $world->getId();
$this->worldHeight = $world->getWorldHeight(); $this->worldMinY = $world->getMinY();
$this->worldMaxY = $world->getMaxY();
} }
public function onRun() : void{ public function onRun() : void{
@ -63,6 +66,6 @@ class GeneratorRegisterTask extends AsyncTask{
* @see Generator::__construct() * @see Generator::__construct()
*/ */
$generator = new $this->generatorClass($this->seed, igbinary_unserialize($this->settings)); $generator = new $this->generatorClass($this->seed, igbinary_unserialize($this->settings));
ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldHeight), $this->worldId); ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldMinY, $this->worldMaxY), $this->worldId);
} }
} }

View File

@ -83,7 +83,7 @@ class PopulationTask extends AsyncTask{
throw new AssumptionFailedError("Generator context should have been initialized before any PopulationTask execution"); throw new AssumptionFailedError("Generator context should have been initialized before any PopulationTask execution");
} }
$generator = $context->getGenerator(); $generator = $context->getGenerator();
$manager = new SimpleChunkManager($context->getWorldHeight()); $manager = new SimpleChunkManager($context->getWorldMinY(), $context->getWorldMaxY());
/** @var Chunk[] $chunks */ /** @var Chunk[] $chunks */
$chunks = []; $chunks = [];

View File

@ -47,15 +47,21 @@ final class ThreadLocalGeneratorContext{
/** @var Generator */ /** @var Generator */
private $generator; private $generator;
/** @var int */
private $worldHeight;
public function __construct(Generator $generator, int $worldHeight){ /** @var int */
private $worldMinY;
/** @var int */
private $worldMaxY;
public function __construct(Generator $generator, int $worldMinY, int $worldMaxY){
$this->generator = $generator; $this->generator = $generator;
$this->worldHeight = $worldHeight; $this->worldMinY = $worldMinY;
$this->worldMaxY = $worldMaxY;
} }
public function getGenerator() : Generator{ return $this->generator; } public function getGenerator() : Generator{ return $this->generator; }
public function getWorldHeight() : int{ return $this->worldHeight; } public function getWorldMinY() : int{ return $this->worldMinY; }
public function getWorldMaxY() : int{ return $this->worldMaxY; }
} }

View File

@ -62,7 +62,7 @@ class LightPopulationTask extends AsyncTask{
public function onRun() : void{ public function onRun() : void{
$chunk = FastChunkSerializer::deserialize($this->chunk); $chunk = FastChunkSerializer::deserialize($this->chunk);
$manager = new SimpleChunkManager(); $manager = new SimpleChunkManager(World::Y_MIN, World::Y_MAX);
$manager->setChunk($this->chunkX, $this->chunkZ, $chunk); $manager->setChunk($this->chunkX, $this->chunkZ, $chunk);
$blockFactory = BlockFactory::getInstance(); $blockFactory = BlockFactory::getInstance();

View File

@ -178,7 +178,7 @@ class SkyLightUpdate extends LightUpdate{
break; break;
} }
} }
$result = HeightArray::fill(0); $result = HeightArray::fill(World::Y_MIN);
if($maxSubChunkY === -1){ //whole column is definitely empty if($maxSubChunkY === -1){ //whole column is definitely empty
return $result; return $result;
} }
@ -188,16 +188,16 @@ class SkyLightUpdate extends LightUpdate{
$y = null; $y = null;
for($subChunkY = $maxSubChunkY; $subChunkY >= 0; $subChunkY--){ for($subChunkY = $maxSubChunkY; $subChunkY >= 0; $subChunkY--){
$subHighestBlockY = $chunk->getSubChunk($subChunkY)->getHighestBlockAt($x, $z); $subHighestBlockY = $chunk->getSubChunk($subChunkY)->getHighestBlockAt($x, $z);
if($subHighestBlockY !== -1){ if($subHighestBlockY !== null){
$y = ($subChunkY * 16) + $subHighestBlockY; $y = ($subChunkY * 16) + $subHighestBlockY;
break; break;
} }
} }
if($y === null){ //no blocks in the column if($y === null){ //no blocks in the column
$result->set($x, $z, 0); $result->set($x, $z, World::Y_MIN);
}else{ }else{
for(; $y >= 0; --$y){ for(; $y >= World::Y_MIN; --$y){
if($directSkyLightBlockers[$chunk->getFullBlock($x, $y, $z)]){ if($directSkyLightBlockers[$chunk->getFullBlock($x, $y, $z)]){
$result->set($x, $z, $y + 1); $result->set($x, $z, $y + 1);
break; break;
@ -221,7 +221,10 @@ class SkyLightUpdate extends LightUpdate{
*/ */
private static function recalculateHeightMapColumn(Chunk $chunk, int $x, int $z, \SplFixedArray $directSkyLightBlockers) : int{ private static function recalculateHeightMapColumn(Chunk $chunk, int $x, int $z, \SplFixedArray $directSkyLightBlockers) : int{
$y = $chunk->getHighestBlockAt($x, $z); $y = $chunk->getHighestBlockAt($x, $z);
for(; $y >= 0; --$y){ if($y === null){
return World::Y_MIN;
}
for(; $y >= World::Y_MIN; --$y){
if($directSkyLightBlockers[$chunk->getFullBlock($x, $y, $z)]){ if($directSkyLightBlockers[$chunk->getFullBlock($x, $y, $z)]){
break; break;
} }