Async lighting updates

This commit is contained in:
Sotr
2019-03-26 17:03:26 +08:00
parent 8c841e73fd
commit 19a9e9ebb3
8 changed files with 267 additions and 18 deletions

View 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);
}
}

View File

@@ -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 {

View File

@@ -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);
}

View 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() {}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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) {