Upon initial lighting of a chunk, skylight (and blocklight which doesn't matter here) is directly propagated to all neighbors. However, those neighbors might not have their lightmaps set up correctly, yet.
Non-empty subchunks are only registered during initial lighting
net.minecraft.world.ServerLightingProvider.java
public CompletableFuture<Chunk> light(Chunk chunk, boolean bl) {
ChunkPos chunkPos = chunk.getPos();
chunk.setLightOn(false);
this.enqueue(chunkPos.x, chunkPos.z, ServerLightingProvider.Stage.PRE_UPDATE, Util.debugRunnable(() -> {
ChunkSection[] chunkSections = chunk.getSectionArray();
for(int i = 0; i < 16; ++i) {
ChunkSection chunkSection = chunkSections[i];
if (!ChunkSection.isEmpty(chunkSection)) {
super.updateSectionStatus(ChunkSectionPos.from(chunkPos, i), false);
}
}
...
This means that during initial lighting, neighbor chunks might have not yet registered their non-empty subchunks and hence lightmaps might not have been setup yet correctly.
This can cause skylight updates to get stuck at missing lightmaps. When later on the missing lightmaps get created during initial lighting of the neighbor chunks, those lightmaps will be initialized by copying down the skylight values from the lightmap above it. Hence, if such a subchunk contains any blocks but did not have a lightmap associated to it, the lightmap will now be initialized to wrong values, making the subchunk erroneously bright.
As a concrete example, I will use "erase cached data" in order to force a relighting and hence simulate initial skylight on a well controlled geometry.
Create a new redstone-ready world
/tp 500 56 0
/fill 496 80 0 511 95 15 minecraft:stone hollow
/setblock 487 119 7 minecraft:stone
/tp @s 0 56 0 270 0
Optimize world -> erase cached data
Fly 500 blocks in +x direction and fly inside the stone cube using spectator mode
[media]
Fortunately, this issue is quite easy to solve. Simply move the registration of non-empty subchunks outside of the initial lighting to some earlier stage. Concretely, add some pre_light
stage to worldgen that registers all non-empty subchunks and require all neighbors to have passed this stage before running initial lighting for a chunk.
Best,
PhiPro
Linked issues
relates to 3
Attachments
Comments 14
There is another related issue regarding chunks before the light
stage. Upon loading from disk, lightmaps are only loaded for chunks that were already lighted and discarded otherwise, although they are always saved to disk.
net.minecraft.world.ChunkSerializer.java
public static ProtoChunk deserialize(...) {
...
boolean bl = compoundTag.getBoolean("isLightOn");
...
if (bl) {
if (compoundTag2.contains("BlockLight", 7)) {
lightingProvider.enqueueSectionData(LightType.BLOCK, ChunkSectionPos.from(pos, k), new ChunkNibbleArray(compoundTag2.getByteArray("BlockLight")), true);
}
if (bl2 && compoundTag2.contains("SkyLight", 7)) {
lightingProvider.enqueueSectionData(LightType.SKY, ChunkSectionPos.from(pos, k), new ChunkNibbleArray(compoundTag2.getByteArray("SkyLight")), true);
}
}
...
}
This basically erases any light propagations to chunks in pre_light
stage when unloading them before the light
stage and hence causes lighting glitches.
The Vanilla code currently uses this mechanism to erase cached data
, which only removes the isLightOn
field but not the lightmaps themselves.
There is also a related issue for blocklight. As mentioned in the discussion of MC-170010
ProtoChunk.setBlockState(...)
schedules light checks for chunks that have passed thefeatures
generation stage, suggesting that such block updates are indeed possible. Looking at the worldgen code also suggests that this is possible since thefeatures
generation stage has access to an 8 chunk radius.The problem is now that such a block change could place a light source and the scheduled light check would then propagate the light too early. In contrast to the original report about skylight, lightmaps do exist for the neighbors, since they will be created due to the placed blocklight source (well, not quite due to the second issue below). However, the lighting engine treats chunks before the
features
stage as opaque, so the light propagation can get stuck at the chunk boundary nevertheless. So the end-result is similar to the original report, namely that light propagation can get stuck if carried out too early, i.e., before the neighbors are ready, altough the underlying cause is slightly different in this case.Note that this version for blocklight cannot be as easily visualised by relighting the world using
erase cached data
as it relies on blocks being placed between thefeatures
andlight
generation stage.The fix for this is again quite easy. Just let the lighting engine check if a chunk already has its initial lighting done, and otherwise ignore the luminance of a block for light calculations. This way, blocks don't emit light by themselves before the initial lighting and are properly turned on during initial lighting, similar to my proposed solution for initial skylight in MC-170010.
Note that you cannot simply remove the light checks that cause the early propagation. Initial lighting spreads light too all neighbors, which are only guaranteed to be in
pre_light
stage at that point. Any block changes thereafter need to schedule light checks as otherwise you can have missing or ghost-contributions to those neighbors. When later on those neighbors are Initially lighted they will not be completely relighted but only add their own blocklight-sources. So, you need any lighting information to stay consistent after thepre_light
stage and hence you need these light checks.Also note that you should not fix this bug by not treating chunks before the
features
stage as opaque. Leaving in the early propagation of blocklight would then cause light propagations into chunks before thefeatures
stage and hence the above discussion would also apply to those, so you would need to schedule light checks before thefeatures
stage as well. However, for performance reasons this is not desired.There is another issue related to block changes after the
features
stage.ProtoChunk
only schedules light checks upon block changes (for chunks after thefeatures
stage). but it does not handle lightmap creation and removal. Concretely, it misses the calls toLightingView.updateSectionStatus(BlockPos pos, boolean status)
that are present inWorldChunk
andWorld
. In view of MC-169913 lightmap creation schould be scheduled before the block change and lightmap removal should be scheduled after the light checks.Let me know if I should post these as separate bug reports.