From cdab3e967a36cb85ef10b91c3a39369e077164d4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 8 Jul 2019 14:37:48 +0100 Subject: [PATCH] Extract a LightArray unit from SubChunk --- src/pocketmine/world/format/Chunk.php | 4 +- src/pocketmine/world/format/EmptySubChunk.php | 12 +-- src/pocketmine/world/format/LightArray.php | 85 +++++++++++++++++++ src/pocketmine/world/format/SubChunk.php | 84 +++++------------- .../world/format/SubChunkInterface.php | 16 ++-- .../world/format/io/FastChunkSerializer.php | 7 +- 6 files changed, 124 insertions(+), 84 deletions(-) create mode 100644 src/pocketmine/world/format/LightArray.php diff --git a/src/pocketmine/world/format/Chunk.php b/src/pocketmine/world/format/Chunk.php index e6b2c2e6c..a0851eafb 100644 --- a/src/pocketmine/world/format/Chunk.php +++ b/src/pocketmine/world/format/Chunk.php @@ -224,7 +224,7 @@ class Chunk{ $char = chr(($level & 0x0f) | ($level << 4)); $data = str_repeat($char, 2048); for($y = $this->getHighestSubChunkIndex(); $y >= 0; --$y){ - $this->getSubChunk($y, true)->setBlockSkyLightArray($data); + $this->getSubChunk($y, true)->setBlockSkyLightArray(new LightArray($data)); } } @@ -260,7 +260,7 @@ class Chunk{ $char = chr(($level & 0x0f) | ($level << 4)); $data = str_repeat($char, 2048); for($y = $this->getHighestSubChunkIndex(); $y >= 0; --$y){ - $this->getSubChunk($y, true)->setBlockLightArray($data); + $this->getSubChunk($y, true)->setBlockLightArray(new LightArray($data)); } } diff --git a/src/pocketmine/world/format/EmptySubChunk.php b/src/pocketmine/world/format/EmptySubChunk.php index 8606f1d39..7cc607e87 100644 --- a/src/pocketmine/world/format/EmptySubChunk.php +++ b/src/pocketmine/world/format/EmptySubChunk.php @@ -73,19 +73,19 @@ class EmptySubChunk implements SubChunkInterface{ return -1; } - public function getBlockLightArray() : string{ - return str_repeat("\x00", 2048); + public function getBlockLightArray() : LightArray{ + return new LightArray(str_repeat("\x00", 2048)); } - public function setBlockLightArray(string $data) : void{ + public function setBlockLightArray(LightArray $data) : void{ } - public function getBlockSkyLightArray() : string{ - return str_repeat("\xff", 2048); + public function getBlockSkyLightArray() : LightArray{ + return new LightArray(str_repeat("\xff", 2048)); } - public function setBlockSkyLightArray(string $data) : void{ + public function setBlockSkyLightArray(LightArray $data) : void{ } } diff --git a/src/pocketmine/world/format/LightArray.php b/src/pocketmine/world/format/LightArray.php new file mode 100644 index 000000000..8822ddc12 --- /dev/null +++ b/src/pocketmine/world/format/LightArray.php @@ -0,0 +1,85 @@ +data = $payload ?? self::ZERO; + $this->collectGarbage(); + } + + public function get(int $x, int $y, int $z) : int{ + return (ord($this->data{($x << 7) | ($z << 3) | ($y >> 1)}) >> (($y & 1) << 2)) & 0xf; + } + + public function set(int $x, int $y, int $z, int $level) : void{ + $i = ($x << 7) | ($z << 3) | ($y >> 1); + + $shift = ($y & 1) << 2; + $byte = ord($this->data{$i}); + $this->data{$i} = chr(($byte & ~(0xf << $shift)) | (($level & 0xf) << $shift)); + } + + public function collectGarbage() : void{ + /* + * This strange looking code is designed to exploit PHP's copy-on-write behaviour. Assigning will copy a + * reference to the const instead of duplicating the whole string. The string will only be duplicated when + * modified, which is perfect for this purpose. + */ + if($this->data === ZERO_NIBBLE_ARRAY){ + $this->data = ZERO_NIBBLE_ARRAY; + }elseif($this->data === FIFTEEN_NIBBLE_ARRAY){ + $this->data = FIFTEEN_NIBBLE_ARRAY; + } + } + + public function getData() : string{ + return $this->data; + } +} diff --git a/src/pocketmine/world/format/SubChunk.php b/src/pocketmine/world/format/SubChunk.php index e9c132145..19021fd77 100644 --- a/src/pocketmine/world/format/SubChunk.php +++ b/src/pocketmine/world/format/SubChunk.php @@ -25,53 +25,28 @@ namespace pocketmine\world\format; use pocketmine\block\BlockLegacyIds; use function array_values; -use function assert; -use function chr; -use function define; -use function defined; -use function ord; -use function str_repeat; -use function strlen; - -if(!defined(__NAMESPACE__ . '\ZERO_NIBBLE_ARRAY')){ - define(__NAMESPACE__ . '\ZERO_NIBBLE_ARRAY', str_repeat("\x00", 2048)); -} -if(!defined(__NAMESPACE__ . '\FIFTEEN_NIBBLE_ARRAY')){ - define(__NAMESPACE__ . '\FIFTEEN_NIBBLE_ARRAY', str_repeat("\xff", 2048)); -} class SubChunk implements SubChunkInterface{ /** @var PalettedBlockArray[] */ private $blockLayers; - /** @var string */ + /** @var LightArray */ protected $blockLight; - /** @var string */ + /** @var LightArray */ protected $skyLight; - private static function assignData(&$target, string $data, string $default) : void{ - if($data === "" or $data === $default){ - $target = $default; - }elseif(strlen($data) !== 2048){ - assert(false, "Invalid length given, expected 2048, got " . strlen($data)); - $target = $default; - }else{ - $target = $data; - } - } - /** * SubChunk constructor. * * @param PalettedBlockArray[] $blocks - * @param string $skyLight - * @param string $blockLight + * @param LightArray|null $skyLight + * @param LightArray|null $blockLight */ - public function __construct(array $blocks, string $skyLight = "", string $blockLight = ""){ + public function __construct(array $blocks, ?LightArray $skyLight = null, ?LightArray $blockLight = null){ $this->blockLayers = $blocks; - self::assignData($this->skyLight, $skyLight, FIFTEEN_NIBBLE_ARRAY); - self::assignData($this->blockLight, $blockLight, ZERO_NIBBLE_ARRAY); + $this->skyLight = $skyLight ?? new LightArray(LightArray::FIFTEEN); + $this->blockLight = $blockLight ?? new LightArray(LightArray::ZERO); } public function isEmpty(bool $checkLight = true) : bool{ @@ -85,8 +60,8 @@ class SubChunk implements SubChunkInterface{ } return (!$checkLight or ( - $this->skyLight === FIFTEEN_NIBBLE_ARRAY and - $this->blockLight === ZERO_NIBBLE_ARRAY + $this->skyLight->getData() === LightArray::FIFTEEN and + $this->blockLight->getData() === LightArray::ZERO ) ); } @@ -113,29 +88,21 @@ class SubChunk implements SubChunkInterface{ } public function getBlockLight(int $x, int $y, int $z) : int{ - return (ord($this->blockLight{($x << 7) | ($z << 3) | ($y >> 1)}) >> (($y & 1) << 2)) & 0xf; + return $this->blockLight->get($x, $y, $z); } public function setBlockLight(int $x, int $y, int $z, int $level) : bool{ - $i = ($x << 7) | ($z << 3) | ($y >> 1); - - $shift = ($y & 1) << 2; - $byte = ord($this->blockLight{$i}); - $this->blockLight{$i} = chr(($byte & ~(0xf << $shift)) | (($level & 0xf) << $shift)); + $this->blockLight->set($x, $y, $z, $level); return true; } public function getBlockSkyLight(int $x, int $y, int $z) : int{ - return (ord($this->skyLight{($x << 7) | ($z << 3) | ($y >> 1)}) >> (($y & 1) << 2)) & 0xf; + return $this->skyLight->get($x, $y, $z); } public function setBlockSkyLight(int $x, int $y, int $z, int $level) : bool{ - $i = ($x << 7) | ($z << 3) | ($y >> 1); - - $shift = ($y & 1) << 2; - $byte = ord($this->skyLight{$i}); - $this->skyLight{$i} = chr(($byte & ~(0xf << $shift)) | (($level & 0xf) << $shift)); + $this->skyLight->set($x, $y, $z, $level); return true; } @@ -153,23 +120,19 @@ class SubChunk implements SubChunkInterface{ return -1; //highest block not in this subchunk } - public function getBlockSkyLightArray() : string{ - assert(strlen($this->skyLight) === 2048, "Wrong length of skylight array, expecting 2048 bytes, got " . strlen($this->skyLight)); + public function getBlockSkyLightArray() : LightArray{ return $this->skyLight; } - public function setBlockSkyLightArray(string $data) : void{ - assert(strlen($data) === 2048, "Wrong length of skylight array, expecting 2048 bytes, got " . strlen($data)); + public function setBlockSkyLightArray(LightArray $data) : void{ $this->skyLight = $data; } - public function getBlockLightArray() : string{ - assert(strlen($this->blockLight) === 2048, "Wrong length of light array, expecting 2048 bytes, got " . strlen($this->blockLight)); + public function getBlockLightArray() : LightArray{ return $this->blockLight; } - public function setBlockLightArray(string $data) : void{ - assert(strlen($data) === 2048, "Wrong length of light array, expecting 2048 bytes, got " . strlen($data)); + public function setBlockLightArray(LightArray $data) : void{ $this->blockLight = $data; } @@ -190,16 +153,7 @@ class SubChunk implements SubChunkInterface{ } $this->blockLayers = array_values($this->blockLayers); - /* - * This strange looking code is designed to exploit PHP's copy-on-write behaviour. Assigning will copy a - * reference to the const instead of duplicating the whole string. The string will only be duplicated when - * modified, which is perfect for this purpose. - */ - if($this->skyLight === ZERO_NIBBLE_ARRAY){ - $this->skyLight = ZERO_NIBBLE_ARRAY; - } - if($this->blockLight === ZERO_NIBBLE_ARRAY){ - $this->blockLight = ZERO_NIBBLE_ARRAY; - } + $this->skyLight->collectGarbage(); + $this->blockLight->collectGarbage(); } } diff --git a/src/pocketmine/world/format/SubChunkInterface.php b/src/pocketmine/world/format/SubChunkInterface.php index fff078c9b..bb7c5f01d 100644 --- a/src/pocketmine/world/format/SubChunkInterface.php +++ b/src/pocketmine/world/format/SubChunkInterface.php @@ -101,22 +101,22 @@ interface SubChunkInterface{ public function getHighestBlockAt(int $x, int $z) : int; /** - * @return string + * @return LightArray */ - public function getBlockSkyLightArray() : string; + public function getBlockSkyLightArray() : LightArray; /** - * @param string $data + * @param LightArray $data */ - public function setBlockSkyLightArray(string $data) : void; + public function setBlockSkyLightArray(LightArray $data) : void; /** - * @return string + * @return LightArray */ - public function getBlockLightArray() : string; + public function getBlockLightArray() : LightArray; /** - * @param string $data + * @param LightArray $data */ - public function setBlockLightArray(string $data) : void; + public function setBlockLightArray(LightArray $data) : void; } diff --git a/src/pocketmine/world/format/io/FastChunkSerializer.php b/src/pocketmine/world/format/io/FastChunkSerializer.php index 670d26afc..a4ab01c5c 100644 --- a/src/pocketmine/world/format/io/FastChunkSerializer.php +++ b/src/pocketmine/world/format/io/FastChunkSerializer.php @@ -26,6 +26,7 @@ namespace pocketmine\world\format\io; use pocketmine\utils\BinaryStream; use pocketmine\world\format\Chunk; use pocketmine\world\format\EmptySubChunk; +use pocketmine\world\format\LightArray; use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\SubChunk; use function array_values; @@ -82,8 +83,8 @@ final class FastChunkSerializer{ } if($chunk->isLightPopulated()){ - $subStream->put($subChunk->getBlockSkyLightArray()); - $subStream->put($subChunk->getBlockLightArray()); + $subStream->put($subChunk->getBlockSkyLightArray()->getData()); + $subStream->put($subChunk->getBlockLightArray()->getData()); } } $stream->putByte($count); @@ -134,7 +135,7 @@ final class FastChunkSerializer{ $layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette); } $subChunks[$y] = new SubChunk( - $layers, $lightPopulated ? $stream->get(2048) : "", $lightPopulated ? $stream->get(2048) : "" //blocklight + $layers, $lightPopulated ? new LightArray($stream->get(2048)) : null, $lightPopulated ? new LightArray($stream->get(2048)) : null ); }