aboutsummaryrefslogtreecommitdiffhomepage
path: root/Spigot-Server-Patches/0029-Lighting-Queue.patch
blob: 84221c704a340562dd075b0e6f4281728a3d3188 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
From d13d53ab6bafe9851cb3a816ca64e42ab83d6d29 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 e7789117b..f90f5bf84 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 chunkSaveNop;
     public final Timing chunkSaveData;
 
+    public final Timing lightingQueueTimer;
+
     public WorldTimingsHandler(World server) {
         String name = server.worldData.getName() +" - ";
 
@@ -97,5 +99,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 e524a464f..fd606ee14 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 1c0108ef1..b80f95159 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)) {
+            lightingQueue.add(() -> 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]) {
@@ -481,7 +489,7 @@ public class Chunk {
             } else {
                 if (flag) {
                     this.initLighting();
-                } else {
+                } else { lightingQueue.add(() -> { // Paper - Queue light update
                     int j1 = iblockdata.c();
                     int k1 = iblockdata1.c();
 
@@ -496,6 +504,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;
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 3ba489d4f..f7f2d12cf 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 4476799d8..f953ef8e0 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -721,7 +721,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, 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) {
@@ -782,6 +782,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, 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..d8d3e1efd
--- /dev/null
+++ b/src/main/java/net/minecraft/server/PaperLightingQueue.java
@@ -0,0 +1,101 @@
+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;
+        }
+
+        @Override
+        public boolean add(Runnable runnable) {
+            if (chunk.world.paperConfig.queueLightUpdates) {
+                return super.add(runnable);
+            }
+            runnable.run();
+            return true;
+        }
+
+        /**
+         * 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 632d5c760..e6e85e7a8 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -387,7 +387,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.lightingQueue.add(() -> this.w(blockposition)); // Paper - Queue light update
                     this.methodProfiler.b();
                 }
 
-- 
2.12.0.windows.1