diff --git a/src/pocketmine/world/format/Chunk.php b/src/pocketmine/world/format/Chunk.php index d7af0f314..7fccb58ed 100644 --- a/src/pocketmine/world/format/Chunk.php +++ b/src/pocketmine/world/format/Chunk.php @@ -37,12 +37,10 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\NetworkBinaryStream; use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use pocketmine\Player; -use pocketmine\utils\BinaryStream; use pocketmine\world\World; use function array_fill; use function array_filter; use function array_map; -use function array_values; use function assert; use function chr; use function count; @@ -50,7 +48,6 @@ use function ord; use function pack; use function str_repeat; use function strlen; -use function unpack; class Chunk{ @@ -766,116 +763,6 @@ class Chunk{ return $stream->getBuffer(); } - /** - * Fast-serializes the chunk for passing between threads - * TODO: tiles and entities - * - * @return string - */ - public function fastSerialize() : string{ - $stream = new BinaryStream(); - $stream->putInt($this->x); - $stream->putInt($this->z); - $stream->putByte(($this->lightPopulated ? 4 : 0) | ($this->terrainPopulated ? 2 : 0) | ($this->terrainGenerated ? 1 : 0)); - if($this->terrainGenerated){ - //subchunks - $count = 0; - $subStream = new BinaryStream(); - foreach($this->subChunks as $y => $subChunk){ - if($subChunk instanceof EmptySubChunk){ - continue; - } - ++$count; - - $subStream->putByte($y); - $layers = $subChunk->getBlockLayers(); - $subStream->putByte(count($subChunk->getBlockLayers())); - foreach($layers as $blocks){ - $wordArray = $blocks->getWordArray(); - $palette = $blocks->getPalette(); - - $subStream->putByte($blocks->getBitsPerBlock()); - $subStream->put($wordArray); - $subStream->putInt(count($palette)); - foreach($palette as $p){ - $subStream->putInt($p); - } - } - - if($this->lightPopulated){ - $subStream->put($subChunk->getBlockSkyLightArray()); - $subStream->put($subChunk->getBlockLightArray()); - } - } - $stream->putByte($count); - $stream->put($subStream->getBuffer()); - - //biomes - $stream->put($this->biomeIds); - if($this->lightPopulated){ - $stream->put(pack("v*", ...$this->heightMap)); - } - } - - return $stream->getBuffer(); - } - - /** - * Deserializes a fast-serialized chunk - * - * @param string $data - * - * @return Chunk - */ - public static function fastDeserialize(string $data) : Chunk{ - $stream = new BinaryStream($data); - - $x = $stream->getInt(); - $z = $stream->getInt(); - $flags = $stream->getByte(); - $lightPopulated = (bool) ($flags & 4); - $terrainPopulated = (bool) ($flags & 2); - $terrainGenerated = (bool) ($flags & 1); - - $subChunks = []; - $biomeIds = ""; - $heightMap = []; - if($terrainGenerated){ - $count = $stream->getByte(); - for($subCount = 0; $subCount < $count; ++$subCount){ - $y = $stream->getByte(); - - /** @var PalettedBlockArray[] $layers */ - $layers = []; - for($i = 0, $layerCount = $stream->getByte(); $i < $layerCount; ++$i){ - $bitsPerBlock = $stream->getByte(); - $words = $stream->get(PalettedBlockArray::getExpectedWordArraySize($bitsPerBlock)); - $palette = []; - for($k = 0, $paletteSize = $stream->getInt(); $k < $paletteSize; ++$k){ - $palette[] = $stream->getInt(); - } - - $layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette); - } - $subChunks[$y] = new SubChunk( - $layers, $lightPopulated ? $stream->get(2048) : "", $lightPopulated ? $stream->get(2048) : "" //blocklight - ); - } - - $biomeIds = $stream->get(256); - if($lightPopulated){ - $heightMap = array_values(unpack("v*", $stream->get(512))); - } - } - - $chunk = new Chunk($x, $z, $subChunks, null, null, $biomeIds, $heightMap); - $chunk->setGenerated($terrainGenerated); - $chunk->setPopulated($terrainPopulated); - $chunk->setLightPopulated($lightPopulated); - - return $chunk; - } - /** * Hashes the given chunk block coordinates into a single integer. * diff --git a/src/pocketmine/world/format/io/FastChunkSerializer.php b/src/pocketmine/world/format/io/FastChunkSerializer.php new file mode 100644 index 000000000..b86b05446 --- /dev/null +++ b/src/pocketmine/world/format/io/FastChunkSerializer.php @@ -0,0 +1,157 @@ +putInt($chunk->getX()); + $stream->putInt($chunk->getZ()); + $stream->putByte(($chunk->isLightPopulated() ? 4 : 0) | ($chunk->isPopulated() ? 2 : 0) | ($chunk->isGenerated() ? 1 : 0)); + if($chunk->isGenerated()){ + //subchunks + $count = 0; + $subStream = new BinaryStream(); + foreach($chunk->getSubChunks() as $y => $subChunk){ + if($subChunk instanceof EmptySubChunk){ + continue; + } + ++$count; + + $subStream->putByte($y); + $layers = $subChunk->getBlockLayers(); + $subStream->putByte(count($subChunk->getBlockLayers())); + foreach($layers as $blocks){ + $wordArray = $blocks->getWordArray(); + $palette = $blocks->getPalette(); + + $subStream->putByte($blocks->getBitsPerBlock()); + $subStream->put($wordArray); + $subStream->putInt(count($palette)); + foreach($palette as $p){ + $subStream->putInt($p); + } + } + + if($chunk->isLightPopulated()){ + $subStream->put($subChunk->getBlockSkyLightArray()); + $subStream->put($subChunk->getBlockLightArray()); + } + } + $stream->putByte($count); + $stream->put($subStream->getBuffer()); + + //biomes + $stream->put($chunk->getBiomeIdArray()); + if($chunk->isLightPopulated()){ + $stream->put(pack("v*", ...$chunk->getHeightMapArray())); + } + } + + return $stream->getBuffer(); + } + + /** + * Deserializes a fast-serialized chunk + * + * @param string $data + * + * @return Chunk + */ + public static function deserialize(string $data) : Chunk{ + $stream = new BinaryStream($data); + + $x = $stream->getInt(); + $z = $stream->getInt(); + $flags = $stream->getByte(); + $lightPopulated = (bool) ($flags & 4); + $terrainPopulated = (bool) ($flags & 2); + $terrainGenerated = (bool) ($flags & 1); + + $subChunks = []; + $biomeIds = ""; + $heightMap = []; + if($terrainGenerated){ + $count = $stream->getByte(); + for($subCount = 0; $subCount < $count; ++$subCount){ + $y = $stream->getByte(); + + /** @var PalettedBlockArray[] $layers */ + $layers = []; + for($i = 0, $layerCount = $stream->getByte(); $i < $layerCount; ++$i){ + $bitsPerBlock = $stream->getByte(); + $words = $stream->get(PalettedBlockArray::getExpectedWordArraySize($bitsPerBlock)); + $palette = []; + for($k = 0, $paletteSize = $stream->getInt(); $k < $paletteSize; ++$k){ + $palette[] = $stream->getInt(); + } + + $layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette); + } + $subChunks[$y] = new SubChunk( + $layers, $lightPopulated ? $stream->get(2048) : "", $lightPopulated ? $stream->get(2048) : "" //blocklight + ); + } + + $biomeIds = $stream->get(256); + if($lightPopulated){ + $heightMap = array_values(unpack("v*", $stream->get(512))); + } + } + + $chunk = new Chunk($x, $z, $subChunks, null, null, $biomeIds, $heightMap); + $chunk->setGenerated($terrainGenerated); + $chunk->setPopulated($terrainPopulated); + $chunk->setLightPopulated($lightPopulated); + + return $chunk; + } +} diff --git a/src/pocketmine/world/generator/PopulationTask.php b/src/pocketmine/world/generator/PopulationTask.php index f599dce8e..76b715e84 100644 --- a/src/pocketmine/world/generator/PopulationTask.php +++ b/src/pocketmine/world/generator/PopulationTask.php @@ -25,6 +25,7 @@ namespace pocketmine\world\generator; use pocketmine\scheduler\AsyncTask; use pocketmine\world\format\Chunk; +use pocketmine\world\format\io\FastChunkSerializer; use pocketmine\world\SimpleChunkManager; use pocketmine\world\World; @@ -48,10 +49,10 @@ class PopulationTask extends AsyncTask{ public function __construct(World $world, Chunk $chunk){ $this->state = true; $this->worldId = $world->getId(); - $this->chunk = $chunk->fastSerialize(); + $this->chunk = FastChunkSerializer::serialize($chunk); foreach($world->getAdjacentChunks($chunk->getX(), $chunk->getZ()) as $i => $c){ - $this->{"chunk$i"} = $c !== null ? $c->fastSerialize() : null; + $this->{"chunk$i"} = $c !== null ? FastChunkSerializer::serialize($c) : null; } $this->storeLocal(self::TLS_KEY_WORLD, $world); @@ -70,7 +71,7 @@ class PopulationTask extends AsyncTask{ /** @var Chunk[] $chunks */ $chunks = []; - $chunk = Chunk::fastDeserialize($this->chunk); + $chunk = FastChunkSerializer::deserialize($this->chunk); for($i = 0; $i < 9; ++$i){ if($i === 4){ @@ -82,7 +83,7 @@ class PopulationTask extends AsyncTask{ if($ck === null){ $chunks[$i] = new Chunk($chunk->getX() + $xx, $chunk->getZ() + $zz); }else{ - $chunks[$i] = Chunk::fastDeserialize($ck); + $chunks[$i] = FastChunkSerializer::deserialize($ck); } } @@ -110,7 +111,7 @@ class PopulationTask extends AsyncTask{ $chunk->populateSkyLight(); $chunk->setLightPopulated(); $chunk->setPopulated(); - $this->chunk = $chunk->fastSerialize(); + $this->chunk = FastChunkSerializer::serialize($chunk); $manager->setChunk($chunk->getX(), $chunk->getZ(), null); @@ -133,7 +134,7 @@ class PopulationTask extends AsyncTask{ continue; } - $this->{"chunk$i"} = $chunks[$i] !== null ? $chunks[$i]->fastSerialize() : null; + $this->{"chunk$i"} = $chunks[$i] !== null ? FastChunkSerializer::serialize($chunks[$i]) : null; } } @@ -145,7 +146,7 @@ class PopulationTask extends AsyncTask{ $world->registerGeneratorToWorker($this->worker->getAsyncWorkerId()); } - $chunk = Chunk::fastDeserialize($this->chunk); + $chunk = FastChunkSerializer::deserialize($this->chunk); for($i = 0; $i < 9; ++$i){ if($i === 4){ @@ -153,7 +154,7 @@ class PopulationTask extends AsyncTask{ } $c = $this->{"chunk$i"}; if($c !== null){ - $c = Chunk::fastDeserialize($c); + $c = FastChunkSerializer::deserialize($c); $world->generateChunkCallback($c->getX(), $c->getZ(), $this->state ? $c : null); } } diff --git a/src/pocketmine/world/light/LightPopulationTask.php b/src/pocketmine/world/light/LightPopulationTask.php index 1c1e7c7e8..2583f138d 100644 --- a/src/pocketmine/world/light/LightPopulationTask.php +++ b/src/pocketmine/world/light/LightPopulationTask.php @@ -26,6 +26,7 @@ namespace pocketmine\world\light; use pocketmine\block\BlockFactory; use pocketmine\scheduler\AsyncTask; use pocketmine\world\format\Chunk; +use pocketmine\world\format\io\FastChunkSerializer; use pocketmine\world\World; class LightPopulationTask extends AsyncTask{ @@ -35,7 +36,7 @@ class LightPopulationTask extends AsyncTask{ public function __construct(World $world, Chunk $chunk){ $this->storeLocal(self::TLS_KEY_WORLD, $world); - $this->chunk = $chunk->fastSerialize(); + $this->chunk = FastChunkSerializer::serialize($chunk); } public function onRun() : void{ @@ -43,13 +44,13 @@ class LightPopulationTask extends AsyncTask{ BlockFactory::init(); } /** @var Chunk $chunk */ - $chunk = Chunk::fastDeserialize($this->chunk); + $chunk = FastChunkSerializer::deserialize($this->chunk); $chunk->recalculateHeightMap(); $chunk->populateSkyLight(); $chunk->setLightPopulated(); - $this->chunk = $chunk->fastSerialize(); + $this->chunk = FastChunkSerializer::serialize($chunk); } public function onCompletion() : void{ @@ -57,7 +58,7 @@ class LightPopulationTask extends AsyncTask{ $world = $this->fetchLocal(self::TLS_KEY_WORLD); if(!$world->isClosed()){ /** @var Chunk $chunk */ - $chunk = Chunk::fastDeserialize($this->chunk); + $chunk = FastChunkSerializer::deserialize($this->chunk); $world->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); } }