mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-10-16 03:51:37 +00:00
Limit SubChunk to 2 layers, avoid arrays (#6747)
Initially proposed in #6575 This shows about a 10% performance improvement to both SubChunk->getBlockStateId() and SubChunk->setBlockStateId(), so definitely worth doing. It does result in increased complexity, but for a double digits performance gain, I think it's worth it. Closes #6575
This commit is contained in:
@@ -73,7 +73,7 @@ class Chunk{
|
|||||||
|
|
||||||
foreach($this->subChunks as $y => $null){
|
foreach($this->subChunks as $y => $null){
|
||||||
//TODO: we should probably require all subchunks to be provided here
|
//TODO: we should probably require all subchunks to be provided here
|
||||||
$this->subChunks[$y] = $subChunks[$y + self::MIN_SUBCHUNK_INDEX] ?? new SubChunk(Block::EMPTY_STATE_ID, [], new PalettedBlockArray(BiomeIds::OCEAN));
|
$this->subChunks[$y] = $subChunks[$y + self::MIN_SUBCHUNK_INDEX] ?? new SubChunk(Block::EMPTY_STATE_ID, null, null, new PalettedBlockArray(BiomeIds::OCEAN));
|
||||||
}
|
}
|
||||||
|
|
||||||
$val = (self::MAX_SUBCHUNK_INDEX + 1) * SubChunk::EDGE_LENGTH;
|
$val = (self::MAX_SUBCHUNK_INDEX + 1) * SubChunk::EDGE_LENGTH;
|
||||||
@@ -298,7 +298,7 @@ class Chunk{
|
|||||||
throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
|
throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->subChunks[$y - self::MIN_SUBCHUNK_INDEX] = $subChunk ?? new SubChunk(Block::EMPTY_STATE_ID, [], new PalettedBlockArray(BiomeIds::OCEAN));
|
$this->subChunks[$y - self::MIN_SUBCHUNK_INDEX] = $subChunk ?? new SubChunk(Block::EMPTY_STATE_ID, null, null, new PalettedBlockArray(BiomeIds::OCEAN));
|
||||||
$this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
|
$this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,9 +23,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\world\format;
|
namespace pocketmine\world\format;
|
||||||
|
|
||||||
use function array_map;
|
|
||||||
use function count;
|
|
||||||
|
|
||||||
class SubChunk{
|
class SubChunk{
|
||||||
public const COORD_BIT_SIZE = 4;
|
public const COORD_BIT_SIZE = 4;
|
||||||
public const COORD_MASK = ~(~0 << self::COORD_BIT_SIZE);
|
public const COORD_MASK = ~(~0 << self::COORD_BIT_SIZE);
|
||||||
@@ -33,13 +30,11 @@ class SubChunk{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* SubChunk constructor.
|
* SubChunk constructor.
|
||||||
*
|
|
||||||
* @param PalettedBlockArray[] $blockLayers
|
|
||||||
* @phpstan-param list<PalettedBlockArray> $blockLayers
|
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private int $emptyBlockId,
|
private int $emptyBlockId,
|
||||||
private array $blockLayers,
|
private ?PalettedBlockArray $blockLayer0,
|
||||||
|
private ?PalettedBlockArray $blockLayer1,
|
||||||
private PalettedBlockArray $biomes,
|
private PalettedBlockArray $biomes,
|
||||||
private ?LightArray $skyLight = null,
|
private ?LightArray $skyLight = null,
|
||||||
private ?LightArray $blockLight = null
|
private ?LightArray $blockLight = null
|
||||||
@@ -60,7 +55,7 @@ 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 count($this->blockLayers) === 0;
|
return $this->blockLayer0 === null && $this->blockLayer1 === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,33 +65,45 @@ 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(count($this->blockLayers) === 0){
|
return $this->blockLayer0?->get($x, $y, $z) ?? $this->emptyBlockId;
|
||||||
return $this->emptyBlockId;
|
|
||||||
}
|
|
||||||
return $this->blockLayers[0]->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(count($this->blockLayers) === 0){
|
if($this->blockLayer0 === null){
|
||||||
$this->blockLayers[] = new PalettedBlockArray($this->emptyBlockId);
|
$this->blockLayer0 = new PalettedBlockArray($this->emptyBlockId);
|
||||||
}
|
}
|
||||||
$this->blockLayers[0]->set($x, $y, $z, $block);
|
$this->blockLayer0->set($x, $y, $z, $block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlockLayer0() : ?PalettedBlockArray{
|
||||||
|
return $this->blockLayer0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlockLayer1() : ?PalettedBlockArray{
|
||||||
|
return $this->blockLayer1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return PalettedBlockArray[]
|
* @return PalettedBlockArray[]
|
||||||
* @phpstan-return list<PalettedBlockArray>
|
* @phpstan-return array{}|array{PalettedBlockArray}|array{PalettedBlockArray, PalettedBlockArray}
|
||||||
*/
|
*/
|
||||||
public function getBlockLayers() : array{
|
public function getBlockLayers() : array{
|
||||||
return $this->blockLayers;
|
$layers = [];
|
||||||
|
if($this->blockLayer0 !== null){
|
||||||
|
$layers[] = $this->blockLayer0;
|
||||||
|
}
|
||||||
|
if($this->blockLayer1 !== null){
|
||||||
|
$layers[] = $this->blockLayer1;
|
||||||
|
}
|
||||||
|
return $layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHighestBlockAt(int $x, int $z) : ?int{
|
public function getHighestBlockAt(int $x, int $z) : ?int{
|
||||||
if(count($this->blockLayers) === 0){
|
if($this->blockLayer0 === null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for($y = self::EDGE_LENGTH - 1; $y >= 0; --$y){
|
for($y = self::EDGE_LENGTH - 1; $y >= 0; --$y){
|
||||||
if($this->blockLayers[0]->get($x, $y, $z) !== $this->emptyBlockId){
|
if($this->blockLayer0->get($x, $y, $z) !== $this->emptyBlockId){
|
||||||
return $y;
|
return $y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,16 +136,21 @@ class SubChunk{
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function collectGarbage() : void{
|
private static function gcBlockPalette(?PalettedBlockArray $layer, int $emptyBlockId) : ?PalettedBlockArray{
|
||||||
$cleanedLayers = [];
|
if($layer === null){
|
||||||
foreach($this->blockLayers as $layer){
|
return null;
|
||||||
$layer->collectGarbage();
|
}
|
||||||
|
$layer->collectGarbage();
|
||||||
if($layer->getBitsPerBlock() !== 0 || $layer->get(0, 0, 0) !== $this->emptyBlockId){
|
return $layer->getBitsPerBlock() === 0 && $layer->get(0, 0, 0) === $emptyBlockId ? null : $layer;
|
||||||
$cleanedLayers[] = $layer;
|
}
|
||||||
}
|
|
||||||
|
public function collectGarbage() : void{
|
||||||
|
$this->blockLayer0 = self::gcBlockPalette($this->blockLayer0, $this->emptyBlockId);
|
||||||
|
$this->blockLayer1 = self::gcBlockPalette($this->blockLayer1, $this->emptyBlockId);
|
||||||
|
if($this->blockLayer0 === null && $this->blockLayer1 !== null){
|
||||||
|
$this->blockLayer0 = $this->blockLayer1;
|
||||||
|
$this->blockLayer1 = null;
|
||||||
}
|
}
|
||||||
$this->blockLayers = $cleanedLayers;
|
|
||||||
$this->biomes->collectGarbage();
|
$this->biomes->collectGarbage();
|
||||||
|
|
||||||
if($this->skyLight !== null && $this->skyLight->isUniform(0)){
|
if($this->skyLight !== null && $this->skyLight->isUniform(0)){
|
||||||
@@ -150,9 +162,8 @@ class SubChunk{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function __clone(){
|
public function __clone(){
|
||||||
$this->blockLayers = array_map(function(PalettedBlockArray $array) : PalettedBlockArray{
|
$this->blockLayer0 = $this->blockLayer0 !== null ? clone $this->blockLayer0 : null;
|
||||||
return clone $array;
|
$this->blockLayer1 = $this->blockLayer1 !== null ? clone $this->blockLayer1 : null;
|
||||||
}, $this->blockLayers);
|
|
||||||
$this->biomes = clone $this->biomes;
|
$this->biomes = clone $this->biomes;
|
||||||
|
|
||||||
if($this->skyLight !== null){
|
if($this->skyLight !== null){
|
||||||
|
@@ -81,7 +81,6 @@ final class FastChunkSerializer{
|
|||||||
self::serializePalettedArray($stream, $blocks);
|
self::serializePalettedArray($stream, $blocks);
|
||||||
}
|
}
|
||||||
self::serializePalettedArray($stream, $subChunk->getBiomeArray());
|
self::serializePalettedArray($stream, $subChunk->getBiomeArray());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $stream->getData();
|
return $stream->getData();
|
||||||
@@ -115,12 +114,15 @@ final class FastChunkSerializer{
|
|||||||
//TODO: why the heck are we using big-endian here?
|
//TODO: why the heck are we using big-endian here?
|
||||||
$airBlockId = BE::readUnsignedInt($stream);
|
$airBlockId = BE::readUnsignedInt($stream);
|
||||||
|
|
||||||
$layers = [];
|
$layerCount = Byte::readUnsigned($stream);
|
||||||
for($i = 0, $layerCount = Byte::readUnsigned($stream); $i < $layerCount; ++$i){
|
if($layerCount > 2){
|
||||||
$layers[] = self::deserializePalettedArray($stream);
|
throw new \UnexpectedValueException("Expected at most 2 layers, but got $layerCount");
|
||||||
}
|
}
|
||||||
|
$layer0 = $layerCount >= 1 ? self::deserializePalettedArray($stream) : null;
|
||||||
|
$layer1 = $layerCount === 2 ? self::deserializePalettedArray($stream) : null;
|
||||||
|
|
||||||
$biomeArray = self::deserializePalettedArray($stream);
|
$biomeArray = self::deserializePalettedArray($stream);
|
||||||
$subChunks[$y] = new SubChunk($airBlockId, $layers, $biomeArray);
|
$subChunks[$y] = new SubChunk($airBlockId, $layer0, $layer1, $biomeArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Chunk($subChunks, $terrainPopulated);
|
return new Chunk($subChunks, $terrainPopulated);
|
||||||
|
@@ -462,17 +462,15 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
|
|
||||||
$subChunks = [];
|
$subChunks = [];
|
||||||
for($yy = 0; $yy < 8; ++$yy){
|
for($yy = 0; $yy < 8; ++$yy){
|
||||||
$storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy, new \PrefixedLogger($logger, "Subchunk y=$yy"))];
|
$layer0 = $this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy, new \PrefixedLogger($logger, "Subchunk y=$yy"));
|
||||||
if(isset($convertedLegacyExtraData[$yy])){
|
$layer1 = $convertedLegacyExtraData[$yy] ?? null;
|
||||||
$storages[] = $convertedLegacyExtraData[$yy];
|
$subChunks[$yy] = new SubChunk(Block::EMPTY_STATE_ID, $layer0, $layer1, clone $biomes3d);
|
||||||
}
|
|
||||||
$subChunks[$yy] = new SubChunk(Block::EMPTY_STATE_ID, $storages, clone $biomes3d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//make sure extrapolated biomes get filled in correctly
|
//make sure extrapolated biomes get filled in correctly
|
||||||
for($yy = Chunk::MIN_SUBCHUNK_INDEX; $yy <= Chunk::MAX_SUBCHUNK_INDEX; ++$yy){
|
for($yy = Chunk::MIN_SUBCHUNK_INDEX; $yy <= Chunk::MAX_SUBCHUNK_INDEX; ++$yy){
|
||||||
if(!isset($subChunks[$yy])){
|
if(!isset($subChunks[$yy])){
|
||||||
$subChunks[$yy] = new SubChunk(Block::EMPTY_STATE_ID, [], clone $biomes3d);
|
$subChunks[$yy] = new SubChunk(Block::EMPTY_STATE_ID, null, null, clone $biomes3d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,12 +499,10 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$storages = [$this->palettizeLegacySubChunkXZY($blocks, $blockData, $logger)];
|
$layer0 = $this->palettizeLegacySubChunkXZY($blocks, $blockData, $logger);
|
||||||
if($convertedLegacyExtraData !== null){
|
$layer1 = $convertedLegacyExtraData;
|
||||||
$storages[] = $convertedLegacyExtraData;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SubChunk(Block::EMPTY_STATE_ID, $storages, $biomePalette);
|
return new SubChunk(Block::EMPTY_STATE_ID, $layer0, $layer1, $biomePalette);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -526,11 +522,9 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
case SubChunkVersion::CLASSIC_BUG_7:
|
case SubChunkVersion::CLASSIC_BUG_7:
|
||||||
return $this->deserializeNonPalettedSubChunkData($binaryStream, $chunkVersion, $convertedLegacyExtraData, $biomePalette, $logger);
|
return $this->deserializeNonPalettedSubChunkData($binaryStream, $chunkVersion, $convertedLegacyExtraData, $biomePalette, $logger);
|
||||||
case SubChunkVersion::PALETTED_SINGLE:
|
case SubChunkVersion::PALETTED_SINGLE:
|
||||||
$storages = [$this->deserializeBlockPalette($binaryStream, $logger)];
|
$layer0 = $this->deserializeBlockPalette($binaryStream, $logger);
|
||||||
if($convertedLegacyExtraData !== null){
|
$layer1 = $convertedLegacyExtraData;
|
||||||
$storages[] = $convertedLegacyExtraData;
|
return new SubChunk(Block::EMPTY_STATE_ID, $layer0, $layer1, $biomePalette);
|
||||||
}
|
|
||||||
return new SubChunk(Block::EMPTY_STATE_ID, $storages, $biomePalette);
|
|
||||||
case SubChunkVersion::PALETTED_MULTI:
|
case SubChunkVersion::PALETTED_MULTI:
|
||||||
case SubChunkVersion::PALETTED_MULTI_WITH_OFFSET:
|
case SubChunkVersion::PALETTED_MULTI_WITH_OFFSET:
|
||||||
//legacy extradata layers intentionally ignored because they aren't supposed to exist in v8
|
//legacy extradata layers intentionally ignored because they aren't supposed to exist in v8
|
||||||
@@ -541,11 +535,18 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
$binaryStream->getByte();
|
$binaryStream->getByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
$storages = [];
|
$layer0 = null;
|
||||||
|
$layer1 = null;
|
||||||
for($k = 0; $k < $storageCount; ++$k){
|
for($k = 0; $k < $storageCount; ++$k){
|
||||||
$storages[] = $this->deserializeBlockPalette($binaryStream, $logger);
|
$layer = $this->deserializeBlockPalette($binaryStream, $logger);
|
||||||
|
if($k === 0){
|
||||||
|
$layer0 = $layer;
|
||||||
|
}elseif($k === 1){
|
||||||
|
$layer1 = $layer;
|
||||||
|
}
|
||||||
|
// Ignore additional layers beyond the first two
|
||||||
}
|
}
|
||||||
return new SubChunk(Block::EMPTY_STATE_ID, $storages, $biomePalette);
|
return new SubChunk(Block::EMPTY_STATE_ID, $layer0, $layer1, $biomePalette);
|
||||||
default:
|
default:
|
||||||
//this should never happen - an unsupported chunk appearing in a supported world is a sign of corruption
|
//this should never happen - an unsupported chunk appearing in a supported world is a sign of corruption
|
||||||
throw new CorruptedChunkException("don't know how to decode LevelDB subchunk format version $subChunkVersion");
|
throw new CorruptedChunkException("don't know how to decode LevelDB subchunk format version $subChunkVersion");
|
||||||
@@ -575,7 +576,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
$subChunkKeyOffset = self::hasOffsetCavesAndCliffsSubChunks($chunkVersion) ? self::CAVES_CLIFFS_EXPERIMENTAL_SUBCHUNK_KEY_OFFSET : 0;
|
$subChunkKeyOffset = self::hasOffsetCavesAndCliffsSubChunks($chunkVersion) ? self::CAVES_CLIFFS_EXPERIMENTAL_SUBCHUNK_KEY_OFFSET : 0;
|
||||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
||||||
if(($data = $this->db->get($index . ChunkDataKey::SUBCHUNK . chr($y + $subChunkKeyOffset))) === false){
|
if(($data = $this->db->get($index . ChunkDataKey::SUBCHUNK . chr($y + $subChunkKeyOffset))) === false){
|
||||||
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [], $biomeArrays[$y]);
|
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, null, null, $biomeArrays[$y]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,8 +777,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
|
|
||||||
$layers = $subChunk->getBlockLayers();
|
$layers = $subChunk->getBlockLayers();
|
||||||
$subStream->putByte(count($layers));
|
$subStream->putByte(count($layers));
|
||||||
foreach($layers as $blocks){
|
foreach($layers as $layer){
|
||||||
$this->serializeBlockPalette($subStream, $blocks);
|
$this->serializeBlockPalette($subStream, $layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
$write->put($key, $subStream->getBuffer());
|
$write->put($key, $subStream->getBuffer());
|
||||||
|
@@ -32,11 +32,11 @@ class Anvil extends RegionWorldProvider{
|
|||||||
use LegacyAnvilChunkTrait;
|
use LegacyAnvilChunkTrait;
|
||||||
|
|
||||||
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
|
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
|
||||||
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkYZX(
|
return new SubChunk(Block::EMPTY_STATE_ID, $this->palettizeLegacySubChunkYZX(
|
||||||
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
|
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
|
||||||
self::readFixedSizeByteArray($subChunk, "Data", 2048),
|
self::readFixedSizeByteArray($subChunk, "Data", 2048),
|
||||||
$logger
|
$logger
|
||||||
)], $biomes3d);
|
), null, $biomes3d);
|
||||||
//ignore legacy light information
|
//ignore legacy light information
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -94,7 +94,7 @@ trait LegacyAnvilChunkTrait{
|
|||||||
}
|
}
|
||||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
||||||
if(!isset($subChunks[$y])){
|
if(!isset($subChunks[$y])){
|
||||||
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [], clone $biomes3d);
|
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, null, null, clone $biomes3d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -90,16 +90,17 @@ class McRegion extends RegionWorldProvider{
|
|||||||
$fullData = self::readFixedSizeByteArray($chunk, "Data", 16384);
|
$fullData = self::readFixedSizeByteArray($chunk, "Data", 16384);
|
||||||
|
|
||||||
for($y = 0; $y < 8; ++$y){
|
for($y = 0; $y < 8; ++$y){
|
||||||
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn(
|
$blockLayer = $this->palettizeLegacySubChunkFromColumn(
|
||||||
$fullIds,
|
$fullIds,
|
||||||
$fullData,
|
$fullData,
|
||||||
$y,
|
$y,
|
||||||
new \PrefixedLogger($logger, "Subchunk y=$y"),
|
new \PrefixedLogger($logger, "Subchunk y=$y"),
|
||||||
)], clone $biomes3d);
|
);
|
||||||
|
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, $blockLayer, null, clone $biomes3d);
|
||||||
}
|
}
|
||||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
||||||
if(!isset($subChunks[$y])){
|
if(!isset($subChunks[$y])){
|
||||||
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [], clone $biomes3d);
|
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, null, null, clone $biomes3d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -36,11 +36,11 @@ class PMAnvil extends RegionWorldProvider{
|
|||||||
use LegacyAnvilChunkTrait;
|
use LegacyAnvilChunkTrait;
|
||||||
|
|
||||||
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
|
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
|
||||||
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkXZY(
|
return new SubChunk(Block::EMPTY_STATE_ID, $this->palettizeLegacySubChunkXZY(
|
||||||
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
|
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
|
||||||
self::readFixedSizeByteArray($subChunk, "Data", 2048),
|
self::readFixedSizeByteArray($subChunk, "Data", 2048),
|
||||||
$logger
|
$logger
|
||||||
)], $biomes3d);
|
), null, $biomes3d);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function getRegionFileExtension() : string{
|
protected static function getRegionFileExtension() : string{
|
||||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\world\light;
|
namespace pocketmine\world\light;
|
||||||
|
|
||||||
use pocketmine\world\format\LightArray;
|
use pocketmine\world\format\LightArray;
|
||||||
|
use pocketmine\world\format\PalettedBlockArray;
|
||||||
use pocketmine\world\format\SubChunk;
|
use pocketmine\world\format\SubChunk;
|
||||||
use pocketmine\world\utils\SubChunkExplorer;
|
use pocketmine\world\utils\SubChunkExplorer;
|
||||||
use pocketmine\world\utils\SubChunkExplorerStatus;
|
use pocketmine\world\utils\SubChunkExplorerStatus;
|
||||||
@@ -55,6 +56,20 @@ class BlockLightUpdate extends LightUpdate{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function layerHasLightEmitter(?PalettedBlockArray $layer) : bool{
|
||||||
|
if($layer === null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($layer->getPalette() as $state){
|
||||||
|
if(($this->lightEmitters[$state] ?? 0) > 0){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function recalculateChunk(int $chunkX, int $chunkZ) : int{
|
public function recalculateChunk(int $chunkX, int $chunkZ) : int{
|
||||||
if($this->subChunkExplorer->moveToChunk($chunkX, 0, $chunkZ) === SubChunkExplorerStatus::INVALID){
|
if($this->subChunkExplorer->moveToChunk($chunkX, 0, $chunkZ) === SubChunkExplorerStatus::INVALID){
|
||||||
throw new \InvalidArgumentException("Chunk $chunkX $chunkZ does not exist");
|
throw new \InvalidArgumentException("Chunk $chunkX $chunkZ does not exist");
|
||||||
@@ -65,13 +80,11 @@ class BlockLightUpdate extends LightUpdate{
|
|||||||
foreach($chunk->getSubChunks() as $subChunkY => $subChunk){
|
foreach($chunk->getSubChunks() as $subChunkY => $subChunk){
|
||||||
$subChunk->setBlockLightArray(LightArray::fill(0));
|
$subChunk->setBlockLightArray(LightArray::fill(0));
|
||||||
|
|
||||||
foreach($subChunk->getBlockLayers() as $layer){
|
if(
|
||||||
foreach($layer->getPalette() as $state){
|
$this->layerHasLightEmitter($subChunk->getBlockLayer0()) ||
|
||||||
if(($this->lightEmitters[$state] ?? 0) > 0){
|
$this->layerHasLightEmitter($subChunk->getBlockLayer1())
|
||||||
$lightSources += $this->scanForLightEmittingBlocks($subChunk, $chunkX << SubChunk::COORD_BIT_SIZE, $subChunkY << SubChunk::COORD_BIT_SIZE, $chunkZ << SubChunk::COORD_BIT_SIZE);
|
){
|
||||||
break 2;
|
$lightSources += $this->scanForLightEmittingBlocks($subChunk, $chunkX << SubChunk::COORD_BIT_SIZE, $subChunkY << SubChunk::COORD_BIT_SIZE, $chunkZ << SubChunk::COORD_BIT_SIZE);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,7 +32,7 @@ class SubChunkTest extends TestCase{
|
|||||||
* Test that a cloned SubChunk instance doesn't influence the original
|
* Test that a cloned SubChunk instance doesn't influence the original
|
||||||
*/
|
*/
|
||||||
public function testClone() : void{
|
public function testClone() : void{
|
||||||
$sub1 = new SubChunk(0, [], new PalettedBlockArray(BiomeIds::OCEAN));
|
$sub1 = new SubChunk(0, null, null, new PalettedBlockArray(BiomeIds::OCEAN));
|
||||||
|
|
||||||
$sub1->setBlockStateId(0, 0, 0, 1);
|
$sub1->setBlockStateId(0, 0, 0, 1);
|
||||||
$sub1->getBlockLightArray()->set(0, 0, 0, 1);
|
$sub1->getBlockLightArray()->set(0, 0, 0, 1);
|
||||||
|
Reference in New Issue
Block a user