Chunks no longer exist in un-generated state

a non-generated chunk is now always represented by NULL. This forces the case of ungenerated chunks to be handled by all code, which is necessary because ungenerated chunks cannot be interacted with or modified in any meaningful way.
This commit is contained in:
Dylan K. Taylor 2020-12-03 22:28:43 +00:00
parent 05ab44f768
commit b9cd633cee
10 changed files with 59 additions and 97 deletions

View File

@ -936,7 +936,6 @@ class World implements ChunkManager{
$hash = World::chunkHash($dx + $chunkX, $dz + $chunkZ);
if(!isset($chunkTickList[$hash]) and isset($this->chunks[$hash])){
if(
!$this->chunks[$hash]->isGenerated() ||
!$this->chunks[$hash]->isPopulated() ||
$this->isChunkLocked($dx + $chunkX, $dz + $chunkZ)
){
@ -1025,7 +1024,7 @@ class World implements ChunkManager{
$this->timings->syncChunkSaveTimer->startTiming();
try{
foreach($this->chunks as $chunkHash => $chunk){
if($chunk->isDirty() and $chunk->isGenerated()){
if($chunk->isDirty()){
self::getXZ($chunkHash, $chunkX, $chunkZ);
$this->provider->saveChunk($chunkX, $chunkZ, $chunk);
$chunk->clearDirtyFlags();
@ -2085,8 +2084,7 @@ class World implements ChunkManager{
}
public function isChunkGenerated(int $x, int $z) : bool{
$chunk = $this->loadChunk($x, $z);
return $chunk !== null ? $chunk->isGenerated() : false;
return $this->loadChunk($x, $z) !== null;
}
public function isChunkPopulated(int $x, int $z) : bool{
@ -2337,7 +2335,7 @@ class World implements ChunkManager{
return false;
}
if($trySave and $this->getAutoSave() and $chunk->isGenerated() and $chunk->isDirty()){
if($trySave and $this->getAutoSave() and $chunk->isDirty()){
$this->timings->syncChunkSaveTimer->startTiming();
try{
$this->provider->saveChunk($x, $z, $chunk);
@ -2384,7 +2382,7 @@ class World implements ChunkManager{
$max = $this->worldHeight;
$v = $spawn->floor();
$chunk = $this->getOrLoadChunkAtPosition($v);
if($chunk === null || !$chunk->isGenerated()){
if($chunk === null){
throw new WorldException("Cannot find a safe spawn point in non-generated terrain");
}
$x = (int) $v->x;

View File

@ -51,8 +51,6 @@ class Chunk{
/** @var bool|null */
protected $lightPopulated = false;
/** @var bool */
protected $terrainGenerated = false;
/** @var bool */
protected $terrainPopulated = false;
/**
@ -207,15 +205,6 @@ class Chunk{
$this->dirtyFlags |= self::DIRTY_FLAG_TERRAIN;
}
public function isGenerated() : bool{
return $this->terrainGenerated;
}
public function setGenerated(bool $value = true) : void{
$this->terrainGenerated = $value;
$this->dirtyFlags |= self::DIRTY_FLAG_TERRAIN;
}
public function addEntity(Entity $entity) : void{
if($entity->isClosed()){
throw new \InvalidArgumentException("Attempted to add a garbage closed Entity to a chunk");

View File

@ -67,9 +67,6 @@ abstract class BaseWorldProvider implements WorldProvider{
}
public function saveChunk(int $chunkX, int $chunkZ, Chunk $chunk) : void{
if(!$chunk->isGenerated()){
throw new \InvalidStateException("Cannot save un-generated chunk");
}
$this->writeChunk($chunkX, $chunkZ, $chunk);
}

View File

@ -63,44 +63,42 @@ final class FastChunkSerializer{
$stream = new BinaryStream();
$stream->putByte(
($includeLight ? self::FLAG_HAS_LIGHT : 0) |
($chunk->isPopulated() ? self::FLAG_POPULATED : 0) |
($chunk->isGenerated() ? self::FLAG_GENERATED : 0)
($chunk->isPopulated() ? self::FLAG_POPULATED : 0)
);
if($chunk->isGenerated()){
//subchunks
$subChunks = $chunk->getSubChunks();
$count = $subChunks->count();
$stream->putByte($count);
foreach($subChunks as $y => $subChunk){
$stream->putByte($y);
$stream->putInt($subChunk->getEmptyBlockId());
$layers = $subChunk->getBlockLayers();
$stream->putByte(count($layers));
foreach($layers as $blocks){
$wordArray = $blocks->getWordArray();
$palette = $blocks->getPalette();
//subchunks
$subChunks = $chunk->getSubChunks();
$count = $subChunks->count();
$stream->putByte($count);
$stream->putByte($blocks->getBitsPerBlock());
$stream->put($wordArray);
$serialPalette = pack("L*", ...$palette);
$stream->putInt(strlen($serialPalette));
$stream->put($serialPalette);
}
foreach($subChunks as $y => $subChunk){
$stream->putByte($y);
$stream->putInt($subChunk->getEmptyBlockId());
$layers = $subChunk->getBlockLayers();
$stream->putByte(count($layers));
foreach($layers as $blocks){
$wordArray = $blocks->getWordArray();
$palette = $blocks->getPalette();
if($includeLight){
$stream->put($subChunk->getBlockSkyLightArray()->getData());
$stream->put($subChunk->getBlockLightArray()->getData());
}
$stream->putByte($blocks->getBitsPerBlock());
$stream->put($wordArray);
$serialPalette = pack("L*", ...$palette);
$stream->putInt(strlen($serialPalette));
$stream->put($serialPalette);
}
//biomes
$stream->put($chunk->getBiomeIdArray());
if($includeLight){
$stream->put(pack("S*", ...$chunk->getHeightMapArray()));
$stream->put($subChunk->getBlockSkyLightArray()->getData());
$stream->put($subChunk->getBlockLightArray()->getData());
}
}
//biomes
$stream->put($chunk->getBiomeIdArray());
if($includeLight){
$stream->put(pack("S*", ...$chunk->getHeightMapArray()));
}
return $stream->getBuffer();
}
@ -113,39 +111,36 @@ final class FastChunkSerializer{
$flags = $stream->getByte();
$lightPopulated = (bool) ($flags & self::FLAG_HAS_LIGHT);
$terrainPopulated = (bool) ($flags & self::FLAG_POPULATED);
$terrainGenerated = (bool) ($flags & self::FLAG_GENERATED);
$subChunks = [];
$biomeIds = null;
$heightMap = null;
if($terrainGenerated){
$count = $stream->getByte();
for($subCount = 0; $subCount < $count; ++$subCount){
$y = $stream->getByte();
$airBlockId = $stream->getInt();
/** @var PalettedBlockArray[] $layers */
$layers = [];
for($i = 0, $layerCount = $stream->getByte(); $i < $layerCount; ++$i){
$bitsPerBlock = $stream->getByte();
$words = $stream->get(PalettedBlockArray::getExpectedWordArraySize($bitsPerBlock));
$palette = array_values(unpack("L*", $stream->get($stream->getInt())));
$count = $stream->getByte();
for($subCount = 0; $subCount < $count; ++$subCount){
$y = $stream->getByte();
$airBlockId = $stream->getInt();
$layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
}
$subChunks[$y] = new SubChunk(
$airBlockId, $layers, $lightPopulated ? new LightArray($stream->get(2048)) : null, $lightPopulated ? new LightArray($stream->get(2048)) : null
);
/** @var PalettedBlockArray[] $layers */
$layers = [];
for($i = 0, $layerCount = $stream->getByte(); $i < $layerCount; ++$i){
$bitsPerBlock = $stream->getByte();
$words = $stream->get(PalettedBlockArray::getExpectedWordArraySize($bitsPerBlock));
$palette = array_values(unpack("L*", $stream->get($stream->getInt())));
$layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
}
$subChunks[$y] = new SubChunk(
$airBlockId, $layers, $lightPopulated ? new LightArray($stream->get(2048)) : null, $lightPopulated ? new LightArray($stream->get(2048)) : null
);
}
$biomeIds = new BiomeArray($stream->get(256));
if($lightPopulated){
$heightMap = new HeightArray(array_values(unpack("S*", $stream->get(512))));
}
$biomeIds = new BiomeArray($stream->get(256));
if($lightPopulated){
$heightMap = new HeightArray(array_values(unpack("S*", $stream->get(512))));
}
$chunk = new Chunk($subChunks, null, null, $biomeIds, $heightMap);
$chunk->setGenerated($terrainGenerated);
$chunk->setPopulated($terrainPopulated);
$chunk->setLightPopulated($lightPopulated);
$chunk->clearDirtyFlags();

View File

@ -406,7 +406,6 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
//TODO: tile ticks, biome states (?)
$chunk->setGenerated();
$chunk->setPopulated();
if($hasBeenUpgraded){
$chunk->setDirty(); //trigger rewriting chunk to disk if it was converted from an older format

View File

@ -99,7 +99,6 @@ trait LegacyAnvilChunkTrait{
$biomeArray
);
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
$result->setGenerated();
return $result;
}

View File

@ -95,7 +95,6 @@ class McRegion extends RegionWorldProvider{
$biomeIds
);
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
$result->setGenerated(true);
return $result;
}

View File

@ -146,7 +146,6 @@ class Flat extends Generator{
protected function generateBaseChunk() : void{
$this->chunk = new Chunk();
$this->chunk->setGenerated();
for($Z = 0; $Z < 16; ++$Z){
for($X = 0; $X < 16; ++$X){

View File

@ -91,7 +91,7 @@ class PopulationTask extends AsyncTask{
/** @var Chunk[] $chunks */
$chunks = [];
$chunk = $this->chunk !== null ? FastChunkSerializer::deserialize($this->chunk) : new Chunk();
$chunk = $this->chunk !== null ? FastChunkSerializer::deserialize($this->chunk) : null;
for($i = 0; $i < 9; ++$i){
if($i === 4){
@ -99,27 +99,29 @@ class PopulationTask extends AsyncTask{
}
$ck = $this->{"chunk$i"};
if($ck === null){
$chunks[$i] = new Chunk();
$chunks[$i] = null;
}else{
$chunks[$i] = FastChunkSerializer::deserialize($ck);
}
}
$manager->setChunk($this->chunkX, $this->chunkZ, $chunk);
if(!$chunk->isGenerated()){
if($chunk === null){
$generator->generateChunk($manager, $this->chunkX, $this->chunkZ);
$chunk = $manager->getChunk($this->chunkX, $this->chunkZ);
$chunk->setGenerated();
$chunk->setDirtyFlag(Chunk::DIRTY_FLAG_TERRAIN, true);
$chunk->setDirtyFlag(Chunk::DIRTY_FLAG_BIOMES, true);
}
foreach($chunks as $i => $c){
$cX = (-1 + $i % 3) + $this->chunkX;
$cZ = (-1 + intdiv($i, 3)) + $this->chunkZ;
$manager->setChunk($cX, $cZ, $c);
if(!$c->isGenerated()){
if($c === null){
$generator->generateChunk($manager, $cX, $cZ);
$chunks[$i] = $manager->getChunk($cX, $cZ);
$chunks[$i]->setGenerated();
$chunks[$i]->setDirtyFlag(Chunk::DIRTY_FLAG_TERRAIN, true);
$chunks[$i]->setDirtyFlag(Chunk::DIRTY_FLAG_BIOMES, true);
}
}

View File

@ -535,16 +535,6 @@ parameters:
count: 3
path: ../../../src/world/World.php
-
message: "#^Cannot call method isPopulated\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
count: 1
path: ../../../src/world/World.php
-
message: "#^Parameter \\#2 \\$chunk of class pocketmine\\\\world\\\\generator\\\\PopulationTask constructor expects pocketmine\\\\world\\\\format\\\\Chunk, pocketmine\\\\world\\\\format\\\\Chunk\\|null given\\.$#"
count: 1
path: ../../../src/world/World.php
-
message: "#^Method pocketmine\\\\world\\\\biome\\\\BiomeRegistry\\:\\:getBiome\\(\\) should return pocketmine\\\\world\\\\biome\\\\Biome but returns pocketmine\\\\world\\\\biome\\\\Biome\\|null\\.$#"
count: 1
@ -591,13 +581,8 @@ parameters:
path: ../../../src/world/generator/PopulationTask.php
-
message: "#^Cannot call method setGenerated\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
count: 2
path: ../../../src/world/generator/PopulationTask.php
-
message: "#^Cannot call method isGenerated\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
count: 1
message: "#^Cannot call method setDirtyFlag\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#"
count: 4
path: ../../../src/world/generator/PopulationTask.php
-