Skip to content

Commit

Permalink
fix light invalidation issue
Browse files Browse the repository at this point in the history
  • Loading branch information
iam4722202468 committed May 30, 2024
1 parent 43ed60d commit 5035c2b
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 87 deletions.
51 changes: 25 additions & 26 deletions src/main/java/net/minestom/server/instance/LightingChunk.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package net.minestom.server.instance;

import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerFlag;
import net.minestom.server.collision.Shape;
import net.minestom.server.coordinate.Point;
Expand Down Expand Up @@ -41,7 +40,6 @@ public class LightingChunk extends DynamicChunk {
final CachedPacket lightCache = new CachedPacket(this::createLightPacket);
private LightData lightData;

boolean chunkLoaded = false;
private int highestBlock;
private boolean freezeInvalidation = false;

Expand Down Expand Up @@ -159,7 +157,7 @@ public void setBlock(int x, int y, int z, @NotNull Block block,

// Invalidate neighbor chunks, since they can be updated by this block change
int coordinate = ChunkUtils.getChunkCoordinate(y);
if (chunkLoaded && !freezeInvalidation) {
if (doneInit && !freezeInvalidation) {
invalidateNeighborsSection(coordinate);
invalidateResendDelay();
this.lightCache.invalidate();
Expand All @@ -173,7 +171,6 @@ public void sendLighting() {

@Override
protected void onLoad() {
chunkLoaded = true;
doneInit = true;
}

Expand All @@ -188,27 +185,16 @@ public void onGenerate() {

invalidate();

MinecraftServer.getSchedulerManager().scheduleNextTick(() -> {
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j);
if (neighborChunk == null) continue;

if (neighborChunk instanceof LightingChunk light) {
for (int section = light.minSection; section < light.maxSection; section++) {
light.getSection(section).blockLight().invalidate();
light.getSection(section).skyLight().invalidate();
}

light.invalidate();
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j);
if (neighborChunk == null) continue;

light.resendTimer.set(20);
}
if (neighborChunk instanceof LightingChunk light) {
if (light.doneInit) light.resendTimer.set(20);
}
}
});

doneInit = true;
}
}

// Lazy compute occlusion map
Expand Down Expand Up @@ -330,6 +316,13 @@ public void tick(long time) {

if (doneInit && resendTimer.get() > 0) {
if (resendTimer.decrementAndGet() == 0) {
invalidate();

for (int section = minSection; section < maxSection; section++) {
getSection(section).blockLight().invalidate();
getSection(section).skyLight().invalidate();
}

sendLighting();
}
}
Expand All @@ -349,11 +342,16 @@ private static Set<Chunk> flushQueue(Instance instance, Set<Point> queue, LightT
var section = chunk.getSection(point.blockY());
responseChunks.add(chunk);

var light = type == LightType.BLOCK ? section.blockLight() : section.skyLight();
Light light = switch(type) {
case BLOCK -> section.blockLight();
case SKY -> section.skyLight();
};

CompletableFuture<Void> task = CompletableFuture.runAsync(() -> {
if (queueType == QueueType.INTERNAL) light.calculateInternal(instance, chunk.getChunkX(), point.blockY(), chunk.getChunkZ());
else light.calculateExternal(instance, chunk, point.blockY());
switch (queueType) {
case INTERNAL -> light.calculateInternal(instance, chunk.getChunkX(), point.blockY(), chunk.getChunkZ());
case EXTERNAL -> light.calculateExternal(instance, chunk, point.blockY());
}

sections.add(light);

Expand Down Expand Up @@ -461,7 +459,8 @@ private static Set<Point> getNearbyRequired(Instance instance, Point point, Ligh

if (sectionPosition.blockY() < chunkCheck.getMaxSection() && sectionPosition.blockY() >= chunkCheck.getMinSection()) {
Section s = chunkCheck.getSection(sectionPosition.blockY());
if (!s.blockLight().requiresUpdate() && !s.skyLight().requiresUpdate()) continue;
if (type == LightType.BLOCK && !s.blockLight().requiresUpdate()) continue;
if (type == LightType.SKY && !s.skyLight().requiresUpdate()) continue;

collected.add(sectionPosition);
}
Expand Down
48 changes: 19 additions & 29 deletions src/main/java/net/minestom/server/instance/light/BlockLight.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.instance.palette.Palette;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import static net.minestom.server.instance.light.LightCompute.*;

Expand All @@ -24,8 +24,8 @@ final class BlockLight implements Light {
private byte[] contentPropagation;
private byte[] contentPropagationSwap;

private boolean isValidBorders = true;
private boolean needsSend = false;
private AtomicBoolean isValidBorders = new AtomicBoolean(true);
private AtomicBoolean needsSend = new AtomicBoolean(false);

private Set<Point> toUpdateSet = new HashSet<>();
private final Section[] neighborSections = new Section[BlockFace.values().length];
Expand Down Expand Up @@ -83,7 +83,7 @@ private ShortArrayFIFOQueue buildExternalQueue(Instance instance, Palette blockP
neighborSections[face.ordinal()] = otherSection;
}

var otherLight = otherSection.blockLight();
Light otherLight = otherSection.blockLight();

for (int bx = 0; bx < 16; bx++) {
for (int by = 0; by < 16; by++) {
Expand Down Expand Up @@ -145,14 +145,14 @@ private ShortArrayFIFOQueue buildExternalQueue(Instance instance, Palette blockP

@Override
public Light calculateInternal(Instance instance, int chunkX, int sectionY, int chunkZ) {
this.isValidBorders.set(true);

Chunk chunk = instance.getChunk(chunkX, chunkZ);
if (chunk == null) {
this.toUpdateSet = Set.of();
return this;
}

this.isValidBorders = true;

Set<Point> toUpdate = new HashSet<>();

// Update single section with base lighting changes
Expand All @@ -171,8 +171,8 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int
Vec neighborPos = new Vec(chunkX + i, sectionY + k, chunkZ + j);

if (neighborPos.blockY() >= neighborChunk.getMinSection() && neighborPos.blockY() < neighborChunk.getMaxSection()) {
toUpdate.add(new Vec(neighborChunk.getChunkX(), neighborPos.blockY(), neighborChunk.getChunkZ()));
neighborChunk.getSection(neighborPos.blockY()).blockLight().invalidatePropagation();
if (neighborChunk.getSection(neighborPos.blockY()).blockLight() instanceof BlockLight blockLight)
blockLight.contentPropagation = null;
}
}
}
Expand All @@ -186,34 +186,28 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int

@Override
public void invalidate() {
invalidatePropagation();
this.needsSend.set(true);
this.isValidBorders.set(false);
this.contentPropagation = null;
}

@Override
public boolean requiresUpdate() {
return !isValidBorders;
return !isValidBorders.get();
}

@Override
@ApiStatus.Internal
public void set(byte[] copyArray) {
this.content = copyArray.clone();
this.contentPropagation = this.content;
this.isValidBorders = true;
this.needsSend = true;
this.isValidBorders.set(true);
this.needsSend.set(false);
}

@Override
public boolean requiresSend() {
boolean res = needsSend;
needsSend = false;
return res;
}

private void clearCache() {
this.contentPropagation = null;
isValidBorders = true;
needsSend = true;
return needsSend.getAndSet(false);
}

@Override
Expand All @@ -227,7 +221,10 @@ public byte[] array() {

@Override
public Light calculateExternal(Instance instance, Chunk chunk, int sectionY) {
if (!isValidBorders) clearCache();
if (!isValidBorders.get()) {
this.toUpdateSet = Set.of();
return this;
}

Point[] neighbors = Light.getNeighbors(chunk, sectionY);

Expand Down Expand Up @@ -280,13 +277,6 @@ private byte[] bake(byte[] content1, byte[] content2) {
return lightMax;
}

@Override
public void invalidatePropagation() {
this.isValidBorders = false;
this.needsSend = false;
this.contentPropagation = null;
}

@Override
public int getLevel(int x, int y, int z) {
if (content == null) return 0;
Expand Down
5 changes: 0 additions & 5 deletions src/main/java/net/minestom/server/instance/light/Light.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static net.minestom.server.instance.light.LightCompute.SECTION_SIZE;
Expand All @@ -36,9 +34,6 @@ static Light block(@NotNull Palette blockPalette) {
@ApiStatus.Internal
Light calculateExternal(Instance instance, Chunk chunk, int sectionY);

@ApiStatus.Internal
void invalidatePropagation();

int getLevel(int x, int y, int z);

@ApiStatus.Internal
Expand Down
46 changes: 19 additions & 27 deletions src/main/java/net/minestom/server/instance/light/SkyLight.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import static net.minestom.server.instance.light.LightCompute.*;

Expand All @@ -24,8 +25,8 @@ final class SkyLight implements Light {
private byte[] contentPropagation;
private byte[] contentPropagationSwap;

private boolean isValidBorders = true;
private boolean needsSend = false;
private final AtomicBoolean isValidBorders = new AtomicBoolean(true);
private final AtomicBoolean needsSend = new AtomicBoolean(false);

private Set<Point> toUpdateSet = new HashSet<>();
private final Section[] neighborSections = new Section[BlockFace.values().length];
Expand Down Expand Up @@ -160,7 +161,7 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int
this.toUpdateSet = Set.of();
return this;
}
this.isValidBorders = true;
this.isValidBorders.set(true);

// Update single section with base lighting changes
int queueSize = SECTION_SIZE * SECTION_SIZE * SECTION_SIZE;
Expand Down Expand Up @@ -190,8 +191,10 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int
Vec neighborPos = new Vec(chunkX + i, sectionY + k, chunkZ + j);

if (neighborPos.blockY() >= neighborChunk.getMinSection() && neighborPos.blockY() < neighborChunk.getMaxSection()) {
toUpdate.add(new Vec(neighborChunk.getChunkX(), neighborPos.blockY(), neighborChunk.getChunkZ()));
neighborChunk.getSection(neighborPos.blockY()).skyLight().invalidatePropagation();
if (neighborChunk.getSection(neighborPos.blockY()).skyLight() instanceof SkyLight skyLight) {
skyLight.contentPropagation = null;
toUpdate.add(new Vec(neighborChunk.getChunkX(), neighborPos.blockY(), neighborChunk.getChunkZ()));
}
}
}
}
Expand All @@ -205,35 +208,28 @@ public Light calculateInternal(Instance instance, int chunkX, int sectionY, int

@Override
public void invalidate() {
invalidatePropagation();
this.needsSend.set(true);
this.isValidBorders.set(false);
this.contentPropagation = null;
}

@Override
public boolean requiresUpdate() {
return !isValidBorders;
return !isValidBorders.get();
}

@Override
@ApiStatus.Internal
public void set(byte[] copyArray) {
this.content = copyArray.clone();
this.contentPropagation = this.content;
this.isValidBorders = true;
this.needsSend = true;
this.isValidBorders.set(true);
this.needsSend.set(false);
}

@Override
public boolean requiresSend() {
boolean res = needsSend;
needsSend = false;
return res;
}

private void clearCache() {
this.contentPropagation = null;
isValidBorders = true;
needsSend = true;
fullyLit = false;
return needsSend.getAndSet(false);
}

@Override
Expand All @@ -247,7 +243,10 @@ public byte[] array() {

@Override
public Light calculateExternal(Instance instance, Chunk chunk, int sectionY) {
if (!isValidBorders) clearCache();
if (!isValidBorders.get()) {
this.toUpdateSet = Set.of();
return this;
}

Point[] neighbors = Light.getNeighbors(chunk, sectionY);
Set<Point> toUpdate = new HashSet<>();
Expand Down Expand Up @@ -307,13 +306,6 @@ private byte[] bake(byte[] content1, byte[] content2) {
return lightMax;
}

@Override
public void invalidatePropagation() {
this.isValidBorders = false;
this.needsSend = false;
this.contentPropagation = null;
}

@Override
public int getLevel(int x, int y, int z) {
if (content == null) return 0;
Expand Down

0 comments on commit 5035c2b

Please sign in to comment.