mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-08-11 05:52:05 +00:00
Refactor SubChunk to use separate block and liquid layer fields
Replace blockLayers array with individual blockLayer and liquidLayer fields. Update constructor and all usages throughout the codebase. Deprecate getBlockLayers() method for backward compatibility.
This commit is contained in:
parent
7ea0f2ff43
commit
8005c74681
@ -112,7 +112,15 @@ final class ChunkSerializer{
|
||||
}
|
||||
|
||||
public static function serializeSubChunk(SubChunk $subChunk, BlockTranslator $blockTranslator, PacketSerializer $stream, bool $persistentBlockStates) : void{
|
||||
$layers = $subChunk->getBlockLayers();
|
||||
// Create array from the new methods to minimize code changes
|
||||
$layers = [];
|
||||
if(($blockLayer = $subChunk->getBlockLayer()) !== null){
|
||||
$layers[] = $blockLayer;
|
||||
}
|
||||
if(($liquidLayer = $subChunk->getLiquidLayer()) !== null){
|
||||
$layers[] = $liquidLayer;
|
||||
}
|
||||
|
||||
$stream->putByte(8); //version
|
||||
|
||||
$stream->putByte(count($layers));
|
||||
|
@ -73,7 +73,7 @@ class Chunk{
|
||||
|
||||
foreach($this->subChunks as $y => $null){
|
||||
//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;
|
||||
@ -298,7 +298,7 @@ class Chunk{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -33,13 +33,11 @@ class SubChunk{
|
||||
|
||||
/**
|
||||
* SubChunk constructor.
|
||||
*
|
||||
* @param PalettedBlockArray[] $blockLayers
|
||||
* @phpstan-param list<PalettedBlockArray> $blockLayers
|
||||
*/
|
||||
public function __construct(
|
||||
private int $emptyBlockId,
|
||||
private array $blockLayers,
|
||||
private ?PalettedBlockArray $blockLayer,
|
||||
private ?PalettedBlockArray $liquidLayer,
|
||||
private PalettedBlockArray $biomes,
|
||||
private ?LightArray $skyLight = null,
|
||||
private ?LightArray $blockLight = null
|
||||
@ -60,7 +58,7 @@ class SubChunk{
|
||||
* This may report non-empty erroneously if the chunk has been modified and not garbage-collected.
|
||||
*/
|
||||
public function isEmptyFast() : bool{
|
||||
return count($this->blockLayers) === 0;
|
||||
return $this->blockLayer === null && $this->liquidLayer === null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,33 +68,57 @@ class SubChunk{
|
||||
public function getEmptyBlockId() : int{ return $this->emptyBlockId; }
|
||||
|
||||
public function getBlockStateId(int $x, int $y, int $z) : int{
|
||||
if(count($this->blockLayers) === 0){
|
||||
if($this->blockLayer === null){
|
||||
return $this->emptyBlockId;
|
||||
}
|
||||
return $this->blockLayers[0]->get($x, $y, $z);
|
||||
return $this->blockLayer->get($x, $y, $z);
|
||||
}
|
||||
|
||||
public function setBlockStateId(int $x, int $y, int $z, int $block) : void{
|
||||
if(count($this->blockLayers) === 0){
|
||||
$this->blockLayers[] = new PalettedBlockArray($this->emptyBlockId);
|
||||
if($this->blockLayer === null){
|
||||
$this->blockLayer = new PalettedBlockArray($this->emptyBlockId);
|
||||
}
|
||||
$this->blockLayers[0]->set($x, $y, $z, $block);
|
||||
$this->blockLayer->set($x, $y, $z, $block);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use getBlockLayer() and getLiquidLayer() instead
|
||||
* @return PalettedBlockArray[]
|
||||
* @phpstan-return list<PalettedBlockArray>
|
||||
*/
|
||||
public function getBlockLayers() : array{
|
||||
return $this->blockLayers;
|
||||
$layers = [];
|
||||
if($this->blockLayer !== null){
|
||||
$layers[] = $this->blockLayer;
|
||||
}
|
||||
if($this->liquidLayer !== null){
|
||||
$layers[] = $this->liquidLayer;
|
||||
}
|
||||
return $layers;
|
||||
}
|
||||
|
||||
public function getBlockLayer() : ?PalettedBlockArray{
|
||||
return $this->blockLayer;
|
||||
}
|
||||
|
||||
public function setBlockLayer(?PalettedBlockArray $blockLayer) : void{
|
||||
$this->blockLayer = $blockLayer;
|
||||
}
|
||||
|
||||
public function getLiquidLayer() : ?PalettedBlockArray{
|
||||
return $this->liquidLayer;
|
||||
}
|
||||
|
||||
public function setLiquidLayer(?PalettedBlockArray $liquidLayer) : void{
|
||||
$this->liquidLayer = $liquidLayer;
|
||||
}
|
||||
|
||||
public function getHighestBlockAt(int $x, int $z) : ?int{
|
||||
if(count($this->blockLayers) === 0){
|
||||
if($this->blockLayer === null){
|
||||
return null;
|
||||
}
|
||||
for($y = self::EDGE_LENGTH - 1; $y >= 0; --$y){
|
||||
if($this->blockLayers[0]->get($x, $y, $z) !== $this->emptyBlockId){
|
||||
if($this->blockLayer->get($x, $y, $z) !== $this->emptyBlockId){
|
||||
return $y;
|
||||
}
|
||||
}
|
||||
@ -130,15 +152,18 @@ class SubChunk{
|
||||
}
|
||||
|
||||
public function collectGarbage() : void{
|
||||
$cleanedLayers = [];
|
||||
foreach($this->blockLayers as $layer){
|
||||
$layer->collectGarbage();
|
||||
|
||||
if($layer->getBitsPerBlock() !== 0 || $layer->get(0, 0, 0) !== $this->emptyBlockId){
|
||||
$cleanedLayers[] = $layer;
|
||||
if($this->blockLayer !== null){
|
||||
$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();
|
||||
if($this->liquidLayer->getBitsPerBlock() === 0 && $this->liquidLayer->get(0, 0, 0) === $this->emptyBlockId){
|
||||
$this->liquidLayer = null;
|
||||
}
|
||||
}
|
||||
$this->blockLayers = $cleanedLayers;
|
||||
$this->biomes->collectGarbage();
|
||||
|
||||
if($this->skyLight !== null && $this->skyLight->isUniform(0)){
|
||||
@ -150,9 +175,12 @@ class SubChunk{
|
||||
}
|
||||
|
||||
public function __clone(){
|
||||
$this->blockLayers = array_map(function(PalettedBlockArray $array) : PalettedBlockArray{
|
||||
return clone $array;
|
||||
}, $this->blockLayers);
|
||||
if($this->blockLayer !== null){
|
||||
$this->blockLayer = clone $this->blockLayer;
|
||||
}
|
||||
if($this->liquidLayer !== null){
|
||||
$this->liquidLayer = clone $this->liquidLayer;
|
||||
}
|
||||
$this->biomes = clone $this->biomes;
|
||||
|
||||
if($this->skyLight !== null){
|
||||
|
@ -74,11 +74,21 @@ final class FastChunkSerializer{
|
||||
foreach($subChunks as $y => $subChunk){
|
||||
$stream->putByte($y);
|
||||
$stream->putInt($subChunk->getEmptyBlockId());
|
||||
$layers = $subChunk->getBlockLayers();
|
||||
$stream->putByte(count($layers));
|
||||
foreach($layers as $blocks){
|
||||
self::serializePalettedArray($stream, $blocks);
|
||||
|
||||
// Write block layer presence and data
|
||||
$blockLayer = $subChunk->getBlockLayer();
|
||||
$stream->putByte($blockLayer !== null ? 1 : 0);
|
||||
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());
|
||||
|
||||
}
|
||||
@ -112,12 +122,20 @@ final class FastChunkSerializer{
|
||||
$y = Binary::signByte($stream->getByte());
|
||||
$airBlockId = $stream->getInt();
|
||||
|
||||
$layers = [];
|
||||
for($i = 0, $layerCount = $stream->getByte(); $i < $layerCount; ++$i){
|
||||
$layers[] = self::deserializePalettedArray($stream);
|
||||
// Read block layer
|
||||
$blockLayer = null;
|
||||
if($stream->getByte() !== 0){
|
||||
$blockLayer = self::deserializePalettedArray($stream);
|
||||
}
|
||||
|
||||
// Read liquid layer
|
||||
$liquidLayer = null;
|
||||
if($stream->getByte() !== 0){
|
||||
$liquidLayer = self::deserializePalettedArray($stream);
|
||||
}
|
||||
|
||||
$biomeArray = self::deserializePalettedArray($stream);
|
||||
$subChunks[$y] = new SubChunk($airBlockId, $layers, $biomeArray);
|
||||
$subChunks[$y] = new SubChunk($airBlockId, $blockLayer, $liquidLayer, $biomeArray);
|
||||
}
|
||||
|
||||
return new Chunk($subChunks, $terrainPopulated);
|
||||
|
@ -462,17 +462,15 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
|
||||
$subChunks = [];
|
||||
for($yy = 0; $yy < 8; ++$yy){
|
||||
$storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy, new \PrefixedLogger($logger, "Subchunk y=$yy"))];
|
||||
if(isset($convertedLegacyExtraData[$yy])){
|
||||
$storages[] = $convertedLegacyExtraData[$yy];
|
||||
}
|
||||
$subChunks[$yy] = new SubChunk(Block::EMPTY_STATE_ID, $storages, clone $biomes3d);
|
||||
$blockLayer = $this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy, new \PrefixedLogger($logger, "Subchunk y=$yy"));
|
||||
$liquidLayer = $convertedLegacyExtraData[$yy] ?? null;
|
||||
$subChunks[$yy] = new SubChunk(Block::EMPTY_STATE_ID, $blockLayer, $liquidLayer, clone $biomes3d);
|
||||
}
|
||||
|
||||
//make sure extrapolated biomes get filled in correctly
|
||||
for($yy = Chunk::MIN_SUBCHUNK_INDEX; $yy <= Chunk::MAX_SUBCHUNK_INDEX; ++$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)];
|
||||
if($convertedLegacyExtraData !== null){
|
||||
$storages[] = $convertedLegacyExtraData;
|
||||
}
|
||||
$blockLayer = $this->palettizeLegacySubChunkXZY($blocks, $blockData, $logger);
|
||||
$liquidLayer = $convertedLegacyExtraData;
|
||||
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $storages, $biomePalette);
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $blockLayer, $liquidLayer, $biomePalette);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -526,11 +522,9 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
case SubChunkVersion::CLASSIC_BUG_7:
|
||||
return $this->deserializeNonPalettedSubChunkData($binaryStream, $chunkVersion, $convertedLegacyExtraData, $biomePalette, $logger);
|
||||
case SubChunkVersion::PALETTED_SINGLE:
|
||||
$storages = [$this->deserializeBlockPalette($binaryStream, $logger)];
|
||||
if($convertedLegacyExtraData !== null){
|
||||
$storages[] = $convertedLegacyExtraData;
|
||||
}
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $storages, $biomePalette);
|
||||
$blockLayer = $this->deserializeBlockPalette($binaryStream, $logger);
|
||||
$liquidLayer = $convertedLegacyExtraData;
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $blockLayer, $liquidLayer, $biomePalette);
|
||||
case SubChunkVersion::PALETTED_MULTI:
|
||||
case SubChunkVersion::PALETTED_MULTI_WITH_OFFSET:
|
||||
//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();
|
||||
}
|
||||
|
||||
$storages = [];
|
||||
$blockLayer = null;
|
||||
$liquidLayer = null;
|
||||
for($k = 0; $k < $storageCount; ++$k){
|
||||
$storages[] = $this->deserializeBlockPalette($binaryStream, $logger);
|
||||
$layer = $this->deserializeBlockPalette($binaryStream, $logger);
|
||||
if($k === 0){
|
||||
$blockLayer = $layer;
|
||||
}elseif($k === 1){
|
||||
$liquidLayer = $layer;
|
||||
}
|
||||
// Ignore additional layers beyond the first two
|
||||
}
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $storages, $biomePalette);
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $blockLayer, $liquidLayer, $biomePalette);
|
||||
default:
|
||||
//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");
|
||||
@ -575,7 +576,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
$subChunkKeyOffset = self::hasOffsetCavesAndCliffsSubChunks($chunkVersion) ? self::CAVES_CLIFFS_EXPERIMENTAL_SUBCHUNK_KEY_OFFSET : 0;
|
||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
|
||||
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;
|
||||
}
|
||||
|
||||
@ -774,10 +775,20 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
||||
$subStream = new BinaryStream();
|
||||
$subStream->putByte(self::CURRENT_LEVEL_SUBCHUNK_VERSION);
|
||||
|
||||
$layers = $subChunk->getBlockLayers();
|
||||
$subStream->putByte(count($layers));
|
||||
foreach($layers as $blocks){
|
||||
$this->serializeBlockPalette($subStream, $blocks);
|
||||
$layerCount = 0;
|
||||
$blockLayer = $subChunk->getBlockLayer();
|
||||
$liquidLayer = $subChunk->getLiquidLayer();
|
||||
|
||||
if($blockLayer !== null) $layerCount++;
|
||||
if($liquidLayer !== null) $layerCount++;
|
||||
|
||||
$subStream->putByte($layerCount);
|
||||
|
||||
if($blockLayer !== null){
|
||||
$this->serializeBlockPalette($subStream, $blockLayer);
|
||||
}
|
||||
if($liquidLayer !== null){
|
||||
$this->serializeBlockPalette($subStream, $liquidLayer);
|
||||
}
|
||||
|
||||
$write->put($key, $subStream->getBuffer());
|
||||
|
@ -32,11 +32,12 @@ class Anvil extends RegionWorldProvider{
|
||||
use LegacyAnvilChunkTrait;
|
||||
|
||||
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkYZX(
|
||||
$blockLayer = $this->palettizeLegacySubChunkYZX(
|
||||
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
|
||||
self::readFixedSizeByteArray($subChunk, "Data", 2048),
|
||||
$logger
|
||||
)], $biomes3d);
|
||||
);
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $blockLayer, null, $biomes3d);
|
||||
//ignore legacy light information
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ trait LegacyAnvilChunkTrait{
|
||||
}
|
||||
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$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);
|
||||
|
||||
for($y = 0; $y < 8; ++$y){
|
||||
$subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn(
|
||||
$blockLayer = $this->palettizeLegacySubChunkFromColumn(
|
||||
$fullIds,
|
||||
$fullData,
|
||||
$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){
|
||||
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,12 @@ class PMAnvil extends RegionWorldProvider{
|
||||
use LegacyAnvilChunkTrait;
|
||||
|
||||
protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkXZY(
|
||||
$blockLayer = $this->palettizeLegacySubChunkXZY(
|
||||
self::readFixedSizeByteArray($subChunk, "Blocks", 4096),
|
||||
self::readFixedSizeByteArray($subChunk, "Data", 2048),
|
||||
$logger
|
||||
)], $biomes3d);
|
||||
);
|
||||
return new SubChunk(Block::EMPTY_STATE_ID, $blockLayer, null, $biomes3d);
|
||||
}
|
||||
|
||||
protected static function getRegionFileExtension() : string{
|
||||
|
@ -65,11 +65,29 @@ class BlockLightUpdate extends LightUpdate{
|
||||
foreach($chunk->getSubChunks() as $subChunkY => $subChunk){
|
||||
$subChunk->setBlockLightArray(LightArray::fill(0));
|
||||
|
||||
foreach($subChunk->getBlockLayers() as $layer){
|
||||
foreach($layer->getPalette() as $state){
|
||||
$foundLightEmitter = false;
|
||||
|
||||
// Check block layer for light emitters
|
||||
$blockLayer = $subChunk->getBlockLayer();
|
||||
if($blockLayer !== null){
|
||||
foreach($blockLayer->getPalette() as $state){
|
||||
if(($this->lightEmitters[$state] ?? 0) > 0){
|
||||
$lightSources += $this->scanForLightEmittingBlocks($subChunk, $chunkX << SubChunk::COORD_BIT_SIZE, $subChunkY << SubChunk::COORD_BIT_SIZE, $chunkZ << SubChunk::COORD_BIT_SIZE);
|
||||
break 2;
|
||||
$foundLightEmitter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check liquid layer for light emitters if not found in block layer
|
||||
if(!$foundLightEmitter){
|
||||
$liquidLayer = $subChunk->getLiquidLayer();
|
||||
if($liquidLayer !== null){
|
||||
foreach($liquidLayer->getPalette() as $state){
|
||||
if(($this->lightEmitters[$state] ?? 0) > 0){
|
||||
$lightSources += $this->scanForLightEmittingBlocks($subChunk, $chunkX << SubChunk::COORD_BIT_SIZE, $subChunkY << SubChunk::COORD_BIT_SIZE, $chunkZ << SubChunk::COORD_BIT_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class SubChunkTest extends TestCase{
|
||||
* Test that a cloned SubChunk instance doesn't influence the original
|
||||
*/
|
||||
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->getBlockLightArray()->set(0, 0, 0, 1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user