Discard light information from disk storage

this makes world conversion faster and offers the opportunity to correct age-old lighting bugs.
This commit is contained in:
Dylan K. Taylor 2019-03-03 16:22:44 +00:00
parent 6e00ab2069
commit 1bb9b3d3ab
6 changed files with 33 additions and 89 deletions

View File

@ -2663,7 +2663,7 @@ class Level implements ChunkManager, Metadatable{
(new ChunkLoadEvent($this, $chunk, !$chunk->isGenerated()))->call(); (new ChunkLoadEvent($this, $chunk, !$chunk->isGenerated()))->call();
if(!$chunk->isLightPopulated() and $chunk->isPopulated() and $this->getServer()->getProperty("chunk-ticking.light-updates", false)){ if($chunk->isPopulated() and $this->getServer()->getProperty("chunk-ticking.light-updates", false)){
$this->getServer()->getAsyncPool()->submitTask(new LightPopulationTask($this, $chunk)); $this->getServer()->getAsyncPool()->submitTask(new LightPopulationTask($this, $chunk));
} }

View File

@ -46,7 +46,7 @@ use function file_exists;
use function is_dir; use function is_dir;
use function mkdir; use function mkdir;
use function ord; use function ord;
use function pack; use function str_repeat;
use function strlen; use function strlen;
use function substr; use function substr;
use function unpack; use function unpack;
@ -146,14 +146,9 @@ class LevelDB extends BaseLevelProvider{
/** @var SubChunk[] $subChunks */ /** @var SubChunk[] $subChunks */
$subChunks = []; $subChunks = [];
/** @var int[] $heightMap */
$heightMap = [];
/** @var string $biomeIds */ /** @var string $biomeIds */
$biomeIds = ""; $biomeIds = "";
/** @var bool $lightPopulated */
$lightPopulated = true;
$chunkVersion = ord($this->db->get($index . self::TAG_VERSION)); $chunkVersion = ord($this->db->get($index . self::TAG_VERSION));
$binaryStream = new BinaryStream(); $binaryStream = new BinaryStream();
@ -179,20 +174,15 @@ class LevelDB extends BaseLevelProvider{
try{ try{
$blocks = $binaryStream->get(4096); $blocks = $binaryStream->get(4096);
$blockData = $binaryStream->get(2048); $blockData = $binaryStream->get(2048);
if($chunkVersion < 4){ if($chunkVersion < 4){
$blockSkyLight = $binaryStream->get(2048); $binaryStream->get(4096); //legacy light info, discard it
$blockLight = $binaryStream->get(2048);
}else{
//Mojang didn't bother changing the subchunk version when they stopped saving sky light -_-
$blockSkyLight = "";
$blockLight = "";
$lightPopulated = false;
} }
}catch(BinaryDataException $e){ }catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e); throw new CorruptedChunkException($e->getMessage(), 0, $e);
} }
$subChunks[$y] = new SubChunk($blocks, $blockData, $blockSkyLight, $blockLight); $subChunks[$y] = new SubChunk($blocks, $blockData);
break; break;
default: default:
//TODO: set chunks read-only so the version on disk doesn't get overwritten //TODO: set chunks read-only so the version on disk doesn't get overwritten
@ -204,7 +194,7 @@ class LevelDB extends BaseLevelProvider{
$binaryStream->setBuffer($maps2d); $binaryStream->setBuffer($maps2d);
try{ try{
$heightMap = array_values(unpack("v*", $binaryStream->get(512))); $binaryStream->get(512); //heightmap, discard it
$biomeIds = $binaryStream->get(256); $biomeIds = $binaryStream->get(256);
}catch(BinaryDataException $e){ }catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e); throw new CorruptedChunkException($e->getMessage(), 0, $e);
@ -220,8 +210,7 @@ class LevelDB extends BaseLevelProvider{
try{ try{
$fullIds = $binaryStream->get(32768); $fullIds = $binaryStream->get(32768);
$fullData = $binaryStream->get(16384); $fullData = $binaryStream->get(16384);
$fullSkyLight = $binaryStream->get(16384); $binaryStream->get(32768); //legacy light info, discard it
$fullBlockLight = $binaryStream->get(16384);
}catch(BinaryDataException $e){ }catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e); throw new CorruptedChunkException($e->getMessage(), 0, $e);
} }
@ -239,23 +228,12 @@ class LevelDB extends BaseLevelProvider{
$data .= substr($fullData, $subOffset, 8); $data .= substr($fullData, $subOffset, 8);
$subOffset += 64; $subOffset += 64;
} }
$skyLight = "";
$subOffset = ($yy << 3); $subChunks[$yy] = new SubChunk($ids, $data);
for($i = 0; $i < 256; ++$i){
$skyLight .= substr($fullSkyLight, $subOffset, 8);
$subOffset += 64;
}
$blockLight = "";
$subOffset = ($yy << 3);
for($i = 0; $i < 256; ++$i){
$blockLight .= substr($fullBlockLight, $subOffset, 8);
$subOffset += 64;
}
$subChunks[$yy] = new SubChunk($ids, $data, $skyLight, $blockLight);
} }
try{ try{
$heightMap = array_values(unpack("C*", $binaryStream->get(256))); $binaryStream->get(256); //heightmap, discard it
$biomeIds = ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024)))); $biomeIds = ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024))));
}catch(BinaryDataException $e){ }catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e); throw new CorruptedChunkException($e->getMessage(), 0, $e);
@ -307,15 +285,13 @@ class LevelDB extends BaseLevelProvider{
$subChunks, $subChunks,
$entities, $entities,
$tiles, $tiles,
$biomeIds, $biomeIds
$heightMap
); );
//TODO: tile ticks, biome states (?) //TODO: tile ticks, biome states (?)
$chunk->setGenerated(true); $chunk->setGenerated();
$chunk->setPopulated(true); $chunk->setPopulated();
$chunk->setLightPopulated($lightPopulated);
return $chunk; return $chunk;
} }
@ -338,7 +314,7 @@ class LevelDB extends BaseLevelProvider{
} }
} }
$this->db->put($index . self::TAG_DATA_2D, pack("v*", ...$chunk->getHeightMapArray()) . $chunk->getBiomeIdArray()); $this->db->put($index . self::TAG_DATA_2D, str_repeat("\x00", 512) . $chunk->getBiomeIdArray());
//TODO: use this properly //TODO: use this properly
$this->db->put($index . self::TAG_STATE_FINALISATION, chr(self::FINALISATION_DONE)); $this->db->put($index . self::TAG_STATE_FINALISATION, chr(self::FINALISATION_DONE));

