read($decompressed)->mustGetCompoundTag(); }catch(NbtDataException $e){ throw new CorruptedChunkException($e->getMessage(), 0, $e); } $chunk = $chunk->getTag("Level"); if(!($chunk instanceof CompoundTag)){ throw new CorruptedChunkException("'Level' key is missing from chunk NBT"); } $legacyGeneratedTag = $chunk->getTag("TerrainGenerated"); if($legacyGeneratedTag instanceof ByteTag && $legacyGeneratedTag->getValue() === 0){ //In legacy PM before 3.0, PM used to save MCRegion chunks even when they weren't generated. In these cases //(we'll see them in old worlds), some of the tags which we expect to always be present, will be missing. //If TerrainGenerated (PM-specific tag from the olden days) is false, toss the chunk data and don't bother //trying to read it. return null; } $makeBiomeArray = function(string $biomeIds) : PalettedBlockArray{ if(strlen($biomeIds) !== 256){ throw new CorruptedChunkException("Expected biome array to be exactly 256 bytes, got " . strlen($biomeIds)); } return ChunkUtils::extrapolate3DBiomes($biomeIds); }; if(($biomeColorsTag = $chunk->getTag("BiomeColors")) instanceof IntArrayTag){ $biomes3d = $makeBiomeArray(ChunkUtils::convertBiomeColors($biomeColorsTag->getValue())); //Convert back to original format }elseif(($biomesTag = $chunk->getTag("Biomes")) instanceof ByteArrayTag){ $biomes3d = $makeBiomeArray($biomesTag->getValue()); }else{ $biomes3d = new PalettedBlockArray(BiomeIds::OCEAN); } $subChunks = []; $fullIds = self::readFixedSizeByteArray($chunk, "Blocks", 32768); $fullData = self::readFixedSizeByteArray($chunk, "Data", 16384); for($y = 0; $y < 8; ++$y){ $subChunks[$y] = new SubChunk(BlockTypeIds::AIR << Block::INTERNAL_STATE_DATA_BITS, [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $y)], clone $biomes3d); } for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){ if(!isset($subChunks[$y])){ $subChunks[$y] = new SubChunk(BlockTypeIds::AIR << Block::INTERNAL_STATE_DATA_BITS, [], clone $biomes3d); } } return new LoadedChunkData( data: new ChunkData( new Chunk( $subChunks, $chunk->getByte("TerrainPopulated", 0) !== 0 ), ($entitiesTag = $chunk->getTag("Entities")) instanceof ListTag ? self::getCompoundList("Entities", $entitiesTag) : [], ($tilesTag = $chunk->getTag("TileEntities")) instanceof ListTag ? self::getCompoundList("TileEntities", $tilesTag) : [], ), upgraded: true, fixerFlags: LoadedChunkData::FIXER_FLAG_ALL ); } protected static function getRegionFileExtension() : string{ return "mcr"; } protected static function getPcWorldFormatVersion() : int{ return 19132; } public function getWorldMinY() : int{ return 0; } public function getWorldMaxY() : int{ //TODO: add world height options return 128; } }