CavePVP-Stuff/cSpigot-master/spigot-server-Patches/0047-Configurable-async-lig...

483 lines
19 KiB
Diff

From b1cba01d8a1076be8f83077de542d6bc32acd587 Mon Sep 17 00:00:00 2001
From: Poweruser_rs <poweruser.rs@hotmail.com>
Date: Thu, 17 Dec 2015 05:00:58 +0100
Subject: [PATCH] Configurable async light updates
diff --git a/src/main/java/net/frozenorb/LightingUpdater.java b/src/main/java/net/frozenorb/LightingUpdater.java
new file mode 100644
index 000000000..a0b5960e3
--- /dev/null
+++ b/src/main/java/net/frozenorb/LightingUpdater.java
@@ -0,0 +1,244 @@
+package net.frozenorb;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.bukkit.craftbukkit.util.LongHash;
+
+import net.minecraft.server.Block;
+import net.minecraft.server.Blocks;
+import net.minecraft.server.Chunk;
+import net.minecraft.server.EnumSkyBlock;
+import net.minecraft.server.Facing;
+import net.minecraft.server.IWorldAccess;
+import net.minecraft.server.MathHelper;
+
+public class LightingUpdater {
+
+ private int[] arrayI;
+ private HashMap<Long, Chunk> chunks;
+
+ public LightingUpdater() {
+ this.arrayI = new int['\u8000'];
+ this.chunks = new HashMap<Long, Chunk>();
+ }
+
+ public boolean c(EnumSkyBlock enumskyblock, int i, int j, int k, Chunk chunk, List<Chunk> neighbors) { // PaperSpigot
+ if (chunk != null && neighbors != null) {
+ this.chunks.clear();
+ this.chunks.put(LongHash.toLong(chunk.locX, chunk.locZ), chunk);
+ for(Chunk neighborchunk: neighbors) {
+ this.chunks.put(LongHash.toLong(neighborchunk.locX, neighborchunk.locZ), neighborchunk);
+ }
+
+ int l = 0;
+ int i1 = 0;
+
+ //this.methodProfiler.a("getBrightness");
+ int j1 = this.b(enumskyblock, i, j, k);
+ int k1 = this.a(i, j, k, enumskyblock);
+ int l1;
+ int i2;
+ int j2;
+ int k2;
+ int l2;
+ int i3;
+ int j3;
+ int k3;
+ int l3;
+
+ if (k1 > j1) {
+ arrayI[i1++] = 133152;
+ } else if (k1 < j1) {
+ arrayI[i1++] = 133152 | j1 << 18;
+
+ while (l < i1) {
+ l1 = arrayI[l++];
+ i2 = (l1 & 63) - 32 + i;
+ j2 = (l1 >> 6 & 63) - 32 + j;
+ k2 = (l1 >> 12 & 63) - 32 + k;
+ l2 = l1 >> 18 & 15;
+ i3 = this.b(enumskyblock, i2, j2, k2);
+ if (i3 == l2) {
+ this.b(enumskyblock, i2, j2, k2, 0);
+ if (l2 > 0) {
+ j3 = MathHelper.a(i2 - i);
+ l3 = MathHelper.a(j2 - j);
+ k3 = MathHelper.a(k2 - k);
+ if (j3 + l3 + k3 < 17) {
+ for (int i4 = 0; i4 < 6; ++i4) {
+ int j4 = i2 + Facing.b[i4];
+ int k4 = j2 + Facing.c[i4];
+ int l4 = k2 + Facing.d[i4];
+ int i5 = Math.max(1, this.getType(j4, k4, l4).k());
+
+ i3 = this.b(enumskyblock, j4, k4, l4);
+ if (i3 == l2 - i5 && i1 < arrayI.length) {
+ arrayI[i1++] = j4 - i + 32 | k4 - j + 32 << 6 | l4 - k + 32 << 12 | l2 - i5 << 18;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ l = 0;
+ }
+
+ //this.methodProfiler.b();
+ //this.methodProfiler.a("checkedPosition < toCheckCount");
+
+ while (l < i1) {
+ l1 = arrayI[l++];
+ i2 = (l1 & 63) - 32 + i;
+ j2 = (l1 >> 6 & 63) - 32 + j;
+ k2 = (l1 >> 12 & 63) - 32 + k;
+ l2 = this.b(enumskyblock, i2, j2, k2);
+ i3 = this.a(i2, j2, k2, enumskyblock);
+ if (i3 != l2) {
+ this.b(enumskyblock, i2, j2, k2, i3);
+ if (i3 > l2) {
+ j3 = Math.abs(i2 - i);
+ l3 = Math.abs(j2 - j);
+ k3 = Math.abs(k2 - k);
+ boolean flag = i1 < arrayI.length - 6;
+
+ if (j3 + l3 + k3 < 17 && flag) {
+ if (this.b(enumskyblock, i2 - 1, j2, k2) < i3) {
+ arrayI[i1++] = i2 - 1 - i + 32 + (j2 - j + 32 << 6) + (k2 - k + 32 << 12);
+ }
+
+ if (this.b(enumskyblock, i2 + 1, j2, k2) < i3) {
+ arrayI[i1++] = i2 + 1 - i + 32 + (j2 - j + 32 << 6) + (k2 - k + 32 << 12);
+ }
+
+ if (this.b(enumskyblock, i2, j2 - 1, k2) < i3) {
+ arrayI[i1++] = i2 - i + 32 + (j2 - 1 - j + 32 << 6) + (k2 - k + 32 << 12);
+ }
+
+ if (this.b(enumskyblock, i2, j2 + 1, k2) < i3) {
+ arrayI[i1++] = i2 - i + 32 + (j2 + 1 - j + 32 << 6) + (k2 - k + 32 << 12);
+ }
+
+ if (this.b(enumskyblock, i2, j2, k2 - 1) < i3) {
+ arrayI[i1++] = i2 - i + 32 + (j2 - j + 32 << 6) + (k2 - 1 - k + 32 << 12);
+ }
+
+ if (this.b(enumskyblock, i2, j2, k2 + 1) < i3) {
+ arrayI[i1++] = i2 - i + 32 + (j2 - j + 32 << 6) + (k2 + 1 - k + 32 << 12);
+ }
+ }
+ }
+ }
+ }
+
+ // PaperSpigot start - Asynchronous light updates
+ if (chunk.world.paperSpigotConfig.useAsyncLighting) {
+ chunk.pendingLightUpdates.decrementAndGet();
+ if (neighbors != null) {
+ for (Chunk neighbor : neighbors) {
+ neighbor.pendingLightUpdates.decrementAndGet();
+ }
+ }
+ }
+ // PaperSpigot end
+ //this.methodProfiler.b();
+ this.chunks.clear();
+ return true;
+ }
+ return false;
+ }
+
+ private void b(EnumSkyBlock enumskyblock, int i, int j, int k, int l) {
+ if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000) {
+ if (j >= 0) {
+ if (j < 256) {
+ Chunk chunk = this.getChunk(i >> 4, k >> 4);
+ if(chunk != null) {
+ chunk.a(enumskyblock, i & 15, j, k & 15, l);
+ chunk.world.m(i, j, k);
+ }
+ }
+ }
+ }
+ }
+
+ private int b(EnumSkyBlock enumskyblock, int x, int y, int z) {
+ Chunk chunk = this.getChunk(x >> 4, z >> 4);
+ if (chunk != null && x >= -30000000 && z >= -30000000 && x < 30000000 && z < 30000000) {
+ if (y < 0) {
+ y = 0;
+ }
+
+ if (y >= 256) {
+ y = 255;
+ }
+ return chunk.getBrightness(enumskyblock, x & 15, y, z & 15);
+ } else {
+ return enumskyblock.c;
+ }
+ }
+
+ private Chunk getChunk(int x, int z) {
+ return this.chunks.get(LongHash.toLong(x, z));
+ }
+
+ private int a(int i, int j, int k, EnumSkyBlock enumskyblock) {
+ if (enumskyblock == EnumSkyBlock.SKY && this.i(i, j, k)) {
+ return 15;
+ } else {
+ Block block = this.getType(i, j, k);
+ int l = enumskyblock == EnumSkyBlock.SKY ? 0 : block.m();
+ int i1 = block.k();
+
+ if (i1 >= 15 && block.m() > 0) {
+ i1 = 1;
+ }
+
+ if (i1 < 1) {
+ i1 = 1;
+ }
+
+ if (i1 >= 15) {
+ return 0;
+ } else if (l >= 14) {
+ return l;
+ } else {
+ for (int j1 = 0; j1 < 6; ++j1) {
+ int k1 = i + Facing.b[j1];
+ int l1 = j + Facing.c[j1];
+ int i2 = k + Facing.d[j1];
+ int j2 = this.b(enumskyblock, k1, l1, i2) - i1;
+
+ if (j2 > l) {
+ l = j2;
+ }
+
+ if (l >= 14) {
+ return l;
+ }
+ }
+
+ return l;
+ }
+ }
+ }
+
+ private Block getType(int i, int j, int k) {
+ if (i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000 && j >= 0 && j < 256) {
+ Chunk chunk = this.getChunk(i >> 4, k >> 4);
+ if(chunk != null) {
+ return chunk.getType(i & 15, j, k & 15);
+ }
+ }
+ return Blocks.AIR;
+ }
+
+ private boolean i(int i, int j, int k) {
+ Chunk chunk = this.getChunk(i >> 4, k >> 4);
+ if(chunk != null) {
+ return chunk.d(i & 15, j, k & 15);
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index 86a13e755..1b5720622 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -8,6 +8,7 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger; // PaperSpigot
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -41,6 +42,10 @@ public class Chunk {
public long s;
private int x;
protected net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap<Class> entityCount = new net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap<Class>(); // Spigot
+ // PaperSpigot start - Asynchronous light updates
+ public AtomicInteger pendingLightUpdates = new AtomicInteger();
+ public long lightUpdateTime;
+ // PaperSpigot end
// CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking
private int neighbors = 0x1 << 12;
@@ -295,7 +300,7 @@ public class Chunk {
private void c(int i, int j, int k, int l) {
if (l > k && this.world.areChunksLoaded(i, 0, j, 16)) {
for (int i1 = k; i1 < l; ++i1) {
- this.world.c(EnumSkyBlock.SKY, i, i1, j);
+ this.world.updateLight(EnumSkyBlock.SKY, i, i1, j); // PaperSpigot - Asynchronous lighting updates
}
this.n = true;
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 22330c341..8aff01043 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -55,6 +55,12 @@ public class ChunkProviderServer implements IChunkProvider {
}
public void queueUnload(int i, int j) {
+ // PaperSpigot start - Asynchronous lighting updates
+ Chunk chunk = this.chunks.get(LongHash.toLong(i, j));
+ if (chunk != null && chunk.world.paperSpigotConfig.useAsyncLighting && (chunk.pendingLightUpdates.get() > 0 || chunk.world.getTime() - chunk.lightUpdateTime < 20)) {
+ return;
+ }
+ // PaperSpigot end
if (this.world.worldProvider.e()) {
ChunkCoordinates chunkcoordinates = this.world.getSpawn();
int k = i * 16 + 8 - chunkcoordinates.x;
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 042fc7756..ebb537577 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -11,6 +11,13 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
+// PaperSpigot start
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import net.minecraft.util.com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.bukkit.craftbukkit.CraftChunk;
+// PaperSpigot end
+
// CraftBukkit start
import org.bukkit.Bukkit;
import org.bukkit.block.BlockState;
@@ -28,6 +35,10 @@ import org.bukkit.event.weather.WeatherChangeEvent;
import org.bukkit.event.weather.ThunderChangeEvent;
// CraftBukkit end
+// Poweruser start
+import net.frozenorb.LightingUpdater;
+// Poweruser end
+
public abstract class World implements IBlockAccess {
public boolean d;
@@ -114,6 +125,17 @@ public abstract class World implements IBlockAccess {
public static boolean haveWeSilencedAPhysicsCrash;
public static String blockLocation;
public List<TileEntity> triggerHoppersList = new ArrayList<TileEntity>(); // Spigot, When altHopperTicking, tile entities being added go through here.
+ // Poweruser start - only one thread, and with lower priority. Instead of one for each world
+ private static ExecutorService lightingExecutor; // PaperSpigot - Asynchronous lighting updates
+ private LightingUpdater lightingUpdater = new LightingUpdater();
+
+ public static ExecutorService getLightingExecutor() {
+ if(lightingExecutor == null) {
+ lightingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setPriority(Thread.NORM_PRIORITY - 1).setNameFormat("PaperSpigot - Lighting Thread").build());
+ }
+ return lightingExecutor;
+ }
+ // Poweruser end
public static long chunkToKey(int x, int z)
{
@@ -614,7 +636,7 @@ public abstract class World implements IBlockAccess {
if (!this.worldProvider.g) {
for (i1 = k; i1 <= l; ++i1) {
- this.c(EnumSkyBlock.SKY, i, i1, j);
+ this.updateLight(EnumSkyBlock.SKY, i, i1, j); // PaperSpigot - Asynchronous lighting updates
}
}
@@ -2383,10 +2405,10 @@ public abstract class World implements IBlockAccess {
boolean flag = false;
if (!this.worldProvider.g) {
- flag |= this.c(EnumSkyBlock.SKY, i, j, k);
+ flag |= this.updateLight(EnumSkyBlock.SKY, i, j, k); // PaperSpigot - Asynchronous lighting updates
}
- flag |= this.c(EnumSkyBlock.BLOCK, i, j, k);
+ flag |= this.updateLight(EnumSkyBlock.BLOCK, i, j, k); // PaperSpigot - Asynchronous lighting updates
return flag;
}
@@ -2431,10 +2453,10 @@ public abstract class World implements IBlockAccess {
}
}
- public boolean c(EnumSkyBlock enumskyblock, int i, int j, int k) {
+ public boolean c(EnumSkyBlock enumskyblock, int i, int j, int k, Chunk chunk, List<Chunk> neighbors) { // PaperSpigot
// CraftBukkit start - Use neighbor cache instead of looking up
- Chunk chunk = this.getChunkIfLoaded(i >> 4, k >> 4);
- if (chunk == null || !chunk.areNeighborsLoaded(1) /* !this.areChunksLoaded(i, j, k, 17)*/) {
+ //Chunk chunk = this.getChunkIfLoaded(i >> 4, k >> 4);
+ if (chunk == null /*|| !chunk.areNeighborsLoaded(1)*/ /* !this.areChunksLoaded(i, j, k, 17)*/) {
// CraftBukkit end
return false;
} else {
@@ -2539,11 +2561,74 @@ public abstract class World implements IBlockAccess {
}
}
+ // PaperSpigot start - Asynchronous light updates
+ if (chunk.world.paperSpigotConfig.useAsyncLighting) {
+ chunk.pendingLightUpdates.decrementAndGet();
+ if (neighbors != null) {
+ for (Chunk neighbor : neighbors) {
+ neighbor.pendingLightUpdates.decrementAndGet();
+ }
+ }
+ }
+ // PaperSpigot end
this.methodProfiler.b();
return true;
}
}
+ // PaperSpigot start - Asynchronous lighting updates
+ public boolean updateLight(final EnumSkyBlock enumskyblock, final int x, final int y, final int z) {
+ final Chunk chunk = this.getChunkIfLoaded(x >> 4, z >> 4);
+ if (chunk == null || !chunk.areNeighborsLoaded(2)) { // Poweruser - radius 2
+ return false;
+ }
+
+ if (!chunk.world.paperSpigotConfig.useAsyncLighting) {
+ return this.c(enumskyblock, x, y, z, chunk, null);
+ }
+
+ chunk.pendingLightUpdates.incrementAndGet();
+ chunk.lightUpdateTime = chunk.world.getTime();
+
+ final List<Chunk> neighbors = new ArrayList<Chunk>();
+ // Poweruser start
+ int chunkx = chunk.locX;
+ int chunkz = chunk.locZ;
+ int radius = 2;
+ for (int cx = chunkx - radius; cx <= chunkx + radius; ++cx) {
+ for (int cz = chunkz - radius; cz <= chunkz + radius; ++cz) {
+ if(cx != chunkx || cz != chunkz) {
+ // Poweruser end
+ Chunk neighbor = this.getChunkIfLoaded(cx, cz);
+ if (neighbor != null) {
+ neighbor.pendingLightUpdates.incrementAndGet();
+ neighbor.lightUpdateTime = chunk.world.getTime();
+ neighbors.add(neighbor);
+ }
+ }
+ }
+ }
+
+ if (!Bukkit.isPrimaryThread()) {
+ return this.c(enumskyblock, x, y, z, chunk, neighbors);
+ }
+
+ // Poweruser start
+ getLightingExecutor().submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ World.this.lightingUpdater.c(enumskyblock, x, y, z, chunk, neighbors);
+ } catch (Exception e) {
+ MinecraftServer.getLogger().error("Thread " + Thread.currentThread().getName() + " encountered an exception: " + e.getMessage(), e);
+ }
+ }
+ });
+ // Poweruser end
+ return true;
+ }
+ // PaperSpigot end
+
public boolean a(boolean flag) {
return false;
}
diff --git a/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java b/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java
index 44d271ad5..ddd33e81b 100644
--- a/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java
+++ b/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java
@@ -206,4 +206,11 @@ public class PaperSpigotWorldConfig
log( "WorldServer TickNextTickList cap set at " + tickNextTickListCap );
log( "WorldServer TickNextTickList cap always processes redstone: " + tickNextTickListCapIgnoresRedstone );
}
+
+ public boolean useAsyncLighting;
+ private void useAsyncLighting()
+ {
+ useAsyncLighting = getBoolean( "use-async-lighting", false );
+ log( "World async lighting: " + useAsyncLighting );
+ }
}
--
2.13.3