diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index 7f750b7c9..bb13f6168 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -528,6 +528,12 @@ class Block extends Position implements Metadatable{ /** @var \SplFixedArray */ public static $list = null; + /** @var \SplFixedArray */ + public static $light = null; + /** @var \SplFixedArray */ + public static $solid = null; + /** @var \SplFixedArray */ + public static $transparent = null; protected $id; protected $meta; protected $name = "Unknown"; @@ -555,6 +561,9 @@ class Block extends Position implements Metadatable{ public static function init(){ if(self::$list === null){ self::$list = new \SplFixedArray(256); + self::$light = new \SplFixedArray(256); + self::$solid = new \SplFixedArray(256); + self::$transparent = new \SplFixedArray(256); self::$list[self::AIR] = Air::class;; self::$list[self::STONE] = Stone::class;; self::$list[self::GRASS] = Grass::class;; @@ -697,11 +706,21 @@ class Block extends Position implements Metadatable{ self::$list[self::FENCE_JUNGLE] = FenceJungle::class; self::$list[self::FENCE_ACACIA] = FenceAcacia::class; - self::$list[self::PODZOL] = Podzol::class;; - self::$list[self::BEETROOT_BLOCK] = Beetroot::class;; - self::$list[self::STONECUTTER] = Stonecutter::class;; - self::$list[self::GLOWING_OBSIDIAN] = GlowingObsidian::class;; - self::$list[self::NETHER_REACTOR] = NetherReactor::class;; + self::$list[self::PODZOL] = Podzol::class; + self::$list[self::BEETROOT_BLOCK] = Beetroot::class; + self::$list[self::STONECUTTER] = Stonecutter::class; + self::$list[self::GLOWING_OBSIDIAN] = GlowingObsidian::class; + self::$list[self::NETHER_REACTOR] = NetherReactor::class; + + foreach(self::$list as $class){ + if($class !== null){ + /** @var Block $block */ + $block = new $class(); + self::$solid[$block->getId()] = (bool) $block->isSolid; + self::$transparent[$block->getId()] = (bool) $block->isTransparent; + self::$light[$block->getId()] = (int) $block->lightLevel; + } + } } } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 204e56f4c..3635ba9c0 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -926,66 +926,97 @@ class Level implements ChunkManager, Metadatable{ } public function updateAllLight(Vector3 $pos){ - $this->updateBlockSkyLight($pos); - $this->updateBlockLight($pos); + $start = microtime(true); + $this->updateBlockSkyLight($pos->x, $pos->y, $pos->z); + $this->updateBlockLight($pos->x, $pos->y, $pos->z); } - public function updateBlockSkyLight(Vector3 $pos){ - $block = ($pos instanceof Block) ? $pos : $this->getBlock($pos); + public function updateBlockSkyLight($x, $y, $z){ + //TODO + } - $current = $this->getBlockSkyLightAt($pos->x, $pos->y, $pos->z); - $decrease = ($block->isSolid and !$block->isTransparent) ? 15 : 0; + public function updateBlockLight($x, $y, $z){ + $lightPropagationQueue = new \SplQueue(); + $lightRemovalQueue = new \SplQueue(); + $visited = []; + $removalVisited = []; - $calculatedLight = $this->computeBlockSkyLight($block, $current, $decrease); + $oldLevel = $this->getBlockLightAt($x, $y, $z); + $newLevel = (int) Block::$light[$this->getBlockIdAt($x, $y, $z)]; - if($calculatedLight !== $current){ - $this->setBlockSkyLightAt($block->x, $block->y, $block->z, $calculatedLight); - for($side = 0; $side <= 5; ++$side){ - $this->updateBlockSkyLight($block->getSide($side)); + if($oldLevel !== $newLevel){ + $this->setBlockLightAt($x, $y, $z, $newLevel); + $visited["$x:$y:$z"] = true; + $lightPropagationQueue->enqueue(new Vector3($x, $y, $z)); + } + + if($newLevel < $oldLevel){ + $removalVisited["$x:$y:$z"] = true; + $lightRemovalQueue->enqueue([new Vector3($x, $y, $z), $oldLevel]); + } + + while(!$lightRemovalQueue->isEmpty()){ + /** @var Vector3 $node */ + $val = $lightRemovalQueue->dequeue(); + $node = $val[0]; + $lightLevel = $val[1]; + + $this->computeRemoveBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); + $this->computeRemoveBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); + $this->computeRemoveBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); + $this->computeRemoveBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); + $this->computeRemoveBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); + $this->computeRemoveBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); + } + + while(!$lightPropagationQueue->isEmpty()){ + /** @var Vector3 $node */ + $node = $lightPropagationQueue->dequeue(); + + $lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z); + + $this->computeSpreadBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited); + $this->computeSpreadBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited); + $this->computeSpreadBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightPropagationQueue, $visited); + $this->computeSpreadBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightPropagationQueue, $visited); + $this->computeSpreadBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightPropagationQueue, $visited); + $this->computeSpreadBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightPropagationQueue, $visited); + } + } + + private function computeRemoveBlockLight($x, $y, $z, $currentLight, \SplQueue $queue, \SplQueue $spreadQueue, array &$visited, array &$spreadVisited){ + $current = $this->getBlockLightAt($x, $y, $z); + + if($current !== 0 and $current < $currentLight){ + $this->setBlockLightAt($x, $y, $z, 0); + + if(!isset($visited[$index = "$x:$y:$z"])){ + $visited[$index] = true; + $queue->enqueue([new Vector3($x, $y, $z), $current]); + } + }elseif($current >= $currentLight){ + if(!isset($spreadVisited[$index = "$x:$y:$z"])){ + $spreadVisited[$index] = true; + $spreadQueue->enqueue(new Vector3($x, $y, $z)); } } } - public function updateBlockLight(Vector3 $pos){ - $block = ($pos instanceof Block) ? $pos : $this->getBlock($pos); + private function computeSpreadBlockLight($x, $y, $z, $currentLight, \SplQueue $queue, array &$visited){ + $blockId = $this->getBlockIdAt($x, $y, $z); + $decrease = (Block::$solid[$blockId] and !Block::$transparent[$blockId]) ? 15 : 1; + $current = $this->getBlockLightAt($x, $y, $z); - $current = $this->getBlockLightAt($pos->x, $pos->y, $pos->z); - $decrease = ($block->isSolid and !$block->isTransparent) ? 15 : 1; + if($decrease < 15 and ($current + 2) <= $currentLight){ + $this->setBlockLightAt($x, $y, $z, $currentLight - 1); - $calculatedLight = $this->computeBlockLight($block, $block->lightLevel, $decrease); - - if($calculatedLight !== $current){ - $this->setBlockLightAt($block->x, $block->y, $block->z, $calculatedLight); - for($side = 0; $side <= 5; ++$side){ - $this->updateBlockLight($block->getSide($side)); + if(!isset($visited[$index = "$x:$y:$z"])){ + $visited[$index] = true; + $queue->enqueue(new Vector3($x, $y, $z)); } } } - protected function computeBlockLight(Vector3 $pos, $current, $decrease){ - return max( - $current, - $this->getBlockLightAt($pos->x - 1, $pos->y, $pos->z) - $decrease, - $this->getBlockLightAt($pos->x + 1, $pos->y, $pos->z) - $decrease, - $this->getBlockLightAt($pos->x, $pos->y - 1, $pos->z) - $decrease, - $this->getBlockLightAt($pos->x, $pos->y + 1, $pos->z) - $decrease, - $this->getBlockLightAt($pos->x, $pos->y, $pos->z - 1) - $decrease, - $this->getBlockLightAt($pos->x, $pos->y, $pos->z + 1) - $decrease - ); - } - - protected function computeBlockSkyLight(Vector3 $pos, $current, $decrease){ - return max( - $current, - $this->getBlockSkyLightAt($pos->x - 1, $pos->y, $pos->z) - $decrease, - $this->getBlockSkyLightAt($pos->x + 1, $pos->y, $pos->z) - $decrease, - $this->getBlockSkyLightAt($pos->x, $pos->y - 1, $pos->z) - $decrease, - $this->getBlockSkyLightAt($pos->x, $pos->y + 1, $pos->z) - $decrease, - $this->getBlockSkyLightAt($pos->x, $pos->y, $pos->z - 1) - $decrease, - $this->getBlockSkyLightAt($pos->x, $pos->y, $pos->z + 1) - $decrease - ); - } - /** * Sets on Vector3 the data from a Block object, * does block updates and puts the changes to the send queue.