mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 09:56:06 +00:00
migrate to new CompoundTag API (#1515)
This commit is contained in:
@ -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(){
|
||||
|
@ -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{
|
||||
|
@ -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"))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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){
|
||||
|
@ -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")
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user