View File

@ -27,6 +27,7 @@ use pocketmine\level\format\io\ChunkUtils;
use pocketmine\level\format\SubChunk; use pocketmine\level\format\SubChunk;
use pocketmine\nbt\tag\ByteArrayTag; use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use function str_repeat;
class Anvil extends RegionLevelProvider{ class Anvil extends RegionLevelProvider{
use LegacyAnvilChunkTrait; use LegacyAnvilChunkTrait;
@ -35,17 +36,16 @@ class Anvil extends RegionLevelProvider{
return new CompoundTag("", [ return new CompoundTag("", [
new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY
new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())), new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())),
new ByteArrayTag("SkyLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockSkyLightArray(), "\xff")), new ByteArrayTag("SkyLight", str_repeat("\x00", 2048)),
new ByteArrayTag("BlockLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockLightArray())) new ByteArrayTag("BlockLight", str_repeat("\x00", 2048))
]); ]);
} }
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{ protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk( return new SubChunk(
ChunkUtils::reorderByteArray($subChunk->getByteArray("Blocks")), ChunkUtils::reorderByteArray($subChunk->getByteArray("Blocks")),
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("Data")), ChunkUtils::reorderNibbleArray($subChunk->getByteArray("Data"))
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("SkyLight"), "\xff"), //ignore legacy light information
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("BlockLight"))
); );
} }

View File

