Extract FastChunkSerializer unit from Chunk

this functionality doesn't directly pertain to Chunk functionality.
This commit is contained in:
Dylan K. Taylor 2019-06-14 18:19:46 +01:00
parent 92035ac2ec
commit 08de657c8d
4 changed files with 171 additions and 125 deletions

View File

@ -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.
*

View File

@ -0,0 +1,157 @@
<?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;
use pocketmine\utils\BinaryStream;
use pocketmine\world\format\Chunk;
use pocketmine\world\format\EmptySubChunk;
use pocketmine\world\format\PalettedBlockArray;
use pocketmine\world\format\SubChunk;
use function array_values;
use function count;
use function pack;
use function unpack;
/**
* This class provides a serializer used for transmitting chunks between chunks.
* The serialization format **is not intended for permanent storage** and may change without warning.
*/
final class FastChunkSerializer{
private function __construct(){
//NOOP
}
/**
* Fast-serializes the chunk for passing between threads
* TODO: tiles and entities
*
* @param Chunk $chunk
*
* @return string
*/
public static function serialize(Chunk $chunk) : string{
$stream = new BinaryStream();
$stream->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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}