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 e3ec2eda60b7343add29c6546bc8e818239e6a18 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 2767ffb78..f7a0c18a8 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -149,4 +149,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 e18fb875f..8c015c095 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -292,6 +292,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 91fb12128..1d9ba0d0f 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 4a35d06e4..b38228707 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.11.0.windows.3
|