fix: sky light propagation hanging on unloaded chunks (#2334)

* fix(lighting): stop sky light updates from looping on unloaded chunks

The sky light propagation could spin forever when a light update happened
next to a chunk that wasn't loaded. Writes to an unloaded chunk are dropped
silently and reads come back as 0, so the "this neighbor is darker than us"
check stayed true on every pass and the same position kept getting queued
again. In practice this hangs a tick thread when a block is broken near the
edge of the loaded area.

Skip neighbors whose chunk isn't loaded in both the increase and decrease
passes, matching how the border of loaded chunks already behaves: light
doesn't bleed into chunks that aren't there yet.

* Use Level::is_chunk_loaded for the unloaded-chunk guard
This commit is contained in:
TheDarkSword
2026-07-09 20:59:38 +02:00
committed by GitHub
parent 1af05a4530
commit a4af4269fb

View File

@@ -277,6 +277,16 @@ impl DynamicLightEngine {
for dir in BlockDirection::all() {
let neighbor_pos = pos.offset(dir.to_offset());
// Never propagate into an unloaded chunk. Writes to an unloaded
// chunk are dropped silently, so the "brighter than neighbor" check
// below would stay true forever and keep re-queuing the same
// position, spinning this loop indefinitely at the border between
// loaded and unloaded chunks.
let (neighbor_chunk, _) = neighbor_pos.chunk_and_chunk_relative_position();
if !level.is_chunk_loaded(&neighbor_chunk) {
continue;
}
let neighbor_light = self.get_sky_light_level(level, &neighbor_pos);
let neighbor_state = level.get_block_state(&neighbor_pos).to_state();
let opacity = neighbor_state.opacity;
@@ -306,6 +316,13 @@ impl DynamicLightEngine {
for dir in BlockDirection::all() {
let neighbor_pos = pos.offset(dir.to_offset());
// See `propagate_sky_light_increase`: skip unloaded chunks so sky
// light updates never spin at loaded/unloaded chunk borders.
let (neighbor_chunk, _) = neighbor_pos.chunk_and_chunk_relative_position();
if !level.is_chunk_loaded(&neighbor_chunk) {
continue;
}
let neighbor_light = self.get_sky_light_level(level, &neighbor_pos);
if neighbor_light == 0 {
continue; // Already dark