mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-13 09:19:42 +00:00
Extract FastChunkSerializer unit from Chunk
this functionality doesn't directly pertain to Chunk functionality.
This commit is contained in:
parent
92035ac2ec
commit
08de657c8d
@ -37,12 +37,10 @@ use pocketmine\nbt\tag\CompoundTag;
|
|||||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||||
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||||
use pocketmine\Player;
|
use pocketmine\Player;
|
||||||
use pocketmine\utils\BinaryStream;
|
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
use function array_fill;
|
use function array_fill;
|
||||||
use function array_filter;
|
use function array_filter;
|
||||||
use function array_map;
|
use function array_map;
|
||||||
use function array_values;
|
|
||||||
use function assert;
|
use function assert;
|
||||||
use function chr;
|
use function chr;
|
||||||
use function count;
|
use function count;
|
||||||
@ -50,7 +48,6 @@ use function ord;
|
|||||||
use function pack;
|
use function pack;
|
||||||
use function str_repeat;
|
use function str_repeat;
|
||||||
use function strlen;
|
use function strlen;
|
||||||
use function unpack;
|
|
||||||
|
|
||||||
class Chunk{
|
class Chunk{
|
||||||
|
|
||||||
@ -766,116 +763,6 @@ class Chunk{
|
|||||||
return $stream->getBuffer();
|
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.
|
* Hashes the given chunk block coordinates into a single integer.
|
||||||
*
|
*
|
||||||
|
157
src/pocketmine/world/format/io/FastChunkSerializer.php
Normal file
157
src/pocketmine/world/format/io/FastChunkSerializer.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ namespace pocketmine\world\generator;
|
|||||||
|
|
||||||
use pocketmine\scheduler\AsyncTask;
|
use pocketmine\scheduler\AsyncTask;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
|
use pocketmine\world\format\io\FastChunkSerializer;
|
||||||
use pocketmine\world\SimpleChunkManager;
|
use pocketmine\world\SimpleChunkManager;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
|
|
||||||
@ -48,10 +49,10 @@ class PopulationTask extends AsyncTask{
|
|||||||
public function __construct(World $world, Chunk $chunk){
|
public function __construct(World $world, Chunk $chunk){
|
||||||
$this->state = true;
|
$this->state = true;
|
||||||
$this->worldId = $world->getId();
|
$this->worldId = $world->getId();
|
||||||
$this->chunk = $chunk->fastSerialize();
|
$this->chunk = FastChunkSerializer::serialize($chunk);
|
||||||
|
|
||||||
foreach($world->getAdjacentChunks($chunk->getX(), $chunk->getZ()) as $i => $c){
|
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);
|
$this->storeLocal(self::TLS_KEY_WORLD, $world);
|
||||||
@ -70,7 +71,7 @@ class PopulationTask extends AsyncTask{
|
|||||||
/** @var Chunk[] $chunks */
|
/** @var Chunk[] $chunks */
|
||||||
$chunks = [];
|
$chunks = [];
|
||||||
|
|
||||||
$chunk = Chunk::fastDeserialize($this->chunk);
|
$chunk = FastChunkSerializer::deserialize($this->chunk);
|
||||||
|
|
||||||
for($i = 0; $i < 9; ++$i){
|
for($i = 0; $i < 9; ++$i){
|
||||||
if($i === 4){
|
if($i === 4){
|
||||||
@ -82,7 +83,7 @@ class PopulationTask extends AsyncTask{
|
|||||||
if($ck === null){
|
if($ck === null){
|
||||||
$chunks[$i] = new Chunk($chunk->getX() + $xx, $chunk->getZ() + $zz);
|
$chunks[$i] = new Chunk($chunk->getX() + $xx, $chunk->getZ() + $zz);
|
||||||
}else{
|
}else{
|
||||||
$chunks[$i] = Chunk::fastDeserialize($ck);
|
$chunks[$i] = FastChunkSerializer::deserialize($ck);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ class PopulationTask extends AsyncTask{
|
|||||||
$chunk->populateSkyLight();
|
$chunk->populateSkyLight();
|
||||||
$chunk->setLightPopulated();
|
$chunk->setLightPopulated();
|
||||||
$chunk->setPopulated();
|
$chunk->setPopulated();
|
||||||
$this->chunk = $chunk->fastSerialize();
|
$this->chunk = FastChunkSerializer::serialize($chunk);
|
||||||
|
|
||||||
$manager->setChunk($chunk->getX(), $chunk->getZ(), null);
|
$manager->setChunk($chunk->getX(), $chunk->getZ(), null);
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ class PopulationTask extends AsyncTask{
|
|||||||
continue;
|
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());
|
$world->registerGeneratorToWorker($this->worker->getAsyncWorkerId());
|
||||||
}
|
}
|
||||||
|
|
||||||
$chunk = Chunk::fastDeserialize($this->chunk);
|
$chunk = FastChunkSerializer::deserialize($this->chunk);
|
||||||
|
|
||||||
for($i = 0; $i < 9; ++$i){
|
for($i = 0; $i < 9; ++$i){
|
||||||
if($i === 4){
|
if($i === 4){
|
||||||
@ -153,7 +154,7 @@ class PopulationTask extends AsyncTask{
|
|||||||
}
|
}
|
||||||
$c = $this->{"chunk$i"};
|
$c = $this->{"chunk$i"};
|
||||||
if($c !== null){
|
if($c !== null){
|
||||||
$c = Chunk::fastDeserialize($c);
|
$c = FastChunkSerializer::deserialize($c);
|
||||||
$world->generateChunkCallback($c->getX(), $c->getZ(), $this->state ? $c : null);
|
$world->generateChunkCallback($c->getX(), $c->getZ(), $this->state ? $c : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ namespace pocketmine\world\light;
|
|||||||
use pocketmine\block\BlockFactory;
|
use pocketmine\block\BlockFactory;
|
||||||
use pocketmine\scheduler\AsyncTask;
|
use pocketmine\scheduler\AsyncTask;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
|
use pocketmine\world\format\io\FastChunkSerializer;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
|
|
||||||
class LightPopulationTask extends AsyncTask{
|
class LightPopulationTask extends AsyncTask{
|
||||||
@ -35,7 +36,7 @@ class LightPopulationTask extends AsyncTask{
|
|||||||
|
|
||||||
public function __construct(World $world, Chunk $chunk){
|
public function __construct(World $world, Chunk $chunk){
|
||||||
$this->storeLocal(self::TLS_KEY_WORLD, $world);
|
$this->storeLocal(self::TLS_KEY_WORLD, $world);
|
||||||
$this->chunk = $chunk->fastSerialize();
|
$this->chunk = FastChunkSerializer::serialize($chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRun() : void{
|
public function onRun() : void{
|
||||||
@ -43,13 +44,13 @@ class LightPopulationTask extends AsyncTask{
|
|||||||
BlockFactory::init();
|
BlockFactory::init();
|
||||||
}
|
}
|
||||||
/** @var Chunk $chunk */
|
/** @var Chunk $chunk */
|
||||||
$chunk = Chunk::fastDeserialize($this->chunk);
|
$chunk = FastChunkSerializer::deserialize($this->chunk);
|
||||||
|
|
||||||
$chunk->recalculateHeightMap();
|
$chunk->recalculateHeightMap();
|
||||||
$chunk->populateSkyLight();
|
$chunk->populateSkyLight();
|
||||||
$chunk->setLightPopulated();
|
$chunk->setLightPopulated();
|
||||||
|
|
||||||
$this->chunk = $chunk->fastSerialize();
|
$this->chunk = FastChunkSerializer::serialize($chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onCompletion() : void{
|
public function onCompletion() : void{
|
||||||
@ -57,7 +58,7 @@ class LightPopulationTask extends AsyncTask{
|
|||||||
$world = $this->fetchLocal(self::TLS_KEY_WORLD);
|
$world = $this->fetchLocal(self::TLS_KEY_WORLD);
|
||||||
if(!$world->isClosed()){
|
if(!$world->isClosed()){
|
||||||
/** @var Chunk $chunk */
|
/** @var Chunk $chunk */
|
||||||
$chunk = Chunk::fastDeserialize($this->chunk);
|
$chunk = FastChunkSerializer::deserialize($this->chunk);
|
||||||
$world->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
$world->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user