mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-18 03:35:33 +00:00
extract a BiomeArray unit from Chunk
this now also properly validates data read from disk.
This commit is contained in:
parent
c30dd9f1b6
commit
82d361d75f
67
src/world/format/BiomeArray.php
Normal file
67
src/world/format/BiomeArray.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use function strlen;
|
||||||
|
|
||||||
|
final class BiomeArray{
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $payload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $payload ZZZZXXXX key bits
|
||||||
|
*/
|
||||||
|
public function __construct(string $payload){
|
||||||
|
if(strlen($payload) !== 256){
|
||||||
|
throw new \InvalidArgumentException("Biome array is expected to be exactly 256 bytes");
|
||||||
|
}
|
||||||
|
$this->payload = $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function idx(int $x, int $z) : int{
|
||||||
|
if($x >= 16 or $z >= 16){
|
||||||
|
throw new \InvalidArgumentException("x and z must be in the range 0-15");
|
||||||
|
}
|
||||||
|
return ($z << 4) | $x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(int $x, int $z) : int{
|
||||||
|
return ord($this->payload[self::idx($x, $z)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set(int $x, int $z, int $biomeId) : void{
|
||||||
|
if($biomeId < 0 or $biomeId >= 256){
|
||||||
|
throw new \InvalidArgumentException("Biome ID must be in the range 0-255");
|
||||||
|
}
|
||||||
|
$this->payload[self::idx($x, $z)] = chr($biomeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string ZZZZXXXX key bits
|
||||||
|
*/
|
||||||
|
public function getData() : string{
|
||||||
|
return $this->payload;
|
||||||
|
}
|
||||||
|
}
|
@ -87,7 +87,7 @@ class Chunk{
|
|||||||
*/
|
*/
|
||||||
protected $heightMap;
|
protected $heightMap;
|
||||||
|
|
||||||
/** @var string */
|
/** @var BiomeArray */
|
||||||
protected $biomeIds;
|
protected $biomeIds;
|
||||||
|
|
||||||
/** @var CompoundTag[]|null */
|
/** @var CompoundTag[]|null */
|
||||||
@ -102,7 +102,7 @@ class Chunk{
|
|||||||
* @param CompoundTag[] $tiles
|
* @param CompoundTag[] $tiles
|
||||||
* @param int[] $heightMap
|
* @param int[] $heightMap
|
||||||
*/
|
*/
|
||||||
public function __construct(int $chunkX, int $chunkZ, array $subChunks = [], ?array $entities = null, ?array $tiles = null, string $biomeIds = "", array $heightMap = []){
|
public function __construct(int $chunkX, int $chunkZ, array $subChunks = [], ?array $entities = null, ?array $tiles = null, ?BiomeArray $biomeIds = null, array $heightMap = []){
|
||||||
$this->x = $chunkX;
|
$this->x = $chunkX;
|
||||||
$this->z = $chunkZ;
|
$this->z = $chunkZ;
|
||||||
|
|
||||||
@ -120,12 +120,7 @@ class Chunk{
|
|||||||
$this->heightMap = \SplFixedArray::fromArray(array_fill(0, 256, $val));
|
$this->heightMap = \SplFixedArray::fromArray(array_fill(0, 256, $val));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strlen($biomeIds) === 256){
|
$this->biomeIds = $biomeIds ?? new BiomeArray(str_repeat("\x00", 256));
|
||||||
$this->biomeIds = $biomeIds;
|
|
||||||
}else{
|
|
||||||
assert($biomeIds === "", "Wrong BiomeIds value count, expected 256, got " . strlen($biomeIds));
|
|
||||||
$this->biomeIds = str_repeat("\x00", 256);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->NBTtiles = $tiles;
|
$this->NBTtiles = $tiles;
|
||||||
$this->NBTentities = $entities;
|
$this->NBTentities = $entities;
|
||||||
@ -359,7 +354,7 @@ class Chunk{
|
|||||||
* @return int 0-255
|
* @return int 0-255
|
||||||
*/
|
*/
|
||||||
public function getBiomeId(int $x, int $z) : int{
|
public function getBiomeId(int $x, int $z) : int{
|
||||||
return ord($this->biomeIds[($z << 4) | $x]);
|
return $this->biomeIds->get($x, $z);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,7 +365,7 @@ class Chunk{
|
|||||||
* @param int $biomeId 0-255
|
* @param int $biomeId 0-255
|
||||||
*/
|
*/
|
||||||
public function setBiomeId(int $x, int $z, int $biomeId) : void{
|
public function setBiomeId(int $x, int $z, int $biomeId) : void{
|
||||||
$this->biomeIds[($z << 4) | $x] = chr($biomeId & 0xff);
|
$this->biomeIds->set($x, $z, $biomeId);
|
||||||
$this->dirtyFlags |= self::DIRTY_FLAG_BIOMES;
|
$this->dirtyFlags |= self::DIRTY_FLAG_BIOMES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,7 +545,7 @@ class Chunk{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getBiomeIdArray() : string{
|
public function getBiomeIdArray() : string{
|
||||||
return $this->biomeIds;
|
return $this->biomeIds->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,6 +25,7 @@ namespace pocketmine\world\format\io;
|
|||||||
|
|
||||||
use pocketmine\block\BlockLegacyIds;
|
use pocketmine\block\BlockLegacyIds;
|
||||||
use pocketmine\utils\BinaryStream;
|
use pocketmine\utils\BinaryStream;
|
||||||
|
use pocketmine\world\format\BiomeArray;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\format\LightArray;
|
use pocketmine\world\format\LightArray;
|
||||||
use pocketmine\world\format\PalettedBlockArray;
|
use pocketmine\world\format\PalettedBlockArray;
|
||||||
@ -111,7 +112,7 @@ final class FastChunkSerializer{
|
|||||||
$terrainGenerated = (bool) ($flags & 1);
|
$terrainGenerated = (bool) ($flags & 1);
|
||||||
|
|
||||||
$subChunks = [];
|
$subChunks = [];
|
||||||
$biomeIds = "";
|
$biomeIds = null;
|
||||||
$heightMap = [];
|
$heightMap = [];
|
||||||
if($terrainGenerated){
|
if($terrainGenerated){
|
||||||
$count = $stream->getByte();
|
$count = $stream->getByte();
|
||||||
@ -132,7 +133,7 @@ final class FastChunkSerializer{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$biomeIds = $stream->get(256);
|
$biomeIds = new BiomeArray($stream->get(256));
|
||||||
if($lightPopulated){
|
if($lightPopulated){
|
||||||
$heightMap = array_values(unpack("S*", $stream->get(512)));
|
$heightMap = array_values(unpack("S*", $stream->get(512)));
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ use pocketmine\utils\Binary;
|
|||||||
use pocketmine\utils\BinaryDataException;
|
use pocketmine\utils\BinaryDataException;
|
||||||
use pocketmine\utils\BinaryStream;
|
use pocketmine\utils\BinaryStream;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
|
use pocketmine\world\format\BiomeArray;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\format\io\BaseWorldProvider;
|
use pocketmine\world\format\io\BaseWorldProvider;
|
||||||
use pocketmine\world\format\io\ChunkUtils;
|
use pocketmine\world\format\io\ChunkUtils;
|
||||||
@ -232,8 +233,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
/** @var SubChunk[] $subChunks */
|
/** @var SubChunk[] $subChunks */
|
||||||
$subChunks = [];
|
$subChunks = [];
|
||||||
|
|
||||||
/** @var string $biomeIds */
|
/** @var BiomeArray|null $biomeArray */
|
||||||
$biomeIds = "";
|
$biomeArray = null;
|
||||||
|
|
||||||
$chunkVersion = ord($this->db->get($index . self::TAG_VERSION));
|
$chunkVersion = ord($this->db->get($index . self::TAG_VERSION));
|
||||||
$hasBeenUpgraded = $chunkVersion < self::CURRENT_LEVEL_CHUNK_VERSION;
|
$hasBeenUpgraded = $chunkVersion < self::CURRENT_LEVEL_CHUNK_VERSION;
|
||||||
@ -326,7 +327,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
$binaryStream->get(512); //heightmap, discard it
|
$binaryStream->get(512); //heightmap, discard it
|
||||||
$biomeIds = $binaryStream->get(256);
|
$biomeArray = new BiomeArray($binaryStream->get(256)); //never throws
|
||||||
}catch(BinaryDataException $e){
|
}catch(BinaryDataException $e){
|
||||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||||
}
|
}
|
||||||
@ -360,7 +361,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
$binaryStream->get(256); //heightmap, discard it
|
$binaryStream->get(256); //heightmap, discard it
|
||||||
$biomeIds = ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024))));
|
$biomeArray = new BiomeArray(ChunkUtils::convertBiomeColors(array_values(unpack("N*", $binaryStream->get(1024))))); //never throws
|
||||||
}catch(BinaryDataException $e){
|
}catch(BinaryDataException $e){
|
||||||
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||||
}
|
}
|
||||||
@ -398,7 +399,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{
|
|||||||
$subChunks,
|
$subChunks,
|
||||||
$entities,
|
$entities,
|
||||||
$tiles,
|
$tiles,
|
||||||
$biomeIds
|
$biomeArray
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO: tile ticks, biome states (?)
|
//TODO: tile ticks, biome states (?)
|
||||||
|
@ -25,9 +25,11 @@ namespace pocketmine\world\format\io\region;
|
|||||||
|
|
||||||
use pocketmine\nbt\BigEndianNbtSerializer;
|
use pocketmine\nbt\BigEndianNbtSerializer;
|
||||||
use pocketmine\nbt\NbtDataException;
|
use pocketmine\nbt\NbtDataException;
|
||||||
|
use pocketmine\nbt\tag\ByteArrayTag;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
use pocketmine\nbt\tag\IntArrayTag;
|
use pocketmine\nbt\tag\IntArrayTag;
|
||||||
use pocketmine\nbt\tag\ListTag;
|
use pocketmine\nbt\tag\ListTag;
|
||||||
|
use pocketmine\world\format\BiomeArray;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\format\io\ChunkUtils;
|
use pocketmine\world\format\io\ChunkUtils;
|
||||||
use pocketmine\world\format\io\exception\CorruptedChunkException;
|
use pocketmine\world\format\io\exception\CorruptedChunkException;
|
||||||
@ -72,10 +74,18 @@ trait LegacyAnvilChunkTrait{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$makeBiomeArray = function(string $biomeIds) : BiomeArray{
|
||||||
|
try{
|
||||||
|
return new BiomeArray($biomeIds);
|
||||||
|
}catch(\InvalidArgumentException $e){
|
||||||
|
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$biomeArray = null;
|
||||||
if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
|
if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
|
||||||
$biomeIds = ChunkUtils::convertBiomeColors($chunk->getIntArray("BiomeColors")); //Convert back to original format
|
$biomeArray = $makeBiomeArray(ChunkUtils::convertBiomeColors($chunk->getIntArray("BiomeColors"))); //Convert back to original format
|
||||||
}else{
|
}elseif($chunk->hasTag("Biomes", ByteArrayTag::class)){
|
||||||
$biomeIds = $chunk->getByteArray("Biomes", "");
|
$biomeArray = $makeBiomeArray($chunk->getByteArray("Biomes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = new Chunk(
|
$result = new Chunk(
|
||||||
@ -84,7 +94,7 @@ trait LegacyAnvilChunkTrait{
|
|||||||
$subChunks,
|
$subChunks,
|
||||||
$chunk->hasTag("Entities", ListTag::class) ? self::getCompoundList("Entities", $chunk->getListTag("Entities")) : [],
|
$chunk->hasTag("Entities", ListTag::class) ? self::getCompoundList("Entities", $chunk->getListTag("Entities")) : [],
|
||||||
$chunk->hasTag("TileEntities", ListTag::class) ? self::getCompoundList("TileEntities", $chunk->getListTag("TileEntities")) : [],
|
$chunk->hasTag("TileEntities", ListTag::class) ? self::getCompoundList("TileEntities", $chunk->getListTag("TileEntities")) : [],
|
||||||
$biomeIds
|
$biomeArray
|
||||||
);
|
);
|
||||||
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
|
$result->setPopulated($chunk->getByte("TerrainPopulated", 0) !== 0);
|
||||||
$result->setGenerated();
|
$result->setGenerated();
|
||||||
|
@ -29,6 +29,7 @@ use pocketmine\nbt\NbtDataException;
|
|||||||
use pocketmine\nbt\tag\ByteArrayTag;
|
use pocketmine\nbt\tag\ByteArrayTag;
|
||||||
use pocketmine\nbt\tag\IntArrayTag;
|
use pocketmine\nbt\tag\IntArrayTag;
|
||||||
use pocketmine\nbt\tag\ListTag;
|
use pocketmine\nbt\tag\ListTag;
|
||||||
|
use pocketmine\world\format\BiomeArray;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\format\io\ChunkUtils;
|
use pocketmine\world\format\io\ChunkUtils;
|
||||||
use pocketmine\world\format\io\exception\CorruptedChunkException;
|
use pocketmine\world\format\io\exception\CorruptedChunkException;
|
||||||
@ -69,12 +70,18 @@ class McRegion extends RegionWorldProvider{
|
|||||||
$subChunks[$y] = new SubChunk(BlockLegacyIds::AIR << 4, [SubChunkConverter::convertSubChunkFromLegacyColumn($fullIds, $fullData, $y)]);
|
$subChunks[$y] = new SubChunk(BlockLegacyIds::AIR << 4, [SubChunkConverter::convertSubChunkFromLegacyColumn($fullIds, $fullData, $y)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$makeBiomeArray = function(string $biomeIds) : BiomeArray{
|
||||||
|
try{
|
||||||
|
return new BiomeArray($biomeIds);
|
||||||
|
}catch(\InvalidArgumentException $e){
|
||||||
|
throw new CorruptedChunkException($e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$biomeIds = null;
|
||||||
if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
|
if($chunk->hasTag("BiomeColors", IntArrayTag::class)){
|
||||||
$biomeIds = ChunkUtils::convertBiomeColors($chunk->getIntArray("BiomeColors")); //Convert back to original format
|
$biomeIds = $makeBiomeArray(ChunkUtils::convertBiomeColors($chunk->getIntArray("BiomeColors"))); //Convert back to original format
|
||||||
}elseif($chunk->hasTag("Biomes", ByteArrayTag::class)){
|
}elseif($chunk->hasTag("Biomes", ByteArrayTag::class)){
|
||||||
$biomeIds = $chunk->getByteArray("Biomes");
|
$biomeIds = $makeBiomeArray($chunk->getByteArray("Biomes"));
|
||||||
}else{
|
|
||||||
$biomeIds = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = new Chunk(
|
$result = new Chunk(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user