mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-08-19 09:30:32 +00:00
LightUpdate: Move propagation-specific state to a separate unit
this solves multiple architectural issues: - improves reusability by avoiding having old state info stick around to fuck stuff up - prevents access to propagation state from outside of propagation this also reduces the latent memory usage of light-updates after they have been used. TODO: we could probably change LightPropagationContext to LightPropagator and move all the propagation-specific code into it if we can solve the subchunk-iterator and effective light problems.
This commit is contained in:
parent
bde24d9279
commit
c20ac82fe6
54
src/world/light/LightPropagationContext.php
Normal file
54
src/world/light/LightPropagationContext.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?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\light;
|
||||||
|
|
||||||
|
final class LightPropagationContext{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \SplQueue
|
||||||
|
* @phpstan-var \SplQueue<array{int, int, int}>
|
||||||
|
*/
|
||||||
|
public $spreadQueue;
|
||||||
|
/**
|
||||||
|
* @var true[]
|
||||||
|
* @phpstan-var array<int, true>
|
||||||
|
*/
|
||||||
|
public $spreadVisited = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \SplQueue
|
||||||
|
* @phpstan-var \SplQueue<array{int, int, int, int}>
|
||||||
|
*/
|
||||||
|
public $removalQueue;
|
||||||
|
/**
|
||||||
|
* @var true[]
|
||||||
|
* @phpstan-var array<int, true>
|
||||||
|
*/
|
||||||
|
public $removalVisited = [];
|
||||||
|
|
||||||
|
public function __construct(){
|
||||||
|
$this->removalQueue = new \SplQueue();
|
||||||
|
$this->spreadQueue = new \SplQueue();
|
||||||
|
}
|
||||||
|
}
|
@ -44,27 +44,6 @@ abstract class LightUpdate{
|
|||||||
*/
|
*/
|
||||||
protected $updateNodes = [];
|
protected $updateNodes = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \SplQueue
|
|
||||||
* @phpstan-var \SplQueue<array{int, int, int}>
|
|
||||||
*/
|
|
||||||
protected $spreadQueue;
|
|
||||||
/**
|
|
||||||
* @var true[]
|
|
||||||
* @phpstan-var array<int, true>
|
|
||||||
*/
|
|
||||||
protected $spreadVisited = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \SplQueue
|
|
||||||
* @phpstan-var \SplQueue<array{int, int, int, int}>
|
|
||||||
*/
|
|
||||||
protected $removalQueue;
|
|
||||||
/**
|
|
||||||
* @var true[]
|
|
||||||
* @phpstan-var array<int, true>
|
|
||||||
*/
|
|
||||||
protected $removalVisited = [];
|
|
||||||
/** @var SubChunkIteratorManager */
|
/** @var SubChunkIteratorManager */
|
||||||
protected $subChunkHandler;
|
protected $subChunkHandler;
|
||||||
|
|
||||||
@ -78,9 +57,6 @@ abstract class LightUpdate{
|
|||||||
public function __construct(ChunkManager $world, \SplFixedArray $lightFilters){
|
public function __construct(ChunkManager $world, \SplFixedArray $lightFilters){
|
||||||
$this->lightFilters = $lightFilters;
|
$this->lightFilters = $lightFilters;
|
||||||
|
|
||||||
$this->removalQueue = new \SplQueue();
|
|
||||||
$this->spreadQueue = new \SplQueue();
|
|
||||||
|
|
||||||
$this->subChunkHandler = new SubChunkIteratorManager($world);
|
$this->subChunkHandler = new SubChunkIteratorManager($world);
|
||||||
$this->subChunkHandler->onSubChunkChange(\Closure::fromCallable([$this, 'updateLightArrayRef']));
|
$this->subChunkHandler->onSubChunkChange(\Closure::fromCallable([$this, 'updateLightArrayRef']));
|
||||||
}
|
}
|
||||||
@ -117,7 +93,8 @@ abstract class LightUpdate{
|
|||||||
$this->updateNodes[World::blockHash($x, $y, $z)] = [$x, $y, $z, $newLevel];
|
$this->updateNodes[World::blockHash($x, $y, $z)] = [$x, $y, $z, $newLevel];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepareNodes() : void{
|
private function prepareNodes() : LightPropagationContext{
|
||||||
|
$context = new LightPropagationContext();
|
||||||
foreach($this->updateNodes as $blockHash => [$x, $y, $z, $newLevel]){
|
foreach($this->updateNodes as $blockHash => [$x, $y, $z, $newLevel]){
|
||||||
if($this->subChunkHandler->moveTo($x, $y, $z, false)){
|
if($this->subChunkHandler->moveTo($x, $y, $z, false)){
|
||||||
$oldLevel = $this->currentLightArray->get($x & 0xf, $y & 0xf, $z & 0xf);
|
$oldLevel = $this->currentLightArray->get($x & 0xf, $y & 0xf, $z & 0xf);
|
||||||
@ -125,24 +102,25 @@ abstract class LightUpdate{
|
|||||||
if($oldLevel !== $newLevel){
|
if($oldLevel !== $newLevel){
|
||||||
$this->currentLightArray->set($x & 0xf, $y & 0xf, $z & 0xf, $newLevel);
|
$this->currentLightArray->set($x & 0xf, $y & 0xf, $z & 0xf, $newLevel);
|
||||||
if($oldLevel < $newLevel){ //light increased
|
if($oldLevel < $newLevel){ //light increased
|
||||||
$this->spreadVisited[$blockHash] = true;
|
$context->spreadVisited[$blockHash] = true;
|
||||||
$this->spreadQueue->enqueue([$x, $y, $z]);
|
$context->spreadQueue->enqueue([$x, $y, $z]);
|
||||||
}else{ //light removed
|
}else{ //light removed
|
||||||
$this->removalVisited[$blockHash] = true;
|
$context->removalVisited[$blockHash] = true;
|
||||||
$this->removalQueue->enqueue([$x, $y, $z, $oldLevel]);
|
$context->removalQueue->enqueue([$x, $y, $z, $oldLevel]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute() : int{
|
public function execute() : int{
|
||||||
$this->prepareNodes();
|
$context = $this->prepareNodes();
|
||||||
|
|
||||||
$touched = 0;
|
$touched = 0;
|
||||||
while(!$this->removalQueue->isEmpty()){
|
while(!$context->removalQueue->isEmpty()){
|
||||||
$touched++;
|
$touched++;
|
||||||
list($x, $y, $z, $oldAdjacentLight) = $this->removalQueue->dequeue();
|
[$x, $y, $z, $oldAdjacentLight] = $context->removalQueue->dequeue();
|
||||||
|
|
||||||
$points = [
|
$points = [
|
||||||
[$x + 1, $y, $z],
|
[$x + 1, $y, $z],
|
||||||
@ -153,21 +131,21 @@ abstract class LightUpdate{
|
|||||||
[$x, $y, $z - 1]
|
[$x, $y, $z - 1]
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($points as list($cx, $cy, $cz)){
|
foreach($points as [$cx, $cy, $cz]){
|
||||||
if($this->subChunkHandler->moveTo($cx, $cy, $cz, false)){
|
if($this->subChunkHandler->moveTo($cx, $cy, $cz, false)){
|
||||||
$this->computeRemoveLight($cx, $cy, $cz, $oldAdjacentLight);
|
$this->computeRemoveLight($cx, $cy, $cz, $oldAdjacentLight, $context);
|
||||||
}elseif($this->getEffectiveLight($cx, $cy, $cz) > 0 and !isset($this->spreadVisited[$index = World::blockHash($cx, $cy, $cz)])){
|
}elseif($this->getEffectiveLight($cx, $cy, $cz) > 0 and !isset($context->spreadVisited[$index = World::blockHash($cx, $cy, $cz)])){
|
||||||
$this->spreadVisited[$index] = true;
|
$context->spreadVisited[$index] = true;
|
||||||
$this->spreadQueue->enqueue([$cx, $cy, $cz]);
|
$context->spreadQueue->enqueue([$cx, $cy, $cz]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!$this->spreadQueue->isEmpty()){
|
while(!$context->spreadQueue->isEmpty()){
|
||||||
$touched++;
|
$touched++;
|
||||||
list($x, $y, $z) = $this->spreadQueue->dequeue();
|
[$x, $y, $z] = $context->spreadQueue->dequeue();
|
||||||
|
|
||||||
unset($this->spreadVisited[World::blockHash($x, $y, $z)]);
|
unset($context->spreadVisited[World::blockHash($x, $y, $z)]);
|
||||||
|
|
||||||
$newAdjacentLight = $this->getEffectiveLight($x, $y, $z);
|
$newAdjacentLight = $this->getEffectiveLight($x, $y, $z);
|
||||||
if($newAdjacentLight <= 0){
|
if($newAdjacentLight <= 0){
|
||||||
@ -183,9 +161,9 @@ abstract class LightUpdate{
|
|||||||
[$x, $y, $z - 1]
|
[$x, $y, $z - 1]
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($points as list($cx, $cy, $cz)){
|
foreach($points as [$cx, $cy, $cz]){
|
||||||
if($this->subChunkHandler->moveTo($cx, $cy, $cz, false)){
|
if($this->subChunkHandler->moveTo($cx, $cy, $cz, false)){
|
||||||
$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight);
|
$this->computeSpreadLight($cx, $cy, $cz, $newAdjacentLight, $context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,36 +171,36 @@ abstract class LightUpdate{
|
|||||||
return $touched;
|
return $touched;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function computeRemoveLight(int $x, int $y, int $z, int $oldAdjacentLevel) : void{
|
protected function computeRemoveLight(int $x, int $y, int $z, int $oldAdjacentLevel, LightPropagationContext $context) : void{
|
||||||
$current = $this->currentLightArray->get($x & 0xf, $y & 0xf, $z & 0xf);
|
$current = $this->currentLightArray->get($x & 0xf, $y & 0xf, $z & 0xf);
|
||||||
|
|
||||||
if($current !== 0 and $current < $oldAdjacentLevel){
|
if($current !== 0 and $current < $oldAdjacentLevel){
|
||||||
$this->currentLightArray->set($x & 0xf, $y & 0xf, $z & 0xf, 0);
|
$this->currentLightArray->set($x & 0xf, $y & 0xf, $z & 0xf, 0);
|
||||||
|
|
||||||
if(!isset($this->removalVisited[$index = World::blockHash($x, $y, $z)])){
|
if(!isset($context->removalVisited[$index = World::blockHash($x, $y, $z)])){
|
||||||
$this->removalVisited[$index] = true;
|
$context->removalVisited[$index] = true;
|
||||||
if($current > 1){
|
if($current > 1){
|
||||||
$this->removalQueue->enqueue([$x, $y, $z, $current]);
|
$context->removalQueue->enqueue([$x, $y, $z, $current]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}elseif($current >= $oldAdjacentLevel){
|
}elseif($current >= $oldAdjacentLevel){
|
||||||
if(!isset($this->spreadVisited[$index = World::blockHash($x, $y, $z)])){
|
if(!isset($context->spreadVisited[$index = World::blockHash($x, $y, $z)])){
|
||||||
$this->spreadVisited[$index] = true;
|
$context->spreadVisited[$index] = true;
|
||||||
$this->spreadQueue->enqueue([$x, $y, $z]);
|
$context->spreadQueue->enqueue([$x, $y, $z]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel) : void{
|
protected function computeSpreadLight(int $x, int $y, int $z, int $newAdjacentLevel, LightPropagationContext $context) : void{
|
||||||
$current = $this->currentLightArray->get($x & 0xf, $y & 0xf, $z & 0xf);
|
$current = $this->currentLightArray->get($x & 0xf, $y & 0xf, $z & 0xf);
|
||||||
$potentialLight = $newAdjacentLevel - $this->lightFilters[$this->subChunkHandler->currentSubChunk->getFullBlock($x & 0x0f, $y & 0x0f, $z & 0x0f)];
|
$potentialLight = $newAdjacentLevel - $this->lightFilters[$this->subChunkHandler->currentSubChunk->getFullBlock($x & 0x0f, $y & 0x0f, $z & 0x0f)];
|
||||||
|
|
||||||
if($current < $potentialLight){
|
if($current < $potentialLight){
|
||||||
$this->currentLightArray->set($x & 0xf, $y & 0xf, $z & 0xf, $potentialLight);
|
$this->currentLightArray->set($x & 0xf, $y & 0xf, $z & 0xf, $potentialLight);
|
||||||
|
|
||||||
if(!isset($this->spreadVisited[$index = World::blockHash($x, $y, $z)]) and $potentialLight > 1){
|
if(!isset($context->spreadVisited[$index = World::blockHash($x, $y, $z)]) and $potentialLight > 1){
|
||||||
$this->spreadVisited[$index] = true;
|
$context->spreadVisited[$index] = true;
|
||||||
$this->spreadQueue->enqueue([$x, $y, $z]);
|
$context->spreadQueue->enqueue([$x, $y, $z]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user