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();
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));
}

View File

@ -46,7 +46,7 @@ use function file_exists;
use function is_dir;
use function mkdir;
use function ord;
use function pack;
use function str_repeat;
use function strlen;
use function substr;
use function unpack;
@ -146,14 +146,9 @@ class LevelDB extends BaseLevelProvider{
/** @var SubChunk[] $subChunks */
$subChunks = [];
/** @var int[] $heightMap */
$heightMap = [];
/** @var string $biomeIds */
$biomeIds = "";
/** @var bool $lightPopulated */
$lightPopulated = true;
$chunkVersion = ord($this->db->get($index . self::TAG_VERSION));
$binaryStream = new BinaryStream();
@ -179,20 +174,15 @@ class LevelDB extends BaseLevelProvider{
try{
$blocks = $binaryStream->get(4096);
$blockData = $binaryStream->get(2048);
if($chunkVersion < 4){
$blockSkyLight = $binaryStream->get(2048);
$blockLight = $binaryStream->get(2048);
}else{
//Mojang didn't bother changing the subchunk version when they stopped saving sky light -_-
$blockSkyLight = "";
$blockLight = "";
$lightPopulated = false;
$binaryStream->get(4096); //legacy light info, discard it
}
}catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e);
}
$subChunks[$y] = new SubChunk($blocks, $blockData, $blockSkyLight, $blockLight);
$subChunks[$y] = new SubChunk($blocks, $blockData);
break;
default:
//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);
try{
$heightMap = array_values(unpack("v*", $binaryStream->get(512)));
$binaryStream->get(512); //heightmap, discard it
$biomeIds = $binaryStream->get(256);
}catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e);
@ -220,8 +210,7 @@ class LevelDB extends BaseLevelProvider{
try{
$fullIds = $binaryStream->get(32768);
$fullData = $binaryStream->get(16384);
$fullSkyLight = $binaryStream->get(16384);
$fullBlockLight = $binaryStream->get(16384);
$binaryStream->get(32768); //legacy light info, discard it
}catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e);
}
@ -239,23 +228,12 @@ class LevelDB extends BaseLevelProvider{
$data .= substr($fullData, $subOffset, 8);
$subOffset += 64;
}
$skyLight = "";
$subOffset = ($yy << 3);
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);
$subChunks[$yy] = new SubChunk($ids, $data);
}
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))));
}catch(BinaryDataException $e){
throw new CorruptedChunkException($e->getMessage(), 0, $e);
@ -307,15 +285,13 @@ class LevelDB extends BaseLevelProvider{
$subChunks,
$entities,
$tiles,
$biomeIds,
$heightMap
$biomeIds
);
//TODO: tile ticks, biome states (?)
$chunk->setGenerated(true);
$chunk->setPopulated(true);
$chunk->setLightPopulated($lightPopulated);
$chunk->setGenerated();
$chunk->setPopulated();
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
$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\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag;
use function str_repeat;
class Anvil extends RegionLevelProvider{
use LegacyAnvilChunkTrait;
@ -35,17 +36,16 @@ class Anvil extends RegionLevelProvider{
return new CompoundTag("", [
new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY
new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())),
new ByteArrayTag("SkyLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockSkyLightArray(), "\xff")),
new ByteArrayTag("BlockLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockLightArray()))
new ByteArrayTag("SkyLight", str_repeat("\x00", 2048)),
new ByteArrayTag("BlockLight", str_repeat("\x00", 2048))
]);
}
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk(
ChunkUtils::reorderByteArray($subChunk->getByteArray("Blocks")),
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("Data")),
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("SkyLight"), "\xff"),
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("BlockLight"))
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("Data"))
//ignore legacy light information
);
}

View File

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

View File

@ -34,11 +34,8 @@ use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\ListTag;
use function array_values;
use function pack;
use function str_repeat;
use function substr;
use function unpack;
class McRegion extends RegionLevelProvider{
@ -54,12 +51,10 @@ class McRegion extends RegionLevelProvider{
$nbt->setLong("LastUpdate", 0); //TODO
$nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->setByte("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$nbt->setByte("LightPopulated", 0);
$ids = "";
$data = "";
$skyLight = "";
$blockLight = "";
$subChunks = $chunk->getSubChunks();
for($x = 0; $x < 16; ++$x){
for($z = 0; $z < 16; ++$z){
@ -67,19 +62,17 @@ class McRegion extends RegionLevelProvider{
$subChunk = $subChunks[$y];
$ids .= substr($subChunk->getBlockIdArray(), ($x << 8) | ($z << 4), 16);
$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("Data", $data);
$nbt->setByteArray("SkyLight", $skyLight);
$nbt->setByteArray("BlockLight", $blockLight);
$nbt->setByteArray("SkyLight", str_repeat("\x00", 16384));
$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("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("TileEntities", $chunk->getNBTtiles(), NBT::TAG_Compound));
@ -110,8 +103,6 @@ class McRegion extends RegionLevelProvider{
$subChunks = [];
$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);
$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){
$offset = ($y << 4);
@ -126,19 +117,7 @@ class McRegion extends RegionLevelProvider{
$data .= substr($fullData, $offset, 8);
$offset += 64;
}
$skyLight = "";
$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);
$subChunks[$y] = new SubChunk($ids, $data);
}
if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
@ -149,23 +128,14 @@ class McRegion extends RegionLevelProvider{
$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(
$chunk->getInt("xPos"),
$chunk->getInt("zPos"),
$subChunks,
$chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [],
$chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [],
$biomeIds,
$heightMap
$biomeIds
);
$result->setLightPopulated($chunk->getByte("LightPopulated", 0) !== 0);
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
$result->setGenerated(true);
return $result;

View File

@ -26,6 +26,7 @@ namespace pocketmine\level\format\io\region;
use pocketmine\level\format\SubChunk;
use pocketmine\nbt\tag\ByteArrayTag;
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
@ -38,17 +39,15 @@ class PMAnvil extends RegionLevelProvider{
return new CompoundTag("", [
new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()),
new ByteArrayTag("Data", $subChunk->getBlockDataArray()),
new ByteArrayTag("SkyLight", $subChunk->getBlockSkyLightArray()),
new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray())
new ByteArrayTag("SkyLight", str_repeat("\x00", 2048)),
new ByteArrayTag("BlockLight", str_repeat("\x00", 2048))
]);
}
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk(
$subChunk->getByteArray("Blocks"),
$subChunk->getByteArray("Data"),
$subChunk->getByteArray("SkyLight"),
$subChunk->getByteArray("BlockLight")
$subChunk->getByteArray("Data")
);
}