Files
PocketMine-MP/src/world/format/io/region/McRegion.php
Dylan K. Taylor c10be0f346 WorldProvider: allow loadChunk() to return additional information about the loaded chunk data
this will be needed for dealing with #5733. I don't plan to fix that before 5.0, but we need to make the appropriate BC breaks now, before release.
2023-05-29 17:03:39 +01:00

133 lines
4.6 KiB
PHP

<?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\region;
use pocketmine\block\Block;
use pocketmine\block\BlockTypeIds;
use pocketmine\data\bedrock\BiomeIds;
use pocketmine\nbt\BigEndianNbtSerializer;
use pocketmine\nbt\NbtDataException;
use pocketmine\nbt\tag\ByteArrayTag;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntArrayTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\world\format\Chunk;
use pocketmine\world\format\io\ChunkData;
use pocketmine\world\format\io\ChunkUtils;
use pocketmine\world\format\io\exception\CorruptedChunkException;
use pocketmine\world\format\io\LoadedChunkData;
use pocketmine\world\format\PalettedBlockArray;
use pocketmine\world\format\SubChunk;
use function strlen;
use function zlib_decode;
class McRegion extends RegionWorldProvider{
/**
* @throws CorruptedChunkException
*/
protected function deserializeChunk(string $data) : ?LoadedChunkData{
$decompressed = @zlib_decode($data);
if($decompressed === false){
throw new CorruptedChunkException("Failed to decompress chunk NBT");
}
$nbt = new BigEndianNbtSerializer();
try{
$chunk = $nbt->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;
}
}