LevelDB: Added a whole bunch of constants

This commit is contained in:
Dylan K. Taylor 2022-02-07 00:54:00 +00:00
parent 95bc013e97
commit c19a038d47
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
4 changed files with 242 additions and 59 deletions

View File

@ -0,0 +1,53 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\format\io\leveldb;
final class ChunkDataKey{
private function __construct(){
//NOOP
}
public const HEIGHTMAP_AND_3D_BIOMES = "\x2b";
public const NEW_VERSION = "\x2c"; //since 1.16.100?
public const HEIGHTMAP_AND_2D_BIOMES = "\x2d"; //obsolete since 1.18
public const HEIGHTMAP_AND_2D_BIOME_COLORS = "\x2e"; //obsolete since 1.0
public const SUBCHUNK = "\x2f";
public const LEGACY_TERRAIN = "\x30"; //obsolete since 1.0
public const BLOCK_ENTITIES = "\x31";
public const ENTITIES = "\x32";
public const PENDING_SCHEDULED_TICKS = "\x33";
public const LEGACY_BLOCK_EXTRA_DATA = "\x34"; //obsolete since 1.2.13
public const BIOME_STATES = "\x35"; //TODO: is this still applicable to 1.18.0?
public const FINALIZATION = "\x36";
public const CONVERTER_TAG = "\x37"; //???
public const BORDER_BLOCKS = "\x38";
public const HARDCODED_SPAWNERS = "\x39";
public const PENDING_RANDOM_TICKS = "\x3a";
public const XXHASH_CHECKSUMS = "\x3b"; //obsolete since 1.18
public const GENERATION_SEED = "\x3c";
public const GENERATED_BEFORE_CNC_BLENDING = "\x3d";
public const OLD_VERSION = "\x76";
}

View File

@ -0,0 +1,73 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\format\io\leveldb;
final class ChunkVersion{
private function __construct(){
//NOOP
}
public const v0_9_0 = 0;
public const v0_9_2 = 1;
public const v0_9_5 = 2;
public const v1_0_0 = 3;
public const v1_1_0 = 4;
public const v1_1_0_converted_from_console = 5;
public const v1_2_0_2_beta = 6;
public const v1_2_0 = 7;
public const v1_2_13 = 8;
public const v1_8_0 = 9;
public const v1_9_0 = 10;
public const v1_11_0_1_beta = 11;
public const v1_11_0_3_beta = 12;
public const v1_11_0_4_beta = 13;
public const v1_11_1 = 14;
public const v1_12_0_4_beta = 15;
public const v1_12_0_unused1 = 16; //possibly some beta version?
public const v1_12_0_unused2 = 17; //possibly some beta version?
public const v1_16_0_51_beta = 18;
public const v1_16_0 = 19;
public const v1_16_100_52_beta = 20;
public const v1_16_100_57_beta = 21;
public const v1_16_210 = 22;
//Since this version they seem to skip every other version. Possibly the skipped ones are internal use.
public const v1_16_220_50_beta_experimental_caves_cliffs = 23;
public const v1_16_220_50_unused = 24;
public const v1_16_230_50_beta_experimental_caves_cliffs = 25;
public const v1_16_230_50_unused = 26;
public const v1_17_30_23_beta_experimental_caves_cliffs = 27;
public const v1_17_30_23_unused = 28;
public const v1_17_30_25_beta_experimental_caves_cliffs = 29;
public const v1_17_30_25_unused = 30;
public const v1_17_40_20_beta_experimental_caves_cliffs = 31;
public const v1_17_40_unused = 32;
public const v1_18_0_20_beta = 33;
public const v1_18_0_20_unused = 34;
public const v1_18_0_22_beta = 35;
public const v1_18_0_22_unused = 36;
public const v1_18_0_24_beta = 37;
public const v1_18_0_24_unused = 38;
public const v1_18_0_25_beta = 39;
}

View File

