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)])){