mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-13 01:09:44 +00:00
Extract a LightArray unit from SubChunk
This commit is contained in:
parent
2cab22fd38
commit
cdab3e967a
@ -224,7 +224,7 @@ class Chunk{
|
|||||||
$char = chr(($level & 0x0f) | ($level << 4));
|
$char = chr(($level & 0x0f) | ($level << 4));
|
||||||
$data = str_repeat($char, 2048);
|
$data = str_repeat($char, 2048);
|
||||||
for($y = $this->getHighestSubChunkIndex(); $y >= 0; --$y){
|
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));
|
$char = chr(($level & 0x0f) | ($level << 4));
|
||||||
$data = str_repeat($char, 2048);
|
$data = str_repeat($char, 2048);
|
||||||
for($y = $this->getHighestSubChunkIndex(); $y >= 0; --$y){
|
for($y = $this->getHighestSubChunkIndex(); $y >= 0; --$y){
|
||||||
$this->getSubChunk($y, true)->setBlockLightArray($data);
|
$this->getSubChunk($y, true)->setBlockLightArray(new LightArray($data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,19 +73,19 @@ class EmptySubChunk implements SubChunkInterface{
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockLightArray() : string{
|
public function getBlockLightArray() : LightArray{
|
||||||
return str_repeat("\x00", 2048);
|
return new LightArray(str_repeat("\x00", 2048));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBlockLightArray(string $data) : void{
|
public function setBlockLightArray(LightArray $data) : void{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockSkyLightArray() : string{
|
public function getBlockSkyLightArray() : LightArray{
|
||||||
return str_repeat("\xff", 2048);
|
return new LightArray(str_repeat("\xff", 2048));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBlockSkyLightArray(string $data) : void{
|
public function setBlockSkyLightArray(LightArray $data) : void{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
85
src/pocketmine/world/format/LightArray.php
Normal file
85
src/pocketmine/world/format/LightArray.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?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 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
final class LightArray{
|
||||||
|
|
||||||
|
public const ZERO = ZERO_NIBBLE_ARRAY;
|
||||||
|
public const FIFTEEN = FIFTEEN_NIBBLE_ARRAY;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $data;
|
||||||
|
|
||||||
|
public function __construct(?string $payload){
|
||||||
|
if($payload !== null and ($len = strlen($payload)) !== 2048){
|
||||||
|
throw new \InvalidArgumentException("Payload size must be 2048 bytes, but got $len bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
@ -25,53 +25,28 @@ namespace pocketmine\world\format;
|
|||||||
|
|
||||||
use pocketmine\block\BlockLegacyIds;
|
use pocketmine\block\BlockLegacyIds;
|
||||||
use function array_values;
|
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{
|
class SubChunk implements SubChunkInterface{
|
||||||
/** @var PalettedBlockArray[] */
|
/** @var PalettedBlockArray[] */
|
||||||
private $blockLayers;
|
private $blockLayers;
|
||||||
|
|
||||||
/** @var string */
|
/** @var LightArray */
|
||||||
protected $blockLight;
|
protected $blockLight;
|
||||||
/** @var string */
|
/** @var LightArray */
|
||||||
protected $skyLight;
|
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.
|
* SubChunk constructor.
|
||||||
*
|
*
|
||||||
* @param PalettedBlockArray[] $blocks
|
* @param PalettedBlockArray[] $blocks
|
||||||
* @param string $skyLight
|
* @param LightArray|null $skyLight
|
||||||
* @param string $blockLight
|
* @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;
|
$this->blockLayers = $blocks;
|
||||||
|
|
||||||
self::assignData($this->skyLight, $skyLight, FIFTEEN_NIBBLE_ARRAY);
|
$this->skyLight = $skyLight ?? new LightArray(LightArray::FIFTEEN);
|
||||||
self::assignData($this->blockLight, $blockLight, ZERO_NIBBLE_ARRAY);
|
$this->blockLight = $blockLight ?? new LightArray(LightArray::ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEmpty(bool $checkLight = true) : bool{
|
public function isEmpty(bool $checkLight = true) : bool{
|
||||||
@ -85,8 +60,8 @@ class SubChunk implements SubChunkInterface{
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
(!$checkLight or (
|
(!$checkLight or (
|
||||||
$this->skyLight === FIFTEEN_NIBBLE_ARRAY and
|
$this->skyLight->getData() === LightArray::FIFTEEN and
|
||||||
$this->blockLight === ZERO_NIBBLE_ARRAY
|
$this->blockLight->getData() === LightArray::ZERO
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -113,29 +88,21 @@ class SubChunk implements SubChunkInterface{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockLight(int $x, int $y, int $z) : int{
|
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{
|
public function setBlockLight(int $x, int $y, int $z, int $level) : bool{
|
||||||
$i = ($x << 7) | ($z << 3) | ($y >> 1);
|
$this->blockLight->set($x, $y, $z, $level);
|
||||||
|
|
||||||
$shift = ($y & 1) << 2;
|
|
||||||
$byte = ord($this->blockLight{$i});
|
|
||||||
$this->blockLight{$i} = chr(($byte & ~(0xf << $shift)) | (($level & 0xf) << $shift));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockSkyLight(int $x, int $y, int $z) : int{
|
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{
|
public function setBlockSkyLight(int $x, int $y, int $z, int $level) : bool{
|
||||||
$i = ($x << 7) | ($z << 3) | ($y >> 1);
|
$this->skyLight->set($x, $y, $z, $level);
|
||||||
|
|
||||||
$shift = ($y & 1) << 2;
|
|
||||||
$byte = ord($this->skyLight{$i});
|
|
||||||
$this->skyLight{$i} = chr(($byte & ~(0xf << $shift)) | (($level & 0xf) << $shift));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -153,23 +120,19 @@ class SubChunk implements SubChunkInterface{
|
|||||||
return -1; //highest block not in this subchunk
|
return -1; //highest block not in this subchunk
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockSkyLightArray() : string{
|
public function getBlockSkyLightArray() : LightArray{
|
||||||
assert(strlen($this->skyLight) === 2048, "Wrong length of skylight array, expecting 2048 bytes, got " . strlen($this->skyLight));
|
|
||||||
return $this->skyLight;
|
return $this->skyLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBlockSkyLightArray(string $data) : void{
|
public function setBlockSkyLightArray(LightArray $data) : void{
|
||||||
assert(strlen($data) === 2048, "Wrong length of skylight array, expecting 2048 bytes, got " . strlen($data));
|
|
||||||
$this->skyLight = $data;
|
$this->skyLight = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBlockLightArray() : string{
|
public function getBlockLightArray() : LightArray{
|
||||||
assert(strlen($this->blockLight) === 2048, "Wrong length of light array, expecting 2048 bytes, got " . strlen($this->blockLight));
|
|
||||||
return $this->blockLight;
|
return $this->blockLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBlockLightArray(string $data) : void{
|
public function setBlockLightArray(LightArray $data) : void{
|
||||||
assert(strlen($data) === 2048, "Wrong length of light array, expecting 2048 bytes, got " . strlen($data));
|
|
||||||
$this->blockLight = $data;
|
$this->blockLight = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,16 +153,7 @@ class SubChunk implements SubChunkInterface{
|
|||||||
}
|
}
|
||||||
$this->blockLayers = array_values($this->blockLayers);
|
$this->blockLayers = array_values($this->blockLayers);
|
||||||
|
|
||||||
/*
|
$this->skyLight->collectGarbage();
|
||||||
* This strange looking code is designed to exploit PHP's copy-on-write behaviour. Assigning will copy a
|
$this->blockLight->collectGarbage();
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,22 +101,22 @@ interface SubChunkInterface{
|
|||||||
public function getHighestBlockAt(int $x, int $z) : int;
|
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;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ namespace pocketmine\world\format\io;
|
|||||||
use pocketmine\utils\BinaryStream;
|
use pocketmine\utils\BinaryStream;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\format\EmptySubChunk;
|
use pocketmine\world\format\EmptySubChunk;
|
||||||
|
use pocketmine\world\format\LightArray;
|
||||||
use pocketmine\world\format\PalettedBlockArray;
|
use pocketmine\world\format\PalettedBlockArray;
|
||||||
use pocketmine\world\format\SubChunk;
|
use pocketmine\world\format\SubChunk;
|
||||||
use function array_values;
|
use function array_values;
|
||||||
@ -82,8 +83,8 @@ final class FastChunkSerializer{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($chunk->isLightPopulated()){
|
if($chunk->isLightPopulated()){
|
||||||
$subStream->put($subChunk->getBlockSkyLightArray());
|
$subStream->put($subChunk->getBlockSkyLightArray()->getData());
|
||||||
$subStream->put($subChunk->getBlockLightArray());
|
$subStream->put($subChunk->getBlockLightArray()->getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$stream->putByte($count);
|
$stream->putByte($count);
|
||||||
@ -134,7 +135,7 @@ final class FastChunkSerializer{
|
|||||||
$layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
|
$layers[] = PalettedBlockArray::fromData($bitsPerBlock, $words, $palette);
|
||||||
}
|
}
|
||||||
$subChunks[$y] = new SubChunk(
|
$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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user