Paper/Spigot-Server-Patches/0028-Lighting-Queue.patch

281 lines
12 KiB
Diff

From 3e0df901f487044ce2782c682f66e627e9411eb3 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 2 Mar 2016 00:52:31 -0600
Subject: [PATCH] Lighting Queue
This provides option to queue lighting updates to ensure they do not cause the server lag
diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
index 145cb274b..eff9dcf54 100644
--- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java
+++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
@@ -50,6 +50,8 @@ public class WorldTimingsHandler {
public final Timing worldSaveLevel;
public final Timing chunkSaveData;
+ public final Timing lightingQueueTimer;
+
public WorldTimingsHandler(World server) {
String name = server.worldData.getName() +" - ";
@@ -96,6 +98,8 @@ public class WorldTimingsHandler {
tracker2 = Timings.ofSafe(name + "tracker stage 2");
doTick = Timings.ofSafe(name + "doTick");
tickEntities = Timings.ofSafe(name + "tickEntities");
+
+ lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue");
}
public static Timing getTickList(WorldServer worldserver, String timingsType) {
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
index fe148495b..cc69ff3a4 100644
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -191,6 +191,13 @@ public class PaperConfig {
return config.getString(path, config.getString(path));
}
+ public static int maxTickMsLostLightQueue;
+ private static void lightQueue() {
+ int badSetting = config.getInt("queue-light-updates-max-loss", 10);
+ config.set("queue-light-updates-max-loss", null);
+ maxTickMsLostLightQueue = getInt("settings.queue-light-updates-max-loss", badSetting);
+ }
+
private static void timings() {
boolean timings = getBoolean("timings.enabled", true);
boolean verboseTimings = getBoolean("timings.verbose", true);
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 39d565db1..8f6f0288b 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -130,4 +130,12 @@ public class PaperWorldConfig {
netherVoidTopDamage = getBoolean( "nether-ceiling-void-damage", false );
log("Top of the nether void damage: " + netherVoidTopDamage);
}
+
+ public boolean queueLightUpdates;
+ private void queueLightUpdates() {
+ queueLightUpdates = getBoolean("queue-light-updates", false);
+ log("Lighting Queue enabled: " + queueLightUpdates);
+ log("Warning: This feature may help reduce TPS loss from light, but comes at the cost of buggy light data");
+ log("We are working to improve this feature.");
+ }
}
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index da2548603..87ec4d1a2 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -95,6 +95,7 @@ public class Chunk implements IChunkAccess {
return removed;
}
}
+ final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this);
// Paper end
public boolean areNeighborsLoaded(final int radius) {
switch (radius) {
@@ -285,7 +286,7 @@ public class Chunk implements IChunkAccess {
private void g(boolean flag) {
this.world.methodProfiler.enter("recheckGaps");
- if (this.world.areChunksLoaded(new BlockPosition(this.locX * 16 + 8, 0, this.locZ * 16 + 8), 16)) {
+ if (this.areNeighborsLoaded(1)) { // Paper
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (this.g[i + j * 16]) {
@@ -336,7 +337,7 @@ public class Chunk implements IChunkAccess {
}
private void a(int i, int j, int k, int l) {
- if (l > k && this.world.areChunksLoaded(new BlockPosition(i, 0, j), 16)) {
+ if (l > k && this.areNeighborsLoaded(1)) { // Paper
for (int i1 = k; i1 < l; ++i1) {
this.world.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j));
}
@@ -536,6 +537,7 @@ public class Chunk implements IChunkAccess {
if (flag1) {
this.initLighting();
} else {
+ this.runOrQueueLightUpdate(() -> { // Paper - Queue light update
int i1 = iblockdata.b(this.world, blockposition);
int j1 = iblockdata1.b(this.world, blockposition);
@@ -543,6 +545,7 @@ public class Chunk implements IChunkAccess {
if (i1 != j1 && (i1 < j1 || this.getBrightness(EnumSkyBlock.SKY, blockposition) > 0 || this.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 0)) {
this.c(i, k);
}
+ }); // Paper
}
TileEntity tileentity;
@@ -1361,6 +1364,16 @@ public class Chunk implements IChunkAccess {
return this.D == 8;
}
+ // Paper start
+ public void runOrQueueLightUpdate(Runnable runnable) {
+ if (this.world.paperConfig.queueLightUpdates) {
+ lightingQueue.add(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+ // Paper end
+
public static enum EnumTileEntityState {
IMMEDIATE, QUEUED, CHECK;
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 60abc5f28..6a9b9fa2a 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -314,6 +314,7 @@ public class ChunkProviderServer implements IChunkProvider {
return false;
}
save = event.isSaveChunk();
+ chunk.lightingQueue.processUnload(); // Paper
// Update neighbor counts
for (int x = -2; x < 3; x++) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 4acb908ec..194f8441e 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -894,7 +894,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
protected void a(BooleanSupplier booleansupplier) {
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper
this.slackActivityAccountant.tickStarted(); // Spigot
- long i = SystemUtils.getMonotonicNanos();
+ long i = SystemUtils.getMonotonicNanos(); long startTime = i; // Paper
++this.ticks;
if (this.S) {
@@ -952,6 +952,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
this.methodProfiler.exit();
this.methodProfiler.exit();
org.spigotmc.WatchdogThread.tick(); // Spigot
+ PaperLightingQueue.processQueue(startTime); // Paper
this.slackActivityAccountant.tickEnded(l); // Spigot
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper
}
diff --git a/src/main/java/net/minecraft/server/PaperLightingQueue.java b/src/main/java/net/minecraft/server/PaperLightingQueue.java
new file mode 100644
index 000000000..9783f3a0d
--- /dev/null
+++ b/src/main/java/net/minecraft/server/PaperLightingQueue.java
@@ -0,0 +1,98 @@
+package net.minecraft.server;
+
+import co.aikar.timings.Timing;
+import com.destroystokyo.paper.PaperConfig;
+import it.unimi.dsi.fastutil.objects.ObjectCollection;
+
+import java.util.ArrayDeque;
+
+class PaperLightingQueue {
+ private static final long MAX_TIME = (long) (1000000 * (50 + PaperConfig.maxTickMsLostLightQueue));
+
+ static void processQueue(long curTime) {
+ final long startTime = System.nanoTime();
+ final long maxTickTime = MAX_TIME - (startTime - curTime);
+
+ if (maxTickTime <= 0) {
+ return;
+ }
+
+ START:
+ for (World world : MinecraftServer.getServer().getWorlds()) {
+ if (!world.paperConfig.queueLightUpdates) {
+ continue;
+ }
+
+ ObjectCollection<Chunk> loadedChunks = ((WorldServer) world).getChunkProvider().chunks.values();
+ for (Chunk chunk : loadedChunks.toArray(new Chunk[0])) {
+ if (chunk.lightingQueue.processQueue(startTime, maxTickTime)) {
+ break START;
+ }
+ }
+ }
+ }
+
+ static class LightingQueue extends ArrayDeque<Runnable> {
+ final private Chunk chunk;
+
+ LightingQueue(Chunk chunk) {
+ super();
+ this.chunk = chunk;
+ }
+
+ /**
+ * Processes the lighting queue for this chunk
+ *
+ * @param startTime If start Time is 0, we will not limit execution time
+ * @param maxTickTime Maximum time to spend processing lighting updates
+ * @return true to abort processing furthur lighting updates
+ */
+ private boolean processQueue(long startTime, long maxTickTime) {
+ if (this.isEmpty()) {
+ return false;
+ }
+ if (isOutOfTime(maxTickTime, startTime)) {
+ return true;
+ }
+ try (Timing ignored = chunk.world.timings.lightingQueueTimer.startTiming()) {
+ Runnable lightUpdate;
+ while ((lightUpdate = this.poll()) != null) {
+ lightUpdate.run();
+ if (isOutOfTime(maxTickTime, startTime)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Flushes lighting updates to unload the chunk
+ */
+ void processUnload() {
+ if (!chunk.world.paperConfig.queueLightUpdates) {
+ return;
+ }
+ processQueue(0, 0); // No timeout
+
+ final int radius = 1;
+ for (int x = chunk.locX - radius; x <= chunk.locX + radius; ++x) {
+ for (int z = chunk.locZ - radius; z <= chunk.locZ + radius; ++z) {
+ if (x == chunk.locX && z == chunk.locZ) {
+ continue;
+ }
+
+ Chunk neighbor = chunk.world.getChunkIfLoaded(x, z);
+ if (neighbor != null) {
+ neighbor.lightingQueue.processQueue(0, 0); // No timeout
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean isOutOfTime(long maxTickTime, long startTime) {
+ return startTime > 0 && System.nanoTime() - startTime > maxTickTime;
+ }
+}
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 5d5a9f0bb..69f55d0dd 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -335,7 +335,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
if (iblockdata2.b(this, blockposition) != iblockdata1.b(this, blockposition) || iblockdata2.e() != iblockdata1.e()) {
this.methodProfiler.enter("checkLight");
- this.r(blockposition);
+ chunk.runOrQueueLightUpdate(() -> this.r(blockposition)); // Paper - Queue light update
this.methodProfiler.exit();
}
--
2.20.0