Async lighting updates
This commit is contained in:
54
src/main/java/net/minecraft/server/AkarinAsyncLighting.java
Normal file
54
src/main/java/net/minecraft/server/AkarinAsyncLighting.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@ThreadSafe
|
||||
@AllArgsConstructor
|
||||
public class AkarinAsyncLighting {
|
||||
private final World world;
|
||||
private final ChunkSection[] sections;
|
||||
private final Map<HeightMap.Type, HeightMap> heightMap;
|
||||
|
||||
public void getBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition, IntConsumer callback) {
|
||||
this.getBrightness(enumskyblock, blockposition, this.world.o().g(), callback);
|
||||
}
|
||||
|
||||
public void getBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition, boolean canSeeSky, IntConsumer callback) {
|
||||
int i = blockposition.getX() & 15;
|
||||
int j = blockposition.getY();
|
||||
int k = blockposition.getZ() & 15;
|
||||
int l = j >> 4;
|
||||
|
||||
if (l >= 0 && l <= this.sections.length - 1) {
|
||||
ChunkSection chunksection = this.sections[l];
|
||||
|
||||
if (chunksection == Chunk.a)
|
||||
callback.accept(this.canHasLight(blockposition) ? enumskyblock.c : 0);
|
||||
|
||||
switch (enumskyblock) {
|
||||
case SKY:
|
||||
callback.accept(canSeeSky ? chunksection.c(i, j & 15, k) : 0);
|
||||
case BLOCK:
|
||||
callback.accept(chunksection.d(i, j & 15, k));
|
||||
default:
|
||||
callback.accept(enumskyblock.c);
|
||||
}
|
||||
} else {
|
||||
boolean hasLight = (enumskyblock == EnumSkyBlock.SKY && canSeeSky) || enumskyblock == EnumSkyBlock.BLOCK;
|
||||
callback.accept(hasLight ? enumskyblock.c : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canHasLight(BlockPosition blockposition) {
|
||||
int i = blockposition.getX() & 15;
|
||||
int j = blockposition.getY();
|
||||
int k = blockposition.getZ() & 15;
|
||||
|
||||
return j >= ((HeightMap) this.heightMap.get(HeightMap.Type.LIGHT_BLOCKING)).a(i, k);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Queues;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
@@ -40,9 +42,9 @@ public class Chunk implements IChunkAccess {
|
||||
|
||||
private static final Logger d = LogManager.getLogger();
|
||||
public static final ChunkSection a = null; public static final ChunkSection EMPTY_CHUNK_SECTION = Chunk.a; // Paper - OBFHELPER
|
||||
private final ChunkSection[] sections;
|
||||
private volatile ChunkSection[] sections; // Akarin - volatile
|
||||
private final BiomeBase[] f;
|
||||
private final boolean[] g;
|
||||
private final BitSet g; // Akarin
|
||||
private final Map<BlockPosition, NBTTagCompound> h;
|
||||
private boolean i;public boolean isLoaded() { return i; } // Paper - OBFHELPER
|
||||
public final World world;
|
||||
@@ -51,7 +53,7 @@ public class Chunk implements IChunkAccess {
|
||||
private static final Logger logger = LogManager.getLogger(); // Paper
|
||||
public final int locX;
|
||||
public final int locZ;
|
||||
private boolean l;
|
||||
private volatile boolean l; // Akarin
|
||||
private final ChunkConverter m;
|
||||
public final Map<BlockPosition, TileEntity> tileEntities;
|
||||
public final List<Entity>[] entitySlices; // Spigot
|
||||
@@ -63,7 +65,7 @@ public class Chunk implements IChunkAccess {
|
||||
private boolean u;
|
||||
private boolean v;public boolean hasEntities() { return v; } // Paper - OBFHELPER
|
||||
private long lastSaved;
|
||||
private boolean x; public boolean isModified() { return x; } // Paper - OBFHELPER
|
||||
private volatile boolean x; public boolean isModified() { return x; } // Paper - OBFHELPER // Akarin - volatile
|
||||
private int y;
|
||||
private long z;
|
||||
private int A;
|
||||
@@ -72,9 +74,10 @@ public class Chunk implements IChunkAccess {
|
||||
private int D;
|
||||
private final AtomicInteger E;
|
||||
private final ChunkCoordIntPair F;
|
||||
private final AkarinAsyncLighting lightHandler; // Akarin
|
||||
|
||||
// CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking
|
||||
private int neighbors = 0x1 << 12;
|
||||
private volatile int neighbors = 0x1 << 12; // Akarin - volatile
|
||||
public long chunkKey;
|
||||
// Paper start
|
||||
public final co.aikar.util.Counter<String> entityCounts = new co.aikar.util.Counter<>();
|
||||
@@ -137,7 +140,7 @@ public class Chunk implements IChunkAccess {
|
||||
|
||||
public Chunk(World world, int i, int j, BiomeBase[] abiomebase, ChunkConverter chunkconverter, TickList<Block> ticklist, TickList<FluidType> ticklist1, long k) {
|
||||
this.sections = new ChunkSection[16];
|
||||
this.g = new boolean[256];
|
||||
this.g = new BitSet(256); // Akarin
|
||||
this.h = Maps.newHashMap();
|
||||
this.heightMap = Maps.newEnumMap(HeightMap.Type.class);
|
||||
this.tileEntities = new TileEntityHashMap(); // Paper
|
||||
@@ -176,6 +179,7 @@ public class Chunk implements IChunkAccess {
|
||||
// CraftBukkit start
|
||||
this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
|
||||
this.chunkKey = ChunkCoordIntPair.a(this.locX, this.locZ);
|
||||
this.lightHandler = new AkarinAsyncLighting(world, sections, heightMap); // Akarin
|
||||
}
|
||||
|
||||
public org.bukkit.Chunk bukkitChunk;
|
||||
@@ -250,6 +254,7 @@ public class Chunk implements IChunkAccess {
|
||||
}
|
||||
|
||||
public void initLighting() {
|
||||
Runnable runnable = () -> { // Akarin
|
||||
int i = this.b();
|
||||
|
||||
this.y = Integer.MAX_VALUE;
|
||||
@@ -291,10 +296,19 @@ public class Chunk implements IChunkAccess {
|
||||
}
|
||||
|
||||
this.x = true;
|
||||
// Akarin start
|
||||
};
|
||||
if (AkarinGlobalConfig.enableAsyncLighting)
|
||||
MCUtil.scheduleAsyncTask(runnable);
|
||||
else
|
||||
runnable.run();
|
||||
// Akarin end
|
||||
}
|
||||
|
||||
private void c(int i, int j) {
|
||||
this.g[i + j * 16] = true;
|
||||
synchronized (this) { // Akarin - synchronized
|
||||
this.g.set(i + j * 16);
|
||||
} // Akarin - synchronized
|
||||
this.l = true;
|
||||
}
|
||||
|
||||
@@ -303,8 +317,15 @@ public class Chunk implements IChunkAccess {
|
||||
if (this.areNeighborsLoaded(1)) { // Paper
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
if (this.g[i + j * 16]) {
|
||||
this.g[i + j * 16] = false;
|
||||
// Akarin start
|
||||
int index = i + j * 16;
|
||||
boolean has;
|
||||
synchronized (this) {
|
||||
if (has = this.g.get(index))
|
||||
this.g.set(index);
|
||||
}
|
||||
if (has) {
|
||||
// Akarin end
|
||||
int k = this.a(HeightMap.Type.LIGHT_BLOCKING, i, j);
|
||||
int l = this.locX * 16 + i;
|
||||
int i1 = this.locZ * 16 + j;
|
||||
@@ -1513,6 +1534,12 @@ public class Chunk implements IChunkAccess {
|
||||
|
||||
// Paper start
|
||||
public void runOrQueueLightUpdate(Runnable runnable) {
|
||||
// Akarin start
|
||||
if (AkarinGlobalConfig.enableAsyncLighting) {
|
||||
MCUtil.scheduleAsyncTask(runnable);
|
||||
return;
|
||||
}
|
||||
// Akarin end
|
||||
if (this.world.paperConfig.queueLightUpdates) {
|
||||
lightingQueue.add(runnable);
|
||||
} else {
|
||||
|
||||
@@ -95,19 +95,19 @@ public class ChunkSection {
|
||||
return this.yPos;
|
||||
}
|
||||
|
||||
public void a(int i, int j, int k, int l) {
|
||||
public synchronized void a(int i, int j, int k, int l) { // Akarin - synchronized
|
||||
this.skyLight.a(i, j, k, l);
|
||||
}
|
||||
|
||||
public int c(int i, int j, int k) {
|
||||
public synchronized int c(int i, int j, int k) { // Akarin - synchronized
|
||||
return this.skyLight.a(i, j, k);
|
||||
}
|
||||
|
||||
public void b(int i, int j, int k, int l) {
|
||||
public synchronized void b(int i, int j, int k, int l) { // Akarin - synchronized
|
||||
this.emittedLight.a(i, j, k, l);
|
||||
}
|
||||
|
||||
public int d(int i, int j, int k) {
|
||||
public synchronized int d(int i, int j, int k) { // Akarin - synchronized
|
||||
return this.emittedLight.a(i, j, k);
|
||||
}
|
||||
|
||||
|
||||
159
src/main/java/net/minecraft/server/HeightMap.java
Normal file
159
src/main/java/net/minecraft/server/HeightMap.java
Normal file
@@ -0,0 +1,159 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class HeightMap {
|
||||
|
||||
private final DataBits a = new DataBits(9, 256);
|
||||
private final PredicateBlock<IBlockData> b;
|
||||
private final IChunkAccess c;
|
||||
|
||||
public HeightMap(IChunkAccess ichunkaccess, HeightMap.Type heightmap_type) {
|
||||
this.b = PredicateBlocks.a(PredicateBlocks.b(heightmap_type.a()));
|
||||
this.c = ichunkaccess;
|
||||
}
|
||||
|
||||
public void a() {
|
||||
int i = this.c.b() + 16;
|
||||
BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.r();
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
for (int k = 0; k < 16; ++k) {
|
||||
this.a(j, k, this.a(blockposition_pooledblockposition, j, k, this.b, i));
|
||||
}
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (blockposition_pooledblockposition != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
blockposition_pooledblockposition.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
blockposition_pooledblockposition.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(int i, int j, int k, @Nullable IBlockData iblockdata) {
|
||||
int l = this.a(i, k);
|
||||
|
||||
if (j <= l - 2) {
|
||||
return false;
|
||||
} else {
|
||||
if (this.b.test(iblockdata, this.c, new BlockPosition(i, j, k))) {
|
||||
if (j >= l) {
|
||||
this.a(i, k, j + 1);
|
||||
return true;
|
||||
}
|
||||
} else if (l - 1 == j) {
|
||||
this.a(i, k, this.a((BlockPosition.MutableBlockPosition) null, i, k, this.b, j));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private int a(@Nullable BlockPosition.MutableBlockPosition blockposition_mutableblockposition, int i, int j, PredicateBlock<IBlockData> predicateblock, int k) {
|
||||
if (blockposition_mutableblockposition == null) {
|
||||
blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
|
||||
}
|
||||
|
||||
for (int l = k - 1; l >= 0; --l) {
|
||||
blockposition_mutableblockposition.c(i, l, j);
|
||||
IBlockData iblockdata = this.c.getType(blockposition_mutableblockposition);
|
||||
|
||||
if (predicateblock.test(iblockdata, this.c, blockposition_mutableblockposition)) {
|
||||
return l + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int a(int i, int j) {
|
||||
return this.a(b(i, j));
|
||||
}
|
||||
|
||||
private synchronized int a(int i) { // Akarin - synchronized
|
||||
return this.a.a(i);
|
||||
}
|
||||
|
||||
private synchronized void a(int i, int j, int k) { // Akarin - synchronized
|
||||
this.a.a(b(i, j), k);
|
||||
}
|
||||
|
||||
public void a(long[] along) {
|
||||
System.arraycopy(along, 0, this.a.a(), 0, along.length);
|
||||
}
|
||||
|
||||
public synchronized long[] b() { // Akarin - synchronized
|
||||
return this.a.a();
|
||||
}
|
||||
|
||||
private static int b(int i, int j) {
|
||||
return i + j * 16;
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
|
||||
WORLD_SURFACE_WG("WORLD_SURFACE_WG", HeightMap.Use.WORLDGEN, new PredicateBlock[] { PredicateBlockType.a(Blocks.AIR)}), OCEAN_FLOOR_WG("OCEAN_FLOOR_WG", HeightMap.Use.WORLDGEN, new PredicateBlock[] { PredicateBlockType.a(Blocks.AIR), PredicateBlockLiquid.a()}), LIGHT_BLOCKING("LIGHT_BLOCKING", HeightMap.Use.LIVE_WORLD, new PredicateBlock[] { PredicateBlockType.a(Blocks.AIR), PredicateBlockLightTransmission.a()}), MOTION_BLOCKING("MOTION_BLOCKING", HeightMap.Use.LIVE_WORLD, new PredicateBlock[] { PredicateBlockType.a(Blocks.AIR), PredicateBlockNotSolidOrLiquid.a()}), MOTION_BLOCKING_NO_LEAVES("MOTION_BLOCKING_NO_LEAVES", HeightMap.Use.LIVE_WORLD, new PredicateBlock[] { PredicateBlockType.a(Blocks.AIR), PredicateBlockTag.a(TagsBlock.LEAVES), PredicateBlockNotSolidOrLiquid.a()}), OCEAN_FLOOR("OCEAN_FLOOR", HeightMap.Use.LIVE_WORLD, new PredicateBlock[] { PredicateBlockType.a(Blocks.AIR), PredicateBlockSolid.a()}), WORLD_SURFACE("WORLD_SURFACE", HeightMap.Use.LIVE_WORLD, new PredicateBlock[] { PredicateBlockType.a(Blocks.AIR)});
|
||||
|
||||
private final PredicateBlock<IBlockData>[] h;
|
||||
private final String i;
|
||||
private final HeightMap.Use j;
|
||||
private static final Map<String, HeightMap.Type> k = (Map<String, HeightMap.Type>) SystemUtils.a(Maps.<String, HeightMap.Type>newHashMap(), (hashmap) -> { // Akarin - fixes decompile error
|
||||
HeightMap.Type[] aheightmap_type = values();
|
||||
int i = aheightmap_type.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
HeightMap.Type heightmap_type = aheightmap_type[j];
|
||||
|
||||
hashmap.put(heightmap_type.i, heightmap_type);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
private Type(String s, HeightMap.Use heightmap_use, PredicateBlock... apredicateblock) {
|
||||
this.i = s;
|
||||
this.h = apredicateblock;
|
||||
this.j = heightmap_use;
|
||||
}
|
||||
|
||||
public PredicateBlock<IBlockData>[] a() {
|
||||
return this.h;
|
||||
}
|
||||
|
||||
public String b() {
|
||||
return this.i;
|
||||
}
|
||||
|
||||
public HeightMap.Use c() {
|
||||
return this.j;
|
||||
}
|
||||
|
||||
public static HeightMap.Type a(String s) {
|
||||
return (HeightMap.Type) HeightMap.Type.k.get(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Use {
|
||||
|
||||
WORLDGEN, LIVE_WORLD;
|
||||
|
||||
private Use() {}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import co.aikar.timings.Timing;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectCollection;
|
||||
|
||||
@@ -19,7 +21,7 @@ class PaperLightingQueue {
|
||||
|
||||
START:
|
||||
for (World world : MinecraftServer.getServer().getWorlds()) {
|
||||
if (!world.paperConfig.queueLightUpdates) {
|
||||
if (!world.paperConfig.queueLightUpdates || AkarinGlobalConfig.enableAsyncLighting) { // Akarin
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ class PaperLightingQueue {
|
||||
* Flushes lighting updates to unload the chunk
|
||||
*/
|
||||
void processUnload() {
|
||||
if (!chunk.world.paperConfig.queueLightUpdates) {
|
||||
if (!chunk.world.paperConfig.queueLightUpdates || AkarinGlobalConfig.enableAsyncLighting) { // Akarin
|
||||
return;
|
||||
}
|
||||
processQueue(0, 0); // No timeout
|
||||
|
||||
@@ -446,11 +446,13 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
||||
if (spigotConfig.randomLightUpdates && !this.players.isEmpty()) { // Spigot
|
||||
int i = this.random.nextInt(this.players.size());
|
||||
EntityHuman entityhuman = (EntityHuman) this.players.get(i);
|
||||
MCUtil.scheduleAsyncTask(() -> { // Akarin
|
||||
int j = MathHelper.floor(entityhuman.locX) + this.random.nextInt(11) - 5;
|
||||
int k = MathHelper.floor(entityhuman.locY) + this.random.nextInt(11) - 5;
|
||||
int l = MathHelper.floor(entityhuman.locZ) + this.random.nextInt(11) - 5;
|
||||
|
||||
this.r(new BlockPosition(j, k, l));
|
||||
}); // Akarin
|
||||
}
|
||||
|
||||
//this.methodProfiler.exit(); // Akarin - remove caller
|
||||
@@ -472,14 +474,14 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
||||
|
||||
//this.methodProfiler.enter(* // Akarin - remove caller
|
||||
|
||||
for (Iterator iterator1 = this.manager.b(); iterator1.hasNext(); this.methodProfiler.exit()) {
|
||||
for (Iterator iterator1 = this.manager.b(); iterator1.hasNext(); /*this.methodProfiler.exit()*/) { // Akarin - remove caller
|
||||
//this.methodProfiler.enter(* // Akarin - remove caller
|
||||
Chunk chunk = (Chunk) iterator1.next();
|
||||
int j = chunk.locX * 16;
|
||||
int k = chunk.locZ * 16;
|
||||
|
||||
//this.methodProfiler.exitEnter("checkNextLight"); // Akarin - remove caller
|
||||
chunk.x();
|
||||
MCUtil.scheduleAsyncTask(chunk::x); // Akarin
|
||||
//this.methodProfiler.exitEnter("tickChunk"); // Akarin - remove caller
|
||||
chunk.d(false);
|
||||
if ( !chunk.areNeighborsLoaded( 1 ) ) continue; // Spigot
|
||||
|
||||
@@ -540,7 +540,7 @@ public class CraftScheduler implements BukkitScheduler {
|
||||
task.setNext(null);
|
||||
}
|
||||
this.head = lastTask;
|
||||
if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(Unsafe); // Paper
|
||||
if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTimingUnsafe(); // Paper
|
||||
}
|
||||
|
||||
private boolean isReady(final int currentTick) {
|
||||
|
||||
Reference in New Issue
Block a user