Paper/Spigot-Server-Patches/0028-Lighting-Queue.patch
Shane Freeder f4e3b4e439
Updated Upstream (Bukkit/CraftBukkit/Spigot)
Upstream has released updates that appears to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
5b680f0b Note maximum objective score length in documentation

CraftBukkit Changes:
5932f8a7 Load default world spawn areas in consistent order
3a5dc78f Fix confusing migration message appearing on fresh server
516a408f Remove redundant CraftBukkit change for secondary world data
73a2c749 Process conversation input on the main thread.
100c3f07 Cap Objective Score Length
6e842759 Cross World Entity Teleportation
7deba1c6 Check for blank OfflinePlayer Names
f2746a5e Descriptive kick reasons instead of Nope!
b0212308 Cap Channel Registrations
a610dcd8 Identify CraftScheduler threads with useful names

Spigot Changes:
19c3c5a5 Rebuild patches
2019-01-06 17:15:21 +00:00

281 lines
12 KiB
Diff

From 4ed9141e99766cbb2b618d73156f34a48726cb7f 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 0a436133a..2afd6befa 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -94,6 +94,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) {
@@ -284,7 +285,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]) {
@@ -335,7 +336,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));
}
@@ -535,6 +536,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);
@@ -542,6 +544,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;
@@ -1360,6 +1363,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 eb83e20d5..c2ecc034e 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -313,6 +313,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 7ca7b9f3a..00f1c36e9 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -890,7 +890,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) {
@@ -948,6 +948,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 086d64628..bf05c47a1 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -330,7 +330,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.1