From ceb6529ee39ec6de433bc7ecfdbeb98a3b707128 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 4 Jul 2019 19:55:48 +0100 Subject: [PATCH] encapsulate light recalculation logic inside LightUpdate classes now we can do a standard light recalculation from anywhere. --- src/pocketmine/world/World.php | 36 +---------------- .../world/light/BlockLightUpdate.php | 8 ++++ src/pocketmine/world/light/LightUpdate.php | 20 ++++++++++ src/pocketmine/world/light/SkyLightUpdate.php | 39 +++++++++++++++++++ 4 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/pocketmine/world/World.php b/src/pocketmine/world/World.php index d62276639..c4bedd673 100644 --- a/src/pocketmine/world/World.php +++ b/src/pocketmine/world/World.php @@ -1397,39 +1397,10 @@ class World implements ChunkManager{ public function updateBlockSkyLight(int $x, int $y, int $z){ $this->timings->doBlockSkyLightUpdates->startTiming(); - $oldHeightMap = $this->getHeightMap($x, $z); - $source = $this->getBlockAt($x, $y, $z); - - $yPlusOne = $y + 1; - - if($yPlusOne === $oldHeightMap){ //Block changed directly beneath the heightmap. Check if a block was removed or changed to a different light-filter. - $newHeightMap = $this->getChunk($x >> 4, $z >> 4)->recalculateHeightMapColumn($x & 0x0f, $z & 0x0f); - }elseif($yPlusOne > $oldHeightMap){ //Block changed above the heightmap. - if($source->getLightFilter() > 0 or $source->diffusesSkyLight()){ - $this->setHeightMap($x, $z, $yPlusOne); - $newHeightMap = $yPlusOne; - }else{ //Block changed which has no effect on direct sky light, for example placing or removing glass. - $this->timings->doBlockSkyLightUpdates->stopTiming(); - return; - } - }else{ //Block changed below heightmap - $newHeightMap = $oldHeightMap; - } - if($this->skyLightUpdate === null){ $this->skyLightUpdate = new SkyLightUpdate($this); } - if($newHeightMap > $oldHeightMap){ //Heightmap increase, block placed, remove sky light - for($i = $y; $i >= $oldHeightMap; --$i){ - $this->skyLightUpdate->setAndUpdateLight($x, $i, $z, 0); //Remove all light beneath, adjacent recalculation will handle the rest. - } - }elseif($newHeightMap < $oldHeightMap){ //Heightmap decrease, block changed or removed, add sky light - for($i = $y; $i >= $newHeightMap; --$i){ - $this->skyLightUpdate->setAndUpdateLight($x, $i, $z, 15); - } - }else{ //No heightmap change, block changed "underground" - $this->skyLightUpdate->setAndUpdateLight($x, $y, $z, max(0, $this->getHighestAdjacentBlockSkyLight($x, $y, $z) - BlockFactory::$lightFilter[($source->getId() << 4) | $source->getMeta()])); - } + $this->skyLightUpdate->recalculateNode($x, $y, $z); $this->timings->doBlockSkyLightUpdates->stopTiming(); } @@ -1457,13 +1428,10 @@ class World implements ChunkManager{ public function updateBlockLight(int $x, int $y, int $z){ $this->timings->doBlockLightUpdates->startTiming(); - $block = $this->getBlockAt($x, $y, $z); - $newLevel = max($block->getLightLevel(), $this->getHighestAdjacentBlockLight($x, $y, $z) - BlockFactory::$lightFilter[($block->getId() << 4) | $block->getMeta()]); - if($this->blockLightUpdate === null){ $this->blockLightUpdate = new BlockLightUpdate($this); } - $this->blockLightUpdate->setAndUpdateLight($x, $y, $z, $newLevel); + $this->blockLightUpdate->recalculateNode($x, $y, $z); $this->timings->doBlockLightUpdates->stopTiming(); } diff --git a/src/pocketmine/world/light/BlockLightUpdate.php b/src/pocketmine/world/light/BlockLightUpdate.php index bfc014fd4..1fbaf04c3 100644 --- a/src/pocketmine/world/light/BlockLightUpdate.php +++ b/src/pocketmine/world/light/BlockLightUpdate.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\world\light; +use pocketmine\block\BlockFactory; +use function max; + class BlockLightUpdate extends LightUpdate{ public function getLight(int $x, int $y, int $z) : int{ @@ -32,4 +35,9 @@ class BlockLightUpdate extends LightUpdate{ public function setLight(int $x, int $y, int $z, int $level) : void{ $this->subChunkHandler->currentSubChunk->setBlockLight($x & 0x0f, $y & 0x0f, $z & 0x0f, $level); } + + public function recalculateNode(int $x, int $y, int $z) : void{ + $block = $this->world->getBlockAt($x, $y, $z); + $this->setAndUpdateLight($x, $y, $z, max($block->getLightLevel(), $this->getHighestAdjacentLight($x, $y, $z) - BlockFactory::$lightFilter[$block->getFullId()])); + } } diff --git a/src/pocketmine/world/light/LightUpdate.php b/src/pocketmine/world/light/LightUpdate.php index cf36908ac..cc3bee6af 100644 --- a/src/pocketmine/world/light/LightUpdate.php +++ b/src/pocketmine/world/light/LightUpdate.php @@ -27,6 +27,7 @@ use pocketmine\block\BlockFactory; use pocketmine\world\ChunkManager; use pocketmine\world\utils\SubChunkIteratorManager; use pocketmine\world\World; +use function max; //TODO: make light updates asynchronous abstract class LightUpdate{ @@ -61,6 +62,25 @@ abstract class LightUpdate{ abstract protected function setLight(int $x, int $y, int $z, int $level) : void; + abstract public function recalculateNode(int $x, int $y, int $z) : void; + + protected function getHighestAdjacentLight(int $x, int $y, int $z) : int{ + $adjacent = 0; + foreach([ + [$x + 1, $y, $z], + [$x - 1, $y, $z], + [$x, $y + 1, $z], + [$x, $y - 1, $z], + [$x, $y, $z + 1], + [$x, $y, $z - 1] + ] as [$x1, $y1, $z1]){ + if($this->subChunkHandler->moveTo($x1, $y1, $z1, false) and ($adjacent = max($adjacent, $this->getLight($x1, $y1, $z1))) === 15){ + break; + } + } + return $adjacent; + } + public function setAndUpdateLight(int $x, int $y, int $z, int $newLevel) : void{ $this->updateNodes[World::blockHash($x, $y, $z)] = [$x, $y, $z, $newLevel]; } diff --git a/src/pocketmine/world/light/SkyLightUpdate.php b/src/pocketmine/world/light/SkyLightUpdate.php index 224160419..8374c862d 100644 --- a/src/pocketmine/world/light/SkyLightUpdate.php +++ b/src/pocketmine/world/light/SkyLightUpdate.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace pocketmine\world\light; +use pocketmine\block\BlockFactory; +use function max; + class SkyLightUpdate extends LightUpdate{ public function getLight(int $x, int $y, int $z) : int{ @@ -32,4 +35,40 @@ class SkyLightUpdate extends LightUpdate{ public function setLight(int $x, int $y, int $z, int $level) : void{ $this->subChunkHandler->currentSubChunk->setBlockSkyLight($x & 0x0f, $y & 0x0f, $z & 0x0f, $level); } + + public function recalculateNode(int $x, int $y, int $z) : void{ + $chunk = $this->world->getChunk($x >> 4, $z >> 4); + if($chunk === null){ + return; + } + $oldHeightMap = $chunk->getHeightMap($x & 0xf, $z & 0xf); + $source = $this->world->getBlockAt($x, $y, $z); + + $yPlusOne = $y + 1; + + if($yPlusOne === $oldHeightMap){ //Block changed directly beneath the heightmap. Check if a block was removed or changed to a different light-filter. + $newHeightMap = $chunk->recalculateHeightMapColumn($x & 0x0f, $z & 0x0f); + }elseif($yPlusOne > $oldHeightMap){ //Block changed above the heightmap. + if($source->getLightFilter() > 0 or $source->diffusesSkyLight()){ + $chunk->setHeightMap($x & 0xf, $z & 0xf, $yPlusOne); + $newHeightMap = $yPlusOne; + }else{ //Block changed which has no effect on direct sky light, for example placing or removing glass. + return; + } + }else{ //Block changed below heightmap + $newHeightMap = $oldHeightMap; + } + + if($newHeightMap > $oldHeightMap){ //Heightmap increase, block placed, remove sky light + for($i = $y; $i >= $oldHeightMap; --$i){ + $this->setAndUpdateLight($x, $i, $z, 0); //Remove all light beneath, adjacent recalculation will handle the rest. + } + }elseif($newHeightMap < $oldHeightMap){ //Heightmap decrease, block changed or removed, add sky light + for($i = $y; $i >= $newHeightMap; --$i){ + $this->setAndUpdateLight($x, $i, $z, 15); + } + }else{ //No heightmap change, block changed "underground" + $this->setAndUpdateLight($x, $y, $z, max(0, $this->getHighestAdjacentLight($x, $y, $z) - BlockFactory::$lightFilter[$source->getFullId()])); + } + } }