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()`.
- 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->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:
- `World->getSafeSpawn()` (previously it just silently returned the input position)
- `World->getHighestBlockAt()` (previously it returned -1)

View File

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

View File

@ -45,9 +45,14 @@ interface ChunkManager{
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

View File

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

View File

@ -174,7 +174,9 @@ class World implements ChunkManager{
private $providerGarbageCollectionTicker = 0;
/** @var int */
private $worldHeight;
private $minY;
/** @var int */
private $maxY;
/** @var ChunkLoader[] */
private $loaders = [];
@ -383,7 +385,8 @@ class World implements ChunkManager{
$this->displayName = $this->provider->getWorldData()->getName();
$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->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{
return (
$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
);
}
@ -2089,10 +2092,10 @@ class World implements ChunkManager{
/**
* 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
*/
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){
return $chunk->getHighestBlockAt($x & 0x0f, $z & 0x0f);
}
@ -2481,7 +2484,7 @@ class World implements ChunkManager{
$spawn = $this->getSpawnLocation();
}
$max = $this->worldHeight;
$max = $this->maxY;
$v = $spawn->floor();
$chunk = $this->getOrLoadChunkAtPosition($v);
if($chunk === null){
@ -2491,7 +2494,7 @@ class World implements ChunkManager{
$z = (int) $v->z;
$y = (int) min($max - 2, $v->y);
$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($wasAir){
$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, $z)->isFullCube()){
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();
}
public function getWorldHeight() : int{
return $this->worldHeight;
public function getMinY() : int{
return $this->minY;
}
public function getMaxY() : int{
return $this->maxY;
}
public function getDifficulty() : int{

View File

@ -131,17 +131,17 @@ class Chunk{
* @param int $x 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){
$height = $this->getSubChunk($y)->getHighestBlockAt($x, $z) | ($y << 4);
if($height !== -1){
return $height;
$height = $this->getSubChunk($y)->getHighestBlockAt($x, $z);
if($height !== null){
return $height | ($y << 4);
}
}
return -1;
return null;
}
/**

View File

@ -96,9 +96,9 @@ class SubChunk{
return $this->blockLayers;
}
public function getHighestBlockAt(int $x, int $z) : int{
public function getHighestBlockAt(int $x, int $z) : ?int{
if(count($this->blockLayers) === 0){
return -1;
return null;
}
for($y = 15; $y >= 0; --$y){
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{

View File

@ -36,10 +36,15 @@ interface WorldProvider{
*/
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
*/
public function getWorldHeight() : int;
public function getWorldMaxY() : int;
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");
}
public function getWorldHeight() : int{
public function getWorldMinY() : int{
return 0;
}
public function getWorldMaxY() : int{
return 256;
}

View File

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

View File

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

View File

@ -50,7 +50,11 @@ class PMAnvil extends RegionWorldProvider{
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;
}
}

View File

@ -42,7 +42,9 @@ class GeneratorRegisterTask extends AsyncTask{
/** @var int */
public $worldId;
/** @var int */
public $worldHeight = World::Y_MAX;
public $worldMinY;
/** @var int */
public $worldMaxY;
/**
* @param mixed[] $generatorSettings
@ -54,7 +56,8 @@ class GeneratorRegisterTask extends AsyncTask{
$this->settings = igbinary_serialize($generatorSettings);
$this->seed = $world->getSeed();
$this->worldId = $world->getId();
$this->worldHeight = $world->getWorldHeight();
$this->worldMinY = $world->getMinY();
$this->worldMaxY = $world->getMaxY();
}
public function onRun() : void{
@ -63,6 +66,6 @@ class GeneratorRegisterTask extends AsyncTask{
* @see Generator::__construct()
*/
$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");
}
$generator = $context->getGenerator();
$manager = new SimpleChunkManager($context->getWorldHeight());
$manager = new SimpleChunkManager($context->getWorldMinY(), $context->getWorldMaxY());
/** @var Chunk[] $chunks */
$chunks = [];

View File

@ -47,15 +47,21 @@ final class ThreadLocalGeneratorContext{
/** @var 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->worldHeight = $worldHeight;
$this->worldMinY = $worldMinY;
$this->worldMaxY = $worldMaxY;
}
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{
$chunk = FastChunkSerializer::deserialize($this->chunk);
$manager = new SimpleChunkManager();
$manager = new SimpleChunkManager(World::Y_MIN, World::Y_MAX);
$manager->setChunk($this->chunkX, $this->chunkZ, $chunk);
$blockFactory = BlockFactory::getInstance();

View File

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