migrate to new CompoundTag API (#1515)

This commit is contained in:
Dylan K. Taylor
2017-11-10 15:38:21 +00:00
committed by GitHub
parent d4494687d1
commit aa399a1109
20 changed files with 305 additions and 370 deletions

View File

@ -31,8 +31,6 @@ use pocketmine\level\LevelException;
use pocketmine\math\Vector3;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\LongTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\scheduler\AsyncTask;
@ -52,19 +50,19 @@ abstract class BaseLevelProvider implements LevelProvider{
}
$nbt = new NBT(NBT::BIG_ENDIAN);
$nbt->readCompressed(file_get_contents($this->getPath() . "level.dat"));
$levelData = $nbt->getData();
if($levelData->Data instanceof CompoundTag){
$this->levelData = $levelData->Data;
$levelData = $nbt->getData()->getCompoundTag("Data");
if($levelData !== null){
$this->levelData = $levelData;
}else{
throw new LevelException("Invalid level.dat");
}
if(!isset($this->levelData->generatorName)){
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
if(!$this->levelData->hasTag("generatorName", StringTag::class)){
$this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"), true);
}
if(!isset($this->levelData->generatorOptions)){
$this->levelData->generatorOptions = new StringTag("generatorOptions", "");
if(!$this->levelData->hasTag("generatorOptions", StringTag::class)){
$this->levelData->setString("generatorOptions", "");
}
}
@ -81,33 +79,33 @@ abstract class BaseLevelProvider implements LevelProvider{
}
public function getName() : string{
return (string) $this->levelData["LevelName"];
return $this->levelData->getString("LevelName");
}
public function getTime() : int{
return $this->levelData["Time"];
return $this->levelData->getLong("Time", 0, true);
}
public function setTime(int $value){
$this->levelData->Time = new LongTag("Time", $value);
$this->levelData->setLong("Time", $value, true); //some older PM worlds had this in the wrong format
}
public function getSeed() : int{
return $this->levelData["RandomSeed"];
return $this->levelData->getLong("RandomSeed");
}
public function setSeed(int $value){
$this->levelData->RandomSeed = new LongTag("RandomSeed", $value);
$this->levelData->setLong("RandomSeed", $value);
}
public function getSpawn() : Vector3{
return new Vector3((float) $this->levelData["SpawnX"], (float) $this->levelData["SpawnY"], (float) $this->levelData["SpawnZ"]);
return new Vector3($this->levelData->getInt("SpawnX"), $this->levelData->getInt("SpawnY"), $this->levelData->getInt("SpawnZ"));
}
public function setSpawn(Vector3 $pos){
$this->levelData->SpawnX = new IntTag("SpawnX", (int) $pos->x);
$this->levelData->SpawnY = new IntTag("SpawnY", (int) $pos->y);
$this->levelData->SpawnZ = new IntTag("SpawnZ", (int) $pos->z);
$this->levelData->setInt("SpawnX", (int) $pos->x);
$this->levelData->setInt("SpawnY", (int) $pos->y);
$this->levelData->setInt("SpawnZ", (int) $pos->z);
}
public function doGarbageCollection(){

View File

@ -96,26 +96,26 @@ class LevelDB extends BaseLevelProvider{
"compression" => LEVELDB_ZLIB_COMPRESSION
]);
if(isset($this->levelData->StorageVersion) and $this->levelData->StorageVersion->getValue() > self::CURRENT_STORAGE_VERSION){
throw new LevelException("Specified LevelDB world format version is newer than the version supported by the server");
if($this->levelData->getInt("StorageVersion", INT32_MAX, true) > self::CURRENT_STORAGE_VERSION){
throw new LevelException("Specified LevelDB world format version is not supported by " . \pocketmine\NAME);
}
if(!isset($this->levelData->generatorName)){
if(isset($this->levelData->Generator)){
switch((int) $this->levelData->Generator->getValue()){ //Detect correct generator from MCPE data
if(!$this->levelData->hasTag("generatorName", StringTag::class)){
if($this->levelData->hasTag("Generator", IntTag::class)){
switch($this->levelData->getInt("Generator")){ //Detect correct generator from MCPE data
case self::GENERATOR_FLAT:
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("FLAT"));
$this->levelData->setString("generatorName", (string) Generator::getGenerator("FLAT"));
if(($layers = $this->db->get(self::ENTRY_FLAT_WORLD_LAYERS)) !== false){ //Detect existing custom flat layers
$layers = trim($layers, "[]");
}else{
$layers = "7,3,3,2";
}
$this->levelData->generatorOptions = new StringTag("generatorOptions", "2;" . $layers . ";1");
$this->levelData->setString("generatorOptions", "2;" . $layers . ";1");
break;
case self::GENERATOR_INFINITE:
//TODO: add a null generator which does not generate missing chunks (to allow importing back to MCPE and generating more normal terrain without PocketMine messing things up)
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->generatorOptions = new StringTag("generatorOptions", "");
$this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->setString("generatorOptions", "");
break;
case self::GENERATOR_LIMITED:
throw new LevelException("Limited worlds are not currently supported");
@ -123,12 +123,12 @@ class LevelDB extends BaseLevelProvider{
throw new LevelException("Unknown LevelDB world format type, this level cannot be loaded");
}
}else{
$this->levelData->generatorName = new StringTag("generatorName", (string) Generator::getGenerator("DEFAULT"));
$this->levelData->setString("generatorName", (string) Generator::getGenerator("DEFAULT"));
}
}
if(!isset($this->levelData->generatorOptions)){
$this->levelData->generatorOptions = new StringTag("generatorOptions", "");
if(!$this->levelData->hasTag("generatorOptions", StringTag::class)){
$this->levelData->setString("generatorOptions", "");
}
}
@ -226,8 +226,8 @@ class LevelDB extends BaseLevelProvider{
}
public function saveLevelData(){
$this->levelData->NetworkVersion = new IntTag("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL);
$this->levelData->StorageVersion = new IntTag("StorageVersion", self::CURRENT_STORAGE_VERSION);
$this->levelData->setInt("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL);
$this->levelData->setInt("StorageVersion", self::CURRENT_STORAGE_VERSION);
$nbt = new NBT(NBT::LITTLE_ENDIAN);
$nbt->setData($this->levelData);
@ -251,11 +251,11 @@ class LevelDB extends BaseLevelProvider{
}
public function getDifficulty() : int{
return isset($this->levelData->Difficulty) ? $this->levelData->Difficulty->getValue() : Level::DIFFICULTY_NORMAL;
return $this->levelData->getInt("Difficulty", Level::DIFFICULTY_NORMAL);
}
public function setDifficulty(int $difficulty){
$this->levelData->Difficulty = new IntTag("Difficulty", $difficulty);
$this->levelData->setInt("Difficulty", $difficulty); //yes, this is intended! (in PE: int, PC: byte)
}
public function getLoadedChunks() : array{

View File

@ -28,9 +28,10 @@ use pocketmine\level\format\ChunkException;
use pocketmine\level\format\io\ChunkUtils;
use pocketmine\level\format\SubChunk;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\{
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag
};
use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\utils\MainLogger;
class Anvil extends McRegion{
@ -39,28 +40,29 @@ class Anvil extends McRegion{
public function nbtSerialize(Chunk $chunk) : string{
$nbt = new CompoundTag("Level", []);
$nbt->xPos = new IntTag("xPos", $chunk->getX());
$nbt->zPos = new IntTag("zPos", $chunk->getZ());
$nbt->setInt("xPos", $chunk->getX());
$nbt->setInt("zPos", $chunk->getZ());
$nbt->V = new ByteTag("V", 1);
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO
$nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$nbt->setByte("V", 1);
$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->Sections = new ListTag("Sections", [], NBT::TAG_Compound);
$subChunks = -1;
$subChunks = [];
foreach($chunk->getSubChunks() as $y => $subChunk){
if($subChunk->isEmpty()){
continue;
}
$tag = $this->serializeSubChunk($subChunk);
$tag->setByte("Y", $y);
$nbt->Sections[++$subChunks] = $tag;
$subChunks[] = $tag;
}
$nbt->setTag(new ListTag("Sections", $subChunks, NBT::TAG_Compound));
$nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray());
$nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray());
$nbt->setByteArray("Biomes", $chunk->getBiomeIdArray());
$nbt->setIntArray("HeightMap", $chunk->getHeightMapArray());
$entities = [];
@ -71,7 +73,7 @@ class Anvil extends McRegion{
}
}
$nbt->Entities = new ListTag("Entities", $entities, NBT::TAG_Compound);
$nbt->setTag(new ListTag("Entities", $entities, NBT::TAG_Compound));
$tiles = [];
foreach($chunk->getTiles() as $tile){
@ -79,7 +81,7 @@ class Anvil extends McRegion{
$tiles[] = $tile->namedtag;
}
$nbt->TileEntities = new ListTag("TileEntities", $tiles, NBT::TAG_Compound);
$nbt->setTag(new ListTag("TileEntities", $tiles, NBT::TAG_Compound));
//TODO: TileTicks
@ -104,43 +106,38 @@ class Anvil extends McRegion{
try{
$nbt->readCompressed($data);
$chunk = $nbt->getData();
$chunk = $nbt->getData()->getCompoundTag("Level");
if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){
if($chunk === null){
throw new ChunkException("Invalid NBT format");
}
$chunk = $chunk->Level;
$subChunks = [];
if($chunk->Sections instanceof ListTag){
foreach($chunk->Sections as $subChunk){
if($subChunk instanceof CompoundTag){
$subChunks[$subChunk->Y->getValue()] = $this->deserializeSubChunk($subChunk);
}
$subChunksTag = $chunk->getListTag("Sections") ?? [];
foreach($subChunksTag as $subChunk){
if($subChunk instanceof CompoundTag){
$subChunks[$subChunk->getByte("Y")] = $this->deserializeSubChunk($subChunk);
}
}
if(isset($chunk->BiomeColors)){
$biomeIds = ChunkUtils::convertBiomeColors($chunk->BiomeColors->getValue()); //Convert back to original format
}elseif(isset($chunk->Biomes)){
$biomeIds = $chunk->Biomes->getValue();
if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
$biomeIds = ChunkUtils::convertBiomeColors($chunk->getIntArray("BiomeColors")); //Convert back to original format
}else{
$biomeIds = "";
$biomeIds = $chunk->getByteArray("Biomes", "", true);
}
$result = new Chunk(
$chunk["xPos"],
$chunk["zPos"],
$chunk->getInt("xPos"),
$chunk->getInt("zPos"),
$subChunks,
isset($chunk->Entities) ? $chunk->Entities->getValue() : [],
isset($chunk->TileEntities) ? $chunk->TileEntities->getValue() : [],
$chunk->hasTag("Entities", ListTag::class) ? $chunk->getListTag("Entities")->getValue() : [],
$chunk->hasTag("TileEntities", ListTag::class) ? $chunk->getListTag("TileEntities")->getValue() : [],
$biomeIds,
isset($chunk->HeightMap) ? $chunk->HeightMap->getValue() : []
$chunk->getIntArray("HeightMap", [])
);
$result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false);
$result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false);
$result->setGenerated(true);
$result->setLightPopulated($chunk->getByte("LightPopulated", 0) !== 0);
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
$result->setGenerated();
return $result;
}catch(\Throwable $e){
MainLogger::getLogger()->logException($e);
@ -150,10 +147,10 @@ class Anvil extends McRegion{
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk(
ChunkUtils::reorderByteArray($subChunk->Blocks->getValue()),
ChunkUtils::reorderNibbleArray($subChunk->Data->getValue()),
ChunkUtils::reorderNibbleArray($subChunk->SkyLight->getValue(), "\xff"),
ChunkUtils::reorderNibbleArray($subChunk->BlockLight->getValue())
ChunkUtils::reorderByteArray($subChunk->getByteArray("Blocks")),
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("Data")),
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("SkyLight"), "\xff"),
ChunkUtils::reorderNibbleArray($subChunk->getByteArray("BlockLight"))
);
}

View File

@ -53,12 +53,12 @@ class McRegion extends BaseLevelProvider{
*/
public function nbtSerialize(Chunk $chunk) : string{
$nbt = new CompoundTag("Level", []);
$nbt->xPos = new IntTag("xPos", $chunk->getX());
$nbt->zPos = new IntTag("zPos", $chunk->getZ());
$nbt->setInt("xPos", $chunk->getX());
$nbt->setInt("zPos", $chunk->getZ());
$nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO
$nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$nbt->setLong("LastUpdate", 0); //TODO
$nbt->setByte("TerrainPopulated", $chunk->isPopulated() ? 1 : 0);
$nbt->setByte("LightPopulated", $chunk->isLightPopulated() ? 1 : 0);
$ids = "";
$data = "";
@ -77,13 +77,13 @@ class McRegion extends BaseLevelProvider{
}
}
$nbt->Blocks = new ByteArrayTag("Blocks", $ids);
$nbt->Data = new ByteArrayTag("Data", $data);
$nbt->SkyLight = new ByteArrayTag("SkyLight", $skyLight);
$nbt->BlockLight = new ByteArrayTag("BlockLight", $blockLight);
$nbt->setByteArray("Blocks", $ids);
$nbt->setByteArray("Data", $data);
$nbt->setByteArray("SkyLight", $skyLight);
$nbt->setByteArray("BlockLight", $blockLight);
$nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); //doesn't exist in regular McRegion, this is here for PocketMine-MP only
$nbt->HeightMap = new ByteArrayTag("HeightMap", pack("C*", ...$chunk->getHeightMapArray()));
$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)
$entities = [];
@ -94,7 +94,7 @@ class McRegion extends BaseLevelProvider{
}
}
$nbt->Entities = new ListTag("Entities", $entities, NBT::TAG_Compound);
$nbt->setTag(new ListTag("Entities", $entities, NBT::TAG_Compound));
$tiles = [];
foreach($chunk->getTiles() as $tile){
@ -102,7 +102,7 @@ class McRegion extends BaseLevelProvider{
$tiles[] = $tile->namedtag;
}
$nbt->TileEntities = new ListTag("TileEntities", $tiles, NBT::TAG_Compound);
$nbt->setTag(new ListTag("TileEntities", $tiles, NBT::TAG_Compound));
$writer = new NBT(NBT::BIG_ENDIAN);
$nbt->setName("Level");
@ -281,11 +281,11 @@ class McRegion extends BaseLevelProvider{
}
public function getDifficulty() : int{
return isset($this->levelData->Difficulty) ? $this->levelData->Difficulty->getValue() : Level::DIFFICULTY_NORMAL;
return $this->levelData->getByte("Difficulty", Level::DIFFICULTY_NORMAL);
}
public function setDifficulty(int $difficulty){
$this->levelData->Difficulty = new ByteTag("Difficulty", $difficulty);
$this->levelData->setByte("Difficulty", $difficulty);
}
public function getChunk(int $chunkX, int $chunkZ, bool $create = false){

View File

@ -23,14 +23,9 @@ declare(strict_types=1);
namespace pocketmine\level\format\io\region;
use pocketmine\level\format\Chunk;
use pocketmine\level\format\ChunkException;
use pocketmine\level\format\SubChunk;
use pocketmine\nbt\NBT;
use pocketmine\nbt\tag\{
ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag
};
use pocketmine\utils\MainLogger;
use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\CompoundTag;
/**
* This format is exactly the same as the PC Anvil format, with the only difference being that the stored data order
@ -51,10 +46,10 @@ class PMAnvil extends Anvil{
protected function deserializeSubChunk(CompoundTag $subChunk) : SubChunk{
return new SubChunk(
$subChunk->Blocks->getValue(),
$subChunk->Data->getValue(),
$subChunk->SkyLight->getValue(),
$subChunk->BlockLight->getValue()
$subChunk->getByteArray("Blocks"),
$subChunk->getByteArray("Data"),
$subChunk->getByteArray("SkyLight"),
$subChunk->getByteArray("BlockLight")
);
}