Refactor SubChunk to use non-nullable block and liquid layers

- Changed SubChunk constructor to accept null for layers and initialize empty PalettedBlockArray internally
- Updated all SubChunk getters to return non-nullable PalettedBlockArray
- Modified serialization/deserialization logic to check for layer emptiness instead of null
- Updated all SubChunk constructor calls across codebase to pass null for empty layers
- Simplified code by removing unnecessary null checks throughout the codebase
- Updated tests to match new SubChunk API
This commit is contained in:
Dylan T. 2025-06-27 15:47:58 +00:00
parent 8005c74681
commit ba2a7cceaf
2 changed files with 39 additions and 74 deletions

View File

@ -31,17 +31,31 @@ class SubChunk{
public const COORD_MASK = ~(~0 << self::COORD_BIT_SIZE); public const COORD_MASK = ~(~0 << self::COORD_BIT_SIZE);
public const EDGE_LENGTH = 1 << self::COORD_BIT_SIZE; public const EDGE_LENGTH = 1 << self::COORD_BIT_SIZE;
private int $emptyBlockId;
private PalettedBlockArray $blockLayer;
private PalettedBlockArray $liquidLayer;
private PalettedBlockArray $biomes;
private ?LightArray $skyLight;
private ?LightArray $blockLight;
/** /**
* SubChunk constructor. * SubChunk constructor.
*/ */
public function __construct( public function __construct(
private int $emptyBlockId, int $emptyBlockId,
private ?PalettedBlockArray $blockLayer, ?PalettedBlockArray $blockLayer,
private ?PalettedBlockArray $liquidLayer, ?PalettedBlockArray $liquidLayer,
private PalettedBlockArray $biomes, PalettedBlockArray $biomes,
private ?LightArray $skyLight = null, ?LightArray $skyLight = null,
private ?LightArray $blockLight = null ?LightArray $blockLight = null
){} ){
$this->emptyBlockId = $emptyBlockId;
$this->blockLayer = $blockLayer ?? new PalettedBlockArray($emptyBlockId);
$this->liquidLayer = $liquidLayer ?? new PalettedBlockArray($emptyBlockId);
$this->biomes = $biomes;
$this->skyLight = $skyLight;
$this->blockLight = $blockLight;
}
/** /**
* Returns whether this subchunk contains any non-air blocks. * Returns whether this subchunk contains any non-air blocks.
@ -58,7 +72,8 @@ class SubChunk{
* This may report non-empty erroneously if the chunk has been modified and not garbage-collected. * This may report non-empty erroneously if the chunk has been modified and not garbage-collected.
*/ */
public function isEmptyFast() : bool{ public function isEmptyFast() : bool{
return $this->blockLayer === null && $this->liquidLayer === null; return $this->blockLayer->getBitsPerBlock() === 0 && $this->blockLayer->get(0, 0, 0) === $this->emptyBlockId &&
$this->liquidLayer->getBitsPerBlock() === 0 && $this->liquidLayer->get(0, 0, 0) === $this->emptyBlockId;
} }
/** /**
@ -68,16 +83,10 @@ class SubChunk{
public function getEmptyBlockId() : int{ return $this->emptyBlockId; } public function getEmptyBlockId() : int{ return $this->emptyBlockId; }
public function getBlockStateId(int $x, int $y, int $z) : int{ public function getBlockStateId(int $x, int $y, int $z) : int{
if($this->blockLayer === null){
return $this->emptyBlockId;
}
return $this->blockLayer->get($x, $y, $z); return $this->blockLayer->get($x, $y, $z);
} }
public function setBlockStateId(int $x, int $y, int $z, int $block) : void{ public function setBlockStateId(int $x, int $y, int $z, int $block) : void{
if($this->blockLayer === null){
$this->blockLayer = new PalettedBlockArray($this->emptyBlockId);
}
$this->blockLayer->set($x, $y, $z, $block); $this->blockLayer->set($x, $y, $z, $block);
} }
@ -87,36 +96,26 @@ class SubChunk{
* @phpstan-return list<PalettedBlockArray> * @phpstan-return list<PalettedBlockArray>
*/ */
public function getBlockLayers() : array{ public function getBlockLayers() : array{
$layers = []; return [$this->blockLayer, $this->liquidLayer];
if($this->blockLayer !== null){
$layers[] = $this->blockLayer;
}
if($this->liquidLayer !== null){
$layers[] = $this->liquidLayer;
}
return $layers;
} }
public function getBlockLayer() : ?PalettedBlockArray{ public function getBlockLayer() : PalettedBlockArray{
return $this->blockLayer; return $this->blockLayer;
} }
public function setBlockLayer(?PalettedBlockArray $blockLayer) : void{ public function setBlockLayer(PalettedBlockArray $blockLayer) : void{
$this->blockLayer = $blockLayer; $this->blockLayer = $blockLayer;
} }
public function getLiquidLayer() : ?PalettedBlockArray{ public function getLiquidLayer() : PalettedBlockArray{
return $this->liquidLayer; return $this->liquidLayer;
} }
public function setLiquidLayer(?PalettedBlockArray $liquidLayer) : void{ public function setLiquidLayer(PalettedBlockArray $liquidLayer) : void{
$this->liquidLayer = $liquidLayer; $this->liquidLayer = $liquidLayer;
} }
public function getHighestBlockAt(int $x, int $z) : ?int{ public function getHighestBlockAt(int $x, int $z) : ?int{
if($this->blockLayer === null){
return null;
}
for($y = self::EDGE_LENGTH - 1; $y >= 0; --$y){ for($y = self::EDGE_LENGTH - 1; $y >= 0; --$y){
if($this->blockLayer->get($x, $y, $z) !== $this->emptyBlockId){ if($this->blockLayer->get($x, $y, $z) !== $this->emptyBlockId){
return $y; return $y;
@ -152,18 +151,8 @@ class SubChunk{
} }
public function collectGarbage() : void{ public function collectGarbage() : void{
if($this->blockLayer !== null){
$this->blockLayer->collectGarbage(); $this->blockLayer->collectGarbage();
if($this->blockLayer->getBitsPerBlock() === 0 && $this->blockLayer->get(0, 0, 0) === $this->emptyBlockId){
$this->blockLayer = null;
}
}
if($this->liquidLayer !== null){
$this->liquidLayer->collectGarbage(); $this->liquidLayer->collectGarbage();
if($this->liquidLayer->getBitsPerBlock() === 0 && $this->liquidLayer->get(0, 0, 0) === $this->emptyBlockId){
$this->liquidLayer = null;
}
}
$this->biomes->collectGarbage(); $this->biomes->collectGarbage();
if($this->skyLight !== null && $this->skyLight->isUniform(0)){ if($this->skyLight !== null && $this->skyLight->isUniform(0)){
@ -175,12 +164,8 @@ class SubChunk{
} }
public function __clone(){ public function __clone(){
if($this->blockLayer !== null){
$this->blockLayer = clone $this->blockLayer; $this->blockLayer = clone $this->blockLayer;
}
if($this->liquidLayer !== null){
$this->liquidLayer = clone $this->liquidLayer; $this->liquidLayer = clone $this->liquidLayer;
}
$this->biomes = clone $this->biomes; $this->biomes = clone $this->biomes;
if($this->skyLight !== null){ if($this->skyLight !== null){

View File

@ -75,22 +75,10 @@ final class FastChunkSerializer{
$stream->putByte($y); $stream->putByte($y);
$stream->putInt($subChunk->getEmptyBlockId()); $stream->putInt($subChunk->getEmptyBlockId());
// Write block layer presence and data // Write block and liquid layers (always present)
$blockLayer = $subChunk->getBlockLayer(); self::serializePalettedArray($stream, $subChunk->getBlockLayer());
$stream->putByte($blockLayer !== null ? 1 : 0); self::serializePalettedArray($stream, $subChunk->getLiquidLayer());
if($blockLayer !== null){
self::serializePalettedArray($stream, $blockLayer);
}
// Write liquid layer presence and data
$liquidLayer = $subChunk->getLiquidLayer();
$stream->putByte($liquidLayer !== null ? 1 : 0);
if($liquidLayer !== null){
self::serializePalettedArray($stream, $liquidLayer);
}
self::serializePalettedArray($stream, $subChunk->getBiomeArray()); self::serializePalettedArray($stream, $subChunk->getBiomeArray());
} }
return $stream->getBuffer(); return $stream->getBuffer();
@ -122,19 +110,11 @@ final class FastChunkSerializer{
$y = Binary::signByte($stream->getByte()); $y = Binary::signByte($stream->getByte());
$airBlockId = $stream->getInt(); $airBlockId = $stream->getInt();
// Read block layer // Read block and liquid layers (always present)
$blockLayer = null;
if($stream->getByte() !== 0){
$blockLayer = self::deserializePalettedArray($stream); $blockLayer = self::deserializePalettedArray($stream);
}
// Read liquid layer
$liquidLayer = null;
if($stream->getByte() !== 0){
$liquidLayer = self::deserializePalettedArray($stream); $liquidLayer = self::deserializePalettedArray($stream);
}
$biomeArray = self::deserializePalettedArray($stream); $biomeArray = self::deserializePalettedArray($stream);
$subChunks[$y] = new SubChunk($airBlockId, $blockLayer, $liquidLayer, $biomeArray); $subChunks[$y] = new SubChunk($airBlockId, $blockLayer, $liquidLayer, $biomeArray);
} }