mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-12 16:59: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));
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
||||
}
|
||||
}
|
||||
|
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 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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user