mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-30 16:19:03 +01:00
8778a2ef97
This enables us a fast reference to the entities current chunk instead of having to look it up by hashmap lookups. We also store counts by type to further enable other performance optimizations in later patches.
247 lines
No EOL
9.9 KiB
Diff
247 lines
No EOL
9.9 KiB
Diff
From 0000000000000000000000000000000000000000 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 e0ad559b7..4eebd9fae 100644
|
|
--- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java
|
|
+++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
|
|
@@ -0,0 +0,0 @@ public class WorldTimingsHandler {
|
|
public final Timing worldSaveLevel;
|
|
public final Timing chunkSaveData;
|
|
|
|
+ public final Timing lightingQueueTimer;
|
|
+
|
|
public WorldTimingsHandler(World server) {
|
|
String name = server.worldData.getName() +" - ";
|
|
|
|
@@ -0,0 +0,0 @@ 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 bf7af475c..94a173045 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -0,0 +0,0 @@ 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 50113c303..aaf253c89 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -0,0 +0,0 @@ public class Chunk {
|
|
return removed;
|
|
}
|
|
}
|
|
+ final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this);
|
|
// Paper end
|
|
private boolean done;
|
|
private boolean lit;
|
|
@@ -0,0 +0,0 @@ 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]) {
|
|
@@ -0,0 +0,0 @@ public class Chunk {
|
|
if (flag) {
|
|
this.initLighting();
|
|
} else {
|
|
+ this.runOrQueueLightUpdate(() -> { // Paper - Queue light update
|
|
int j1 = iblockdata.c();
|
|
int k1 = iblockdata1.c();
|
|
|
|
@@ -0,0 +0,0 @@ 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;
|
|
@@ -0,0 +0,0 @@ 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 bd006ef74..2b320cbd1 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -0,0 +0,0 @@ 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 47fe9262a..d63a243f3 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -0,0 +0,0 @@ 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) {
|
|
@@ -0,0 +0,0 @@ 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 000000000..345cd5824
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/minecraft/server/PaperLightingQueue.java
|
|
@@ -0,0 +0,0 @@
|
|
+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 fd5f8102a..77ed2d249 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -0,0 +0,0 @@ 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();
|
|
}
|
|
|
|
--
|