Instead of overriding add within the queue, never add runnables to the queue if the light queue is disabled. This change is made to make timings reports and stacktraces less confusing for administrators, who prior to this change, would have seen the lighting queue referenced in both, regardless of whether or not it was enabled. This change should not affect performance, nor is it made with the intent to.
250 lines
10 KiB
Diff
250 lines
10 KiB
Diff
From 1564084393d4718d099082ea434dd8f2b1a0b53a Mon Sep 17 00:00:00 2001
|
|
From: Byteflux <byte@byteflux.net>
|
|
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 e0ad559b..4eebd9fa 100644
|
|
--- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java
|
|
+++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
|
|
@@ -49,6 +49,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() +" - ";
|
|
|
|
@@ -95,5 +97,7 @@ 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");
|
|
}
|
|
}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
index e524a464..fd606ee1 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -143,4 +143,10 @@ 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);
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
|
|
index 66c78083..c1f25e8d 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -33,6 +33,7 @@ public class Chunk {
|
|
private boolean m;
|
|
public final Map<BlockPosition, TileEntity> tileEntities;
|
|
public final List<Entity>[] entitySlices; // Spigot
|
|
+ final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Paper
|
|
private boolean done;
|
|
private boolean lit;
|
|
private boolean r;
|
|
@@ -229,6 +230,13 @@ public class Chunk {
|
|
private void h(boolean flag) {
|
|
this.world.methodProfiler.a("recheckGaps");
|
|
if (this.world.areChunksLoaded(new BlockPosition(this.locX * 16 + 8, 0, this.locZ * 16 + 8), 16)) {
|
|
+ this.runOrQueueLightUpdate(() -> recheckGaps(flag)); // Paper - Queue light update
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void recheckGaps(boolean flag) {
|
|
+ if (true) {
|
|
+ // Paper end
|
|
for (int i = 0; i < 16; ++i) {
|
|
for (int j = 0; j < 16; ++j) {
|
|
if (this.i[i + j * 16]) {
|
|
@@ -482,6 +490,7 @@ public class Chunk {
|
|
if (flag) {
|
|
this.initLighting();
|
|
} else {
|
|
+ this.runOrQueueLightUpdate(() -> { // Paper - Queue light update
|
|
int j1 = iblockdata.c();
|
|
int k1 = iblockdata1.c();
|
|
|
|
@@ -496,6 +505,7 @@ public class Chunk {
|
|
if (j1 != k1 && (j1 < k1 || this.getBrightness(EnumSkyBlock.SKY, blockposition) > 0 || this.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 0)) {
|
|
this.d(i, k);
|
|
}
|
|
+ }); // Paper
|
|
}
|
|
|
|
TileEntity tileentity;
|
|
@@ -1340,6 +1350,16 @@ public class Chunk {
|
|
this.w = i;
|
|
}
|
|
|
|
+ // 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 69ded6aa..e9bc23a6 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -295,6 +295,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 47fe9262..d63a243f 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -722,7 +722,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IAs
|
|
protected void C() throws ExceptionWorldConflict { // CraftBukkit - added throws
|
|
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper
|
|
this.slackActivityAccountant.tickStarted(); // Spigot
|
|
- long i = System.nanoTime();
|
|
+ long i = System.nanoTime(); long startTime = i; // Paper
|
|
|
|
++this.ticks;
|
|
if (this.T) {
|
|
@@ -783,6 +783,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IAs
|
|
this.methodProfiler.b();
|
|
|
|
org.spigotmc.WatchdogThread.tick(); // Spigot
|
|
+ PaperLightingQueue.processQueue(startTime); // Paper
|
|
this.slackActivityAccountant.tickEnded(tickNanos); // 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 00000000..345cd582
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/minecraft/server/PaperLightingQueue.java
|
|
@@ -0,0 +1,92 @@
|
|
+package net.minecraft.server;
|
|
+
|
|
+import co.aikar.timings.Timing;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectCollection;
|
|
+
|
|
+import java.util.ArrayDeque;
|
|
+
|
|
+class PaperLightingQueue {
|
|
+ private static final long MAX_TIME = (long) (1000000000 / 20 * .95);
|
|
+ private static int updatesThisTick;
|
|
+
|
|
+
|
|
+ static void processQueue(long curTime) {
|
|
+ updatesThisTick = 0;
|
|
+
|
|
+ final long startTime = System.nanoTime();
|
|
+ final long maxTickTime = MAX_TIME - (startTime - curTime);
|
|
+
|
|
+ START:
|
|
+ for (World world : MinecraftServer.getServer().worlds) {
|
|
+ if (!world.paperConfig.queueLightUpdates) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ObjectCollection<Chunk> loadedChunks = ((WorldServer) world).getChunkProviderServer().chunks.values();
|
|
+ for (Chunk chunk : loadedChunks.toArray(new Chunk[loadedChunks.size()])) {
|
|
+ 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;
|
|
+ }
|
|
+ try (Timing ignored = chunk.world.timings.lightingQueueTimer.startTiming()) {
|
|
+ Runnable lightUpdate;
|
|
+ while ((lightUpdate = this.poll()) != null) {
|
|
+ lightUpdate.run();
|
|
+ if (startTime > 0 && ++PaperLightingQueue.updatesThisTick % 10 == 0 && PaperLightingQueue.updatesThisTick > 10) {
|
|
+ if (System.nanoTime() - startTime > maxTickTime) {
|
|
+ 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; // TODO: bitflip, why should this ever be 2?
|
|
+ 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 = MCUtil.getLoadedChunkWithoutMarkingActive(chunk.world, x, z);
|
|
+ if (neighbor != null) {
|
|
+ neighbor.lightingQueue.processQueue(0, 0); // No timeout
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index fd5f8102..77ed2d24 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -391,7 +391,7 @@ public abstract class World implements IBlockAccess {
|
|
} else {
|
|
if (iblockdata.c() != iblockdata1.c() || iblockdata.d() != iblockdata1.d()) {
|
|
this.methodProfiler.a("checkLight");
|
|
- this.w(blockposition);
|
|
+ chunk.runOrQueueLightUpdate(() -> this.w(blockposition)); // Paper - Queue light update
|
|
this.methodProfiler.b();
|
|
}
|
|
|
|
--
|
|
2.15.1.windows.2
|
|
|