@ -33,6 +33,7 @@ use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntArrayTag; use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use function array_fill;
/** /**
* Trait containing I/O methods for handling legacy Anvil-style chunks. * Trait containing I/O methods for handling legacy Anvil-style chunks.
@ -56,7 +57,7 @@ trait LegacyAnvilChunkTrait{
$nbt->setLong("LastUpdate", 0); //TODO $nbt->setLong("LastUpdate", 0); //TODO
$nbt->setLong("InhabitedTime", 0); //TODO $nbt->setLong("InhabitedTime", 0); //TODO
$nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0); $nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->setByte("LightPopulated", $chunk->isLightPopulated() ? 1 : 0); $nbt->setByte("LightPopulated", 0);
$subChunks = []; $subChunks = [];
foreach($chunk->getSubChunks() as $y => $subChunk){ foreach($chunk->getSubChunks() as $y => $subChunk){
@ -71,7 +72,7 @@ trait LegacyAnvilChunkTrait{
$nbt->setTag(new ListTag("Sections", $subChunks, NBT::TAG_Compound)); $nbt->setTag(new ListTag("Sections", $subChunks, NBT::TAG_Compound));
$nbt->setByteArray("Biomes", $chunk->getBiomeIdArray()); $nbt->setByteArray("Biomes", $chunk->getBiomeIdArray());
$nbt->setIntArray("HeightMap", $chunk->getHeightMapArray()); $nbt->setIntArray("HeightMap", array_fill(0, 256, 0));
$nbt->setTag(new ListTag("Entities", $chunk->getNBTentities(), NBT::TAG_Compound)); $nbt->setTag(new ListTag("Entities", $chunk->getNBTentities(), NBT::TAG_Compound));
$nbt->setTag(new ListTag("TileEntities", $chunk->getNBTtiles(), NBT::TAG_Compound)); $nbt->setTag(new ListTag("TileEntities", $chunk->getNBTtiles(), NBT::TAG_Compound));
@ -123,10 +124,8 @@ trait LegacyAnvilChunkTrait{
$subChunks, $subChunks,
$chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [], $chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [],
$chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [], $chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [],
$biomeIds, $biomeIds
$chunk->getIntArray("HeightMap", [])
); );
$result->setLightPopulated($chunk->getByte("LightPopulated", 0) !== 0);
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0); $result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
$result->setGenerated(); $result->setGenerated();
return $result; return $result;

View File

@ -34,11 +34,8 @@ use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntArrayTag; use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use function array_values;
use function pack;
use function str_repeat; use function str_repeat;
use function substr; use function substr;
use function unpack;
class McRegion extends RegionLevelProvider{ class McRegion extends RegionLevelProvider{
@ -54,12 +51,10 @@ class McRegion extends RegionLevelProvider{
$nbt->setLong("LastUpdate", 0); //TODO $nbt->setLong("LastUpdate", 0); //TODO
$nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0); $nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->setByte("LightPopulated", $chunk->isLightPopulated() ? 1 : 0); $nbt->setByte("LightPopulated", 0);
$ids = ""; $ids = "";
$data = ""; $data = "";
$skyLight = "";
$blockLight = "";
$subChunks = $chunk->getSubChunks(); $subChunks = $chunk->getSubChunks();
for($x = 0; $x < 16; ++$x){ for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){ for($z = 0; $z < 16; ++$z){
@ -67,19 +62,17 @@ class McRegion extends RegionLevelProvider{
$subChunk = $subChunks[$y]; $subChunk = $subChunks[$y];
$ids .= substr($subChunk->getBlockIdArray(), ($x << 8) | ($z << 4), 16); $ids .= substr($subChunk->getBlockIdArray(), ($x << 8) | ($z << 4), 16);
$data .= substr($subChunk->getBlockDataArray(), ($x << 7) | ($z << 3), 8); $data .= substr($subChunk->getBlockDataArray(), ($x << 7) | ($z << 3), 8);
$skyLight .= substr($subChunk->getBlockSkyLightArray(), ($x << 7) | ($z << 3), 8);
$blockLight .= substr($subChunk->getBlockLightArray(), ($x << 7) | ($z << 3), 8);
} }
} }
} }
$nbt->setByteArray("Blocks", $ids); $nbt->setByteArray("Blocks", $ids);
$nbt->setByteArray("Data", $data); $nbt->setByteArray("Data", $data);
$nbt->setByteArray("SkyLight", $skyLight); $nbt->setByteArray("SkyLight", str_repeat("\x00", 16384));
$nbt->setByteArray("BlockLight", $blockLight); $nbt->setByteArray("BlockLight", str_repeat("\x00", 16384));
$nbt->setByteArray("Biomes", $chunk->getBiomeIdArray()); //doesn't exist in regular McRegion, this is here for PocketMine-MP only $nbt->setByteArray("Biomes", $chunk->getBiomeIdArray()); //doesn't exist in regular McRegion, this is here for PocketMine-MP only
$nbt->setByteArray("HeightMap", pack("C*", ...$chunk->getHeightMapArray())); //this is ByteArray in McRegion, but IntArray in Anvil (due to raised build height) $nbt->setByteArray("HeightMap", str_repeat("\x00", 256)); //this is ByteArray in McRegion, but IntArray in Anvil (due to raised build height)
$nbt->setTag(new ListTag("Entities", $chunk->getNBTentities(), NBT::TAG_Compound)); $nbt->setTag(new ListTag("Entities", $chunk->getNBTentities(), NBT::TAG_Compound));
$nbt->setTag(new ListTag("TileEntities", $chunk->getNBTtiles(), NBT::TAG_Compound)); $nbt->setTag(new ListTag("TileEntities", $chunk->getNBTtiles(), NBT::TAG_Compound));
@ -110,8 +103,6 @@ class McRegion extends RegionLevelProvider{
$subChunks = []; $subChunks = [];
$fullIds = $chunk->hasTag("Blocks", ByteArrayTag::class) ? $chunk->getByteArray("Blocks") : str_repeat("\x00", 32768); $fullIds = $chunk->hasTag("Blocks", ByteArrayTag::class) ? $chunk->getByteArray("Blocks") : str_repeat("\x00", 32768);
$fullData = $chunk->hasTag("Data", ByteArrayTag::class) ? $chunk->getByteArray("Data") : str_repeat("\x00", 16384); $fullData = $chunk->hasTag("Data", ByteArrayTag::class) ? $chunk->getByteArray("Data") : str_repeat("\x00", 16384);
$fullSkyLight = $chunk->hasTag("SkyLight", ByteArrayTag::class) ? $chunk->getByteArray("SkyLight") : str_repeat("\xff", 16384);
$fullBlockLight = $chunk->hasTag("BlockLight", ByteArrayTag::class) ? $chunk->getByteArray("BlockLight") : str_repeat("\x00", 16384);
for($y = 0; $y < 8; ++$y){ for($y = 0; $y < 8; ++$y){
$offset = ($y << 4); $offset = ($y << 4);
@ -126,19 +117,7 @@ class McRegion extends RegionLevelProvider{
$data .= substr($fullData, $offset, 8); $data .= substr($fullData, $offset, 8);
$offset += 64; $offset += 64;
} }
$skyLight = ""; $subChunks[$y] = new SubChunk($ids, $data);
$offset = ($y << 3);
for($i = 0; $i < 256; ++$i){
$skyLight .= substr($fullSkyLight, $offset, 8);
$offset += 64;
}
$blockLight = "";
$offset = ($y << 3);
for($i = 0; $i < 256; ++$i){
$blockLight .= substr($fullBlockLight, $offset, 8);
$offset += 64;
}
$subChunks[$y] = new SubChunk($ids, $data, $skyLight, $blockLight);
} }
if($chunk->hasTag("BiomeColors", IntArrayTag::class)){ if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
@ -149,23 +128,14 @@ class McRegion extends RegionLevelProvider{
$biomeIds = ""; $biomeIds = "";
} }
$heightMap = [];
if($chunk->hasTag("HeightMap", ByteArrayTag::class)){
$heightMap = array_values(unpack("C*", $chunk->getByteArray("HeightMap")));
}elseif($chunk->hasTag("HeightMap", IntArrayTag::class)){
$heightMap = $chunk->getIntArray("HeightMap"); #blameshoghicp
}
$result = new Chunk( $result = new Chunk(
$chunk->getInt("xPos"), $chunk->getInt("xPos"),
$chunk->getInt("zPos"), $chunk->getInt("zPos"),
$subChunks, $subChunks,
$chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [], $chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [],
$chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [], $chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [],
$biomeIds, $biomeIds
$heightMap
); );
$result->setLightPopulated($chunk->getByte("LightPopulated", 0) !== 0);
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0); $result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
$result->setGenerated(true); $result->setGenerated(true);
return $result; return $result;

View File

@ -26,6 +26,7 @@ namespace pocketmine\level\format\io\region;
use pocketmine\level\format\SubChunk; use pocketmine\level\format\SubChunk;
use pocketmine\nbt\tag\ByteArrayTag; use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use function str_repeat;
/** /**
* This format is exactly the same as the PC Anvil format, with the only difference being that the stored data order * This format is exactly the same as the PC Anvil format, with the only difference being that the stored data order
@ -38,17 +39,15 @@ class PMAnvil extends RegionLevelProvider{
return new CompoundTag("", [ return new CompoundTag("", [
new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()), new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()),
new ByteArrayTag("Data", $subChunk->getBlockDataArray()), new ByteArrayTag("Data", $subChunk->getBlockDataArray()),
new ByteArrayTag("SkyLight", $subChunk->getBlockSkyLightArray()), new ByteArrayTag("SkyLight", str_repeat("\x00", 2048)),
new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray()) new ByteArrayTag("BlockLight", str_repeat("\x00", 2048))
]); ]);
} }
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{ protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk( return new SubChunk(
$subChunk->getByteArray("Blocks"), $subChunk->getByteArray("Blocks"),
$subChunk->getByteArray("Data"), $subChunk->getByteArray("Data")
$subChunk->getByteArray("SkyLight"),
$subChunk->getByteArray("BlockLight")
); );
} }