setInt("xPos", $chunk->getX()); $nbt->setInt("zPos", $chunk->getZ()); $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); $subChunks = []; foreach($chunk->getSubChunks() as $y => $subChunk){ if($subChunk->isEmpty()){ continue; } $tag = $this->serializeSubChunk($subChunk); $tag->setByte("Y", $y); $subChunks[] = $tag; } $nbt->setTag(new ListTag("Sections", $subChunks, NBT::TAG_Compound)); $nbt->setByteArray("Biomes", $chunk->getBiomeIdArray()); $nbt->setIntArray("HeightMap", $chunk->getHeightMapArray()); $entities = []; foreach($chunk->getEntities() as $entity){ if($entity->canSaveWithChunk() and !$entity->isClosed()){ $entity->saveNBT(); $entities[] = $entity->namedtag; } } $nbt->setTag(new ListTag("Entities", $entities, NBT::TAG_Compound)); $tiles = []; foreach($chunk->getTiles() as $tile){ $tile->saveNBT(); $tiles[] = $tile->namedtag; } $nbt->setTag(new ListTag("TileEntities", $tiles, NBT::TAG_Compound)); //TODO: TileTicks $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new CompoundTag("", [$nbt])); return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); } protected function serializeSubChunk(SubChunk $subChunk) : CompoundTag{ 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())) ]); } public function nbtDeserialize(string $data){ $nbt = new NBT(NBT::BIG_ENDIAN); try{ $nbt->readCompressed($data); $chunk = $nbt->getData()->getCompoundTag("Level"); if($chunk === null){ throw new ChunkException("Invalid NBT format"); } $subChunks = []; $subChunksTag = $chunk->getListTag("Sections") ?? []; foreach($subChunksTag as $subChunk){ if($subChunk instanceof CompoundTag){ $subChunks[$subChunk->getByte("Y")] = $this->deserializeSubChunk($subChunk); } } if($chunk->hasTag("BiomeColors", IntArrayTag::class)){ $biomeIds = ChunkUtils::convertBiomeColors($chunk->getIntArray("BiomeColors")); //Convert back to original format }else{ $biomeIds = $chunk->getByteArray("Biomes", "", true); } $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, $chunk->getIntArray("HeightMap", []) ); $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); return null; } } 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")) ); } public static function getProviderName() : string{ return "anvil"; } public static function getPcWorldFormatVersion() : int{ return 19133; //anvil } public function getWorldHeight() : int{ //TODO: add world height options return 256; } }