mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-01 17:01:56 +01:00
dd60ba4869
The lighting queue spreads out the processing of light updates across multiple ticks based on how much free time the server has left at the end of the tick.
231 lines
No EOL
10 KiB
Diff
231 lines
No EOL
10 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
|
|
|
|
|
|
diff --git a/src/main/java/co/aikar/timings/SpigotTimings.java b/src/main/java/co/aikar/timings/SpigotTimings.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/co/aikar/timings/SpigotTimings.java
|
|
+++ b/src/main/java/co/aikar/timings/SpigotTimings.java
|
|
@@ -0,0 +0,0 @@ public final class SpigotTimings {
|
|
public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update");
|
|
public static final Timing serverCommandTimer = Timings.ofSafe("Server Command");
|
|
public static final Timing worldSaveTimer = Timings.ofSafe("World Save");
|
|
+ public static final Timing lightingQueueTimer = Timings.ofSafe("Lighting Queue");
|
|
|
|
public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity");
|
|
public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity");
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 {
|
|
|
|
}
|
|
|
|
+ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 {
|
|
private int w;
|
|
private ConcurrentLinkedQueue<BlockPosition> x;
|
|
protected gnu.trove.map.hash.TObjectIntHashMap<Class> entityCount = new gnu.trove.map.hash.TObjectIntHashMap<Class>(); // Spigot
|
|
+ public int lightUpdates; // Paper - Number of queued light updates for this chunk
|
|
|
|
// CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking
|
|
private int neighbors = 0x1 << 12;
|
|
@@ -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)) {
|
|
+ // Paper start - Queue light update
|
|
+ if (!world.paperConfig.queueLightUpdates) {
|
|
+ recheckGaps(flag);
|
|
+ } else {
|
|
+ ++lightUpdates;
|
|
+ world.getServer().getServer().lightingQueue.add(() -> {
|
|
+ recheckGaps(flag);
|
|
+ --lightUpdates;
|
|
+ });
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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.h[i + j * 16]) {
|
|
@@ -0,0 +0,0 @@ public class Chunk {
|
|
} else {
|
|
if (flag) {
|
|
this.initLighting();
|
|
- } else {
|
|
+ } else if (!world.paperConfig.queueLightUpdates) { // Paper
|
|
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 start - Queue light update
|
|
+ } else {
|
|
+ int j1 = iblockdata.c();
|
|
+ int k1 = iblockdata1.c();
|
|
+
|
|
+ ++lightUpdates;
|
|
+ world.getServer().getServer().lightingQueue.add(() -> {
|
|
+ if (j1 > 0) {
|
|
+ if (j >= i1) {
|
|
+ this.c(i, j + 1, k);
|
|
+ }
|
|
+ } else if (j == i1 - 1) {
|
|
+ this.c(i, j, k);
|
|
+ }
|
|
+
|
|
+ if (j1 != k1 && (j1 < k1 || this.getBrightness(EnumSkyBlock.SKY, blockposition) > 0 || this.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 0)) {
|
|
+ this.d(i, k);
|
|
+ }
|
|
+
|
|
+ --lightUpdates;
|
|
+ });
|
|
+ // Paper end
|
|
}
|
|
|
|
TileEntity tileentity;
|
|
@@ -0,0 +0,0 @@ public class Chunk {
|
|
|
|
private EnumTileEntityState() {}
|
|
}
|
|
+
|
|
+ // Paper start
|
|
+ public boolean hasLightUpdates() {
|
|
+ if (world.paperConfig.queueLightUpdates) {
|
|
+ if (lightUpdates > 0) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ for (int x = locX - 2; x <= locX + 2; ++x) {
|
|
+ for (int z = locZ - 2; z <= locZ + 2; ++z) {
|
|
+ if ((x == 0 && z == 0) || (x == locX && z == locZ)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ Chunk chunk = world.getChunkIfLoaded(x, z);
|
|
+ if (chunk != null && chunk.lightUpdates > 0) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 {
|
|
long chunkcoordinates = this.unloadQueue.popFirst();
|
|
Chunk chunk = this.chunks.get(chunkcoordinates);
|
|
if (chunk == null) continue;
|
|
+ if (chunk.hasLightUpdates()) continue; // Paper - Don't unload chunks with pending light updates.
|
|
|
|
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk);
|
|
server.getPluginManager().callEvent(event);
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -0,0 +0,0 @@ import org.bukkit.craftbukkit.CraftServer;
|
|
// CraftBukkit end
|
|
import co.aikar.timings.SpigotTimings; // Paper
|
|
|
|
+// Paper start
|
|
+import java.util.LinkedList;
|
|
+import java.util.Queue;
|
|
+// Paper end
|
|
+
|
|
public abstract class MinecraftServer implements Runnable, ICommandListener, IAsyncTaskHandler, IMojangStatistics {
|
|
|
|
public static final Logger LOGGER = LogManager.getLogger();
|
|
@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
|
|
public final Thread primaryThread;
|
|
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
|
public int autosavePeriod;
|
|
+ public final Queue<Runnable> lightingQueue = new LinkedList<Runnable>(); // Paper - Queued light updates
|
|
// CraftBukkit end
|
|
|
|
public MinecraftServer(OptionSet options, Proxy proxy, DataConverterManager dataconvertermanager, YggdrasilAuthenticationService yggdrasilauthenticationservice, MinecraftSessionService minecraftsessionservice, GameProfileRepository gameprofilerepository, UserCache usercache) {
|
|
@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
|
|
|
|
this.methodProfiler.b();
|
|
this.methodProfiler.b();
|
|
+
|
|
+ // Paper start - Flush light updates
|
|
+ if (!lightingQueue.isEmpty()) {
|
|
+ SpigotTimings.lightingQueueTimer.startTiming();
|
|
+
|
|
+ int updatesThisTick = 0;
|
|
+ long cachedTime = System.currentTimeMillis();
|
|
+ long startTime = cachedTime - (this.h[this.ticks % 100] / 1000000);
|
|
+ int maxTickTimeCap = MathHelper.floor((TICK_TIME / 1000000) * 0.8);
|
|
+ int maxTickTime = Math.max(0, (int) (maxTickTimeCap - (cachedTime - startTime)));
|
|
+ Runnable lightUpdate;
|
|
+
|
|
+ while (maxTickTime > 0 && (lightUpdate = lightingQueue.poll()) != null) {
|
|
+ lightUpdate.run();
|
|
+ if (++updatesThisTick % 10 == 0) {
|
|
+ long currentTime = System.currentTimeMillis();
|
|
+ if (currentTime - cachedTime > maxTickTime) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ cachedTime = currentTime;
|
|
+ maxTickTime = Math.max(0, (int) (maxTickTimeCap - (currentTime - startTime)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ SpigotTimings.lightingQueueTimer.stopTiming();
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
org.spigotmc.WatchdogThread.tick(); // Spigot
|
|
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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);
|
|
+ // Paper start - Queue light update
|
|
+ if (!paperConfig.queueLightUpdates) {
|
|
+ this.w(blockposition);
|
|
+ } else {
|
|
+ ++chunk.lightUpdates;
|
|
+ getMinecraftServer().lightingQueue.add(() -> {
|
|
+ this.w(blockposition);
|
|
+ --chunk.lightUpdates;
|
|
+ });
|
|
+ }
|
|
+ // Paper end
|
|
this.methodProfiler.b();
|
|
}
|
|
|
|
--
|