45        private array $directSkyLightBlockers
 
   47        parent::__construct($subChunkExplorer, $lightFilters);
 
 
   50    protected function getCurrentLightArray() : LightArray{
 
   51        return $this->subChunkExplorer->currentSubChunk->getBlockSkyLightArray();
 
   54    protected function getEffectiveLight(
int $x, 
int $y, 
int $z) : int{
 
   55        if($y >= 
World::Y_MAX){
 
   56            $this->subChunkExplorer->invalidate();
 
   59        return parent::getEffectiveLight($x, $y, $z);
 
   62    public function recalculateNode(
int $x, 
int $y, 
int $z) : void{
 
   63        if($this->subChunkExplorer->moveTo($x, $y, $z) === SubChunkExplorerStatus::INVALID){
 
   66        $chunk = $this->subChunkExplorer->currentChunk;
 
   68        $oldHeightMap = $chunk->getHeightMap($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK);
 
   69        $source = $this->subChunkExplorer->currentSubChunk->getBlockStateId($x & SubChunk::COORD_MASK, $y & SubChunk::COORD_MASK, $z & SubChunk::COORD_MASK);
 
   73        if($yPlusOne === $oldHeightMap){ 
 
   74            $newHeightMap = self::recalculateHeightMapColumn($chunk, $x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $this->directSkyLightBlockers);
 
   75            $chunk->setHeightMap($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $newHeightMap);
 
   76        }elseif($yPlusOne > $oldHeightMap){ 
 
   77            if(isset($this->directSkyLightBlockers[$source])){
 
   78                $chunk->setHeightMap($x & Chunk::COORD_MASK, $z & Chunk::COORD_MASK, $yPlusOne);
 
   79                $newHeightMap = $yPlusOne;
 
   84            $newHeightMap = $oldHeightMap;
 
   87        if($newHeightMap >= $oldHeightMap){
 
   88            for($i = $y - 1; $i >= $oldHeightMap; --$i){
 
   89                $this->setAndUpdateLight($x, $i, $z, 0); 
 
   93            $this->setAndUpdateLight($x, $y, $z, max(0, $this->getHighestAdjacentLight($x, $y, $z) - ($this->lightFilters[$source] ?? self::BASE_LIGHT_FILTER)));
 
   95            for($i = $y; $i >= $newHeightMap; --$i){
 
   96                $this->setAndUpdateLight($x, $i, $z, 15);
 
  103            throw new \InvalidArgumentException(
"Chunk $chunkX $chunkZ does not exist");
 
  105        $chunk = $this->subChunkExplorer->currentChunk;
 
  107        $newHeightMap = self::recalculateHeightMap($chunk, $this->directSkyLightBlockers);
 
  108        $chunk->setHeightMapArray($newHeightMap->getValues());
 
  112        $highestHeightMapPlusOne = max($chunk->getHeightMapArray()) + 1;
 
  113        $lowestClearSubChunk = ($highestHeightMapPlusOne >> SubChunk::COORD_BIT_SIZE) + (($highestHeightMapPlusOne & SubChunk::COORD_MASK) !== 0 ? 1 : 0);
 
  114        for($y = Chunk::MIN_SUBCHUNK_INDEX; $y < $lowestClearSubChunk && $y <= Chunk::MAX_SUBCHUNK_INDEX; $y++){
 
  115            $chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(0));
 
  117        for($y = $lowestClearSubChunk; $y <= Chunk::MAX_SUBCHUNK_INDEX; $y++){
 
  118            $chunk->getSubChunk($y)->setBlockSkyLightArray(LightArray::fill(15));
 
  123        $baseX = $chunkX << Chunk::COORD_BIT_SIZE;
 
  124        $baseZ = $chunkZ << Chunk::COORD_BIT_SIZE;
 
  125        for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
 
  126            for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
 
  127                $currentHeight = $chunk->getHeightMap($x, $z);
 
  129                if($currentHeight === World::Y_MAX){
 
  132                    $y = $currentHeight - 1;
 
  133                    if($this->subChunkExplorer->moveTo($x + $baseX, $y, $z + $baseZ) !== SubChunkExplorerStatus::INVALID){
 
  134                        $block = $this->subChunkExplorer->currentSubChunk->getBlockStateId($x, $y & SubChunk::COORD_MASK, $z);
 
  135                        $this->setAndUpdateLight($x + $baseX, $y, $z + $baseZ, max(0, 15 - ($this->lightFilters[$block] ?? self::BASE_LIGHT_FILTER)));
 
  138                    $maxAdjacentHeight = World::Y_MIN;
 
  140                        $maxAdjacentHeight = max($maxAdjacentHeight, $chunk->getHeightMap($x - 1, $z));
 
  143                        $maxAdjacentHeight = max($maxAdjacentHeight, $chunk->getHeightMap($x + 1, $z));
 
  146                        $maxAdjacentHeight = max($maxAdjacentHeight, $chunk->getHeightMap($x, $z - 1));
 
  149                        $maxAdjacentHeight = max($maxAdjacentHeight, $chunk->getHeightMap($x, $z + 1));
 
  158                    $nodeColumnEnd = max($currentHeight, $maxAdjacentHeight - 2);
 
  159                    for($y = $currentHeight; $y <= $nodeColumnEnd; $y++){
 
  160                        $this->setAndUpdateLight($x + $baseX, $y, $z + $baseZ, 15);
 
  163                    for($y = $nodeColumnEnd + 1, $yMax = $lowestClearSubChunk * SubChunk::EDGE_LENGTH; $y < $yMax; $y++){
 
  164                        if($this->subChunkExplorer->moveTo($x + $baseX, $y, $z + $baseZ) !== SubChunkExplorerStatus::INVALID){
 
  165                            $this->getCurrentLightArray()->set($x, $y & SubChunk::COORD_MASK, $z, 15);
 
  172        return $lightSources;
 
 
  181    private static function recalculateHeightMap(Chunk $chunk, array $directSkyLightBlockers) : HeightArray{
 
  182        $maxSubChunkY = Chunk::MAX_SUBCHUNK_INDEX;
 
  183        for(; $maxSubChunkY >= Chunk::MIN_SUBCHUNK_INDEX; $maxSubChunkY--){
 
  184            if(!$chunk->getSubChunk($maxSubChunkY)->isEmptyFast()){
 
  188        $result = HeightArray::fill(World::Y_MIN);
 
  189        if($maxSubChunkY < Chunk::MIN_SUBCHUNK_INDEX){ 
 
  193        for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
 
  194            for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
 
  196                for($subChunkY = $maxSubChunkY; $subChunkY >= Chunk::MIN_SUBCHUNK_INDEX; $subChunkY--){
 
  197                    $subHighestBlockY = $chunk->getSubChunk($subChunkY)->getHighestBlockAt($x, $z);
 
  198                    if($subHighestBlockY !== 
null){
 
  199                        $y = ($subChunkY * SubChunk::EDGE_LENGTH) + $subHighestBlockY;
 
  205                    $result->set($x, $z, World::Y_MIN);
 
  207                    for(; $y >= World::Y_MIN; --$y){
 
  208                        if(isset($directSkyLightBlockers[$chunk->getBlockStateId($x, $y, $z)])){
 
  209                            $result->set($x, $z, $y + 1);
 
  229    private static function recalculateHeightMapColumn(Chunk $chunk, 
int $x, 
int $z, array $directSkyLightBlockers) : int{
 
  230        $y = $chunk->getHighestBlockAt($x, $z);
 
  234        for(; $y >= World::Y_MIN; --$y){
 
  235            if(isset($directSkyLightBlockers[$chunk->getBlockStateId($x, $y, $z)])){