@ -71,30 +71,30 @@ use const LEVELDB_ZLIB_RAW_COMPRESSION;
class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
//According to Tomasso, these aren't supposed to be readable anymore. Thankfully he didn't change the readable ones...
protected const TAG_DATA_2D = "\x2d";
protected const TAG_DATA_2D_LEGACY = "\x2e";
protected const TAG_SUBCHUNK_PREFIX = "\x2f";
protected const TAG_LEGACY_TERRAIN = "0";
protected const TAG_BLOCK_ENTITY = "1";
protected const TAG_ENTITY = "2";
protected const TAG_PENDING_TICK = "3";
protected const TAG_BLOCK_EXTRA_DATA = "4";
protected const TAG_BIOME_STATE = "5";
protected const TAG_STATE_FINALISATION = "6";
protected const TAG_DATA_2D = ChunkDataKey::HEIGHTMAP_AND_2D_BIOMES;
protected const TAG_DATA_2D_LEGACY = ChunkDataKey::HEIGHTMAP_AND_2D_BIOME_COLORS;
protected const TAG_SUBCHUNK_PREFIX = ChunkDataKey::SUBCHUNK;
protected const TAG_LEGACY_TERRAIN = ChunkDataKey::LEGACY_TERRAIN;
protected const TAG_BLOCK_ENTITY = ChunkDataKey::BLOCK_ENTITIES;
protected const TAG_ENTITY = ChunkDataKey::ENTITIES;
protected const TAG_PENDING_TICK = ChunkDataKey::PENDING_SCHEDULED_TICKS;
protected const TAG_BLOCK_EXTRA_DATA = ChunkDataKey::LEGACY_BLOCK_EXTRA_DATA;
protected const TAG_BIOME_STATE = ChunkDataKey::BIOME_STATES;
protected const TAG_STATE_FINALISATION = ChunkDataKey::FINALIZATION;
protected const TAG_BORDER_BLOCKS = "8";
protected const TAG_HARDCODED_SPAWNERS = "9";
protected const TAG_BORDER_BLOCKS = ChunkDataKey::BORDER_BLOCKS;
protected const TAG_HARDCODED_SPAWNERS = ChunkDataKey::HARDCODED_SPAWNERS;
protected const FINALISATION_NEEDS_INSTATICKING = 0;
protected const FINALISATION_NEEDS_POPULATION = 1;
protected const FINALISATION_DONE = 2;
protected const TAG_VERSION = "v";
protected const TAG_VERSION = ChunkDataKey::OLD_VERSION;
protected const ENTRY_FLAT_WORLD_LAYERS = "game_flatworldlayers";
protected const CURRENT_LEVEL_CHUNK_VERSION = 7;
protected const CURRENT_LEVEL_SUBCHUNK_VERSION = 8;
protected const CURRENT_LEVEL_CHUNK_VERSION = ChunkVersion::v1_2_0; //yes, I know this is wrong, but it ensures vanilla auto-fixes stuff we currently don't
protected const CURRENT_LEVEL_SUBCHUNK_VERSION = SubChunkVersion::PALETTED_MULTI;
/** @var \LevelDB */
protected $db;
@ -192,7 +192,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
protected static function deserializeExtraDataKey(int $chunkVersion, int $key, ?int &$x, ?int &$y, ?int &$z) : void{
if($chunkVersion >= 3){
if($chunkVersion >= ChunkVersion::v1_0_0){
$x = ($key >> 12) & 0xf;
$z = ($key >> 8) & 0xf;
$y = $key & 0xff;
@ -207,7 +207,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
* @return PalettedBlockArray[]
*/
protected function deserializeLegacyExtraData(string $index, int $chunkVersion) : array{
if(($extraRawData = $this->db->get($index . self::TAG_BLOCK_EXTRA_DATA)) === false || $extraRawData === ""){
if(($extraRawData = $this->db->get($index . ChunkDataKey::LEGACY_BLOCK_EXTRA_DATA)) === false || $extraRawData === ""){
return [];
}
@ -241,7 +241,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
public function loadChunk(int $chunkX, int $chunkZ) : ?ChunkData{
$index = LevelDB::chunkIndex($chunkX, $chunkZ);
$chunkVersionRaw = $this->db->get($index . self::TAG_VERSION);
$chunkVersionRaw = $this->db->get($index . ChunkDataKey::OLD_VERSION);
if($chunkVersionRaw === false){
return null;
}
@ -256,23 +256,23 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$hasBeenUpgraded = $chunkVersion < self::CURRENT_LEVEL_CHUNK_VERSION;
switch($chunkVersion){
case 15: //MCPE 1.12.0.4 beta (???)
case 14: //MCPE 1.11.1.2 (???)
case 13: //MCPE 1.11.0.4 beta (???)
case 12: //MCPE 1.11.0.3 beta (???)
case 11: //MCPE 1.11.0.1 beta (???)
case 10: //MCPE 1.9 (???)
case 9: //MCPE 1.8 (???)
case 8: //MCPE 1.2.13 (paletted subchunks)
case 7: //MCPE 1.2 (???)
case 6: //MCPE 1.2.0.2 beta (???)
case 4: //MCPE 1.1
case ChunkVersion::v1_12_0_4_beta:
case ChunkVersion::v1_11_1:
case ChunkVersion::v1_11_0_4_beta:
case ChunkVersion::v1_11_0_3_beta:
case ChunkVersion::v1_11_0_1_beta:
case ChunkVersion::v1_9_0:
case ChunkVersion::v1_8_0:
case ChunkVersion::v1_2_13:
case ChunkVersion::v1_2_0:
case ChunkVersion::v1_2_0_2_beta:
case ChunkVersion::v1_1_0:
//TODO: check beds
case 3: //MCPE 1.0
case ChunkVersion::v1_0_0:
$convertedLegacyExtraData = $this->deserializeLegacyExtraData($index, $chunkVersion);
for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){
if(($data = $this->db->get($index . self::TAG_SUBCHUNK_PREFIX . chr($y))) === false){
if(($data = $this->db->get($index . ChunkDataKey::SUBCHUNK . chr($y))) === false){
continue;
}
@ -286,18 +286,18 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
switch($subChunkVersion){
case 0:
case 2: //these are all identical to version 0, but vanilla respects these so we should also
case 3:
case 4:
case 5:
case 6:
case 7:
case SubChunkVersion::CLASSIC:
case SubChunkVersion::CLASSIC_BUG_2: //these are all identical to version 0, but vanilla respects these so we should also
case SubChunkVersion::CLASSIC_BUG_3:
case SubChunkVersion::CLASSIC_BUG_4:
case SubChunkVersion::CLASSIC_BUG_5:
case SubChunkVersion::CLASSIC_BUG_6:
case SubChunkVersion::CLASSIC_BUG_7:
try{
$blocks = $binaryStream->get(4096);
$blockData = $binaryStream->get(2048);
if($chunkVersion < 4){
if($chunkVersion < ChunkVersion::v1_1_0){
$binaryStream->get(4096); //legacy light info, discard it
$hasBeenUpgraded = true;
}
@ -312,14 +312,14 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$subChunks[$y] = new SubChunk(BlockLegacyIds::AIR << Block::INTERNAL_METADATA_BITS, $storages);
break;
case 1: //paletted v1, has a single blockstorage
case SubChunkVersion::PALETTED_SINGLE:
$storages = [$this->deserializePaletted($binaryStream)];
if(isset($convertedLegacyExtraData[$y])){
$storages[] = $convertedLegacyExtraData[$y];
}
$subChunks[$y] = new SubChunk(BlockLegacyIds::AIR << Block::INTERNAL_METADATA_BITS, $storages);
break;
case 8:
case SubChunkVersion::PALETTED_MULTI:
//legacy extradata layers intentionally ignored because they aren't supposed to exist in v8
$storageCount = $binaryStream->getByte();
if($storageCount > 0){
@ -337,7 +337,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
}
if(($maps2d = $this->db->get($index . self::TAG_DATA_2D)) !== false){
if(($maps2d = $this->db->get($index . ChunkDataKey::HEIGHTMAP_AND_2D_BIOMES)) !== false){
$binaryStream = new BinaryStream($maps2d);
try{
@ -348,12 +348,12 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
}
break;
case 2: // < MCPE 1.0
case 1:
case 0: //MCPE 0.9.0.1 beta (first version)
case ChunkVersion::v0_9_5:
case ChunkVersion::v0_9_2:
case ChunkVersion::v0_9_0:
$convertedLegacyExtraData = $this->deserializeLegacyExtraData($index, $chunkVersion);
$legacyTerrain = $this->db->get($index . self::TAG_LEGACY_TERRAIN);
$legacyTerrain = $this->db->get($index . ChunkDataKey::LEGACY_TERRAIN);
if($legacyTerrain === false){
throw new CorruptedChunkException("Missing expected LEGACY_TERRAIN tag for format version $chunkVersion");
}
@ -392,7 +392,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
/** @var CompoundTag[] $entities */
$entities = [];
if(($entityData = $this->db->get($index . self::TAG_ENTITY)) !== false && $entityData !== ""){
if(($entityData = $this->db->get($index . ChunkDataKey::ENTITIES)) !== false && $entityData !== ""){
try{
$entities = array_map(fn(TreeRoot $root) => $root->mustGetCompoundTag(), $nbt->readMultiple($entityData));
}catch(NbtDataException $e){
@ -402,7 +402,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
/** @var CompoundTag[] $tiles */
$tiles = [];
if(($tileData = $this->db->get($index . self::TAG_BLOCK_ENTITY)) !== false && $tileData !== ""){
if(($tileData = $this->db->get($index . ChunkDataKey::BLOCK_ENTITIES)) !== false && $tileData !== ""){
try{
$tiles = array_map(fn(TreeRoot $root) => $root->mustGetCompoundTag(), $nbt->readMultiple($tileData));
}catch(NbtDataException $e){
@ -410,7 +410,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
}
$finalisationChr = $this->db->get($index . self::TAG_STATE_FINALISATION);
$finalisationChr = $this->db->get($index . ChunkDataKey::FINALIZATION);
if($finalisationChr !== false){
$finalisation = ord($finalisationChr);
$terrainPopulated = $finalisation === self::FINALISATION_DONE;
@ -438,13 +438,13 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
$index = LevelDB::chunkIndex($chunkX, $chunkZ);
$write = new \LevelDBWriteBatch();
$write->put($index . self::TAG_VERSION, chr(self::CURRENT_LEVEL_CHUNK_VERSION));
$write->put($index . ChunkDataKey::OLD_VERSION, chr(self::CURRENT_LEVEL_CHUNK_VERSION));
$chunk = $chunkData->getChunk();
if($chunk->getTerrainDirtyFlag(Chunk::DIRTY_FLAG_BLOCKS)){
$subChunks = $chunk->getSubChunks();
foreach($subChunks as $y => $subChunk){
$key = $index . self::TAG_SUBCHUNK_PREFIX . chr($y);
$key = $index . ChunkDataKey::SUBCHUNK . chr($y);
if($subChunk->isEmptyAuthoritative()){
$write->delete($key);
}else{
@ -483,17 +483,17 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
}
if($chunk->getTerrainDirtyFlag(Chunk::DIRTY_FLAG_BIOMES)){
$write->put($index . self::TAG_DATA_2D, str_repeat("\x00", 512) . $chunk->getBiomeIdArray());
$write->put($index . ChunkDataKey::HEIGHTMAP_AND_2D_BIOMES, str_repeat("\x00", 512) . $chunk->getBiomeIdArray());
}
//TODO: use this properly
$write->put($index . self::TAG_STATE_FINALISATION, chr($chunk->isPopulated() ? self::FINALISATION_DONE : self::FINALISATION_NEEDS_POPULATION));
$write->put($index . ChunkDataKey::FINALIZATION, chr($chunk->isPopulated() ? self::FINALISATION_DONE : self::FINALISATION_NEEDS_POPULATION));
$this->writeTags($chunkData->getTileNBT(), $index . self::TAG_BLOCK_ENTITY, $write);
$this->writeTags($chunkData->getEntityNBT(), $index . self::TAG_ENTITY, $write);
$this->writeTags($chunkData->getTileNBT(), $index . ChunkDataKey::BLOCK_ENTITIES, $write);
$this->writeTags($chunkData->getEntityNBT(), $index . ChunkDataKey::ENTITIES, $write);
$write->delete($index . self::TAG_DATA_2D_LEGACY);
$write->delete($index . self::TAG_LEGACY_TERRAIN);
$write->delete($index . ChunkDataKey::HEIGHTMAP_AND_2D_BIOME_COLORS);
$write->delete($index . ChunkDataKey::LEGACY_TERRAIN);
$this->db->write($write);
}
@ -528,7 +528,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
public function getAllChunks(bool $skipCorrupted = false, ?\Logger $logger = null) : \Generator{
foreach($this->db->getIterator() as $key => $_){
if(strlen($key) === 9 && substr($key, -1) === self::TAG_VERSION){
if(strlen($key) === 9 && substr($key, -1) === ChunkDataKey::OLD_VERSION){
$chunkX = Binary::readLInt(substr($key, 0, 4));
$chunkZ = Binary::readLInt(substr($key, 4, 4));
try{
@ -550,7 +550,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
public function calculateChunkCount() : int{
$count = 0;
foreach($this->db->getIterator() as $key => $_){
if(strlen($key) === 9 && substr($key, -1) === self::TAG_VERSION){
if(strlen($key) === 9 && substr($key, -1) === ChunkDataKey::OLD_VERSION){
$count++;
}
}

View File

@ -0,0 +1,57 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\format\io\leveldb;
final class SubChunkVersion{
private function __construct(){
//NOOP
}
/** Original subchunk format as of MCPE 1.0 */
public const CLASSIC = 0;
/** First paletted version, seen in 1.2.13 */
public const PALETTED_SINGLE = 1;
//the following are not used by vanilla, but treated the same as version 0 due to a legacy converter which
//erroneously used the version byte as subchunk height
public const CLASSIC_BUG_2 = 2;
public const CLASSIC_BUG_3 = 3;
public const CLASSIC_BUG_4 = 4;
public const CLASSIC_BUG_5 = 5;
public const CLASSIC_BUG_6 = 6;
public const CLASSIC_BUG_7 = 7;
/**
* Paletted with layers, almost identical to v1, but includes a length prefix and 0 or more storages.
* First seen in 1.4 Update Aquatic to support water inside other blocks.
*/
public const PALETTED_MULTI = 8;
/**
* Paletted with layers, identical to v8 except for a height byte after the layercount byte.
* This seems like a pointless change, but newest versions of the game use it.
* First seen in 1.18 for Caves and Cliffs update (and some experimental versions prior to 1.18).
*/
public const PALETTED_MULTI_WITH_OFFSET = 9;
}