[Major] Finally correct async lighting w/ Add watchcat gc feature
This commit is contained in:
@@ -8,8 +8,6 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.entity.Minecart;
|
||||
|
||||
import com.google.common.collect.Queues;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
@@ -17,6 +15,7 @@ import co.aikar.timings.Timing;
|
||||
import co.aikar.timings.Timings;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public abstract class Akari {
|
||||
/**
|
||||
* A common logger used by mixin classes
|
||||
@@ -31,7 +30,7 @@ public abstract class Akari {
|
||||
/**
|
||||
* A common thread pool factory
|
||||
*/
|
||||
public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Schedule Thread - %1$d").build();
|
||||
public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Schedule Thread").build();
|
||||
|
||||
/**
|
||||
* Main thread callback tasks
|
||||
@@ -43,7 +42,7 @@ public abstract class Akari {
|
||||
*/
|
||||
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(Akari.STAGE_FACTORY));
|
||||
|
||||
public static volatile boolean mayMock;
|
||||
public static boolean mayEnableAsyncCathcer;
|
||||
|
||||
public static boolean isPrimaryThread() {
|
||||
return Thread.currentThread().equals(MinecraftServer.getServer().primaryThread);
|
||||
|
||||
@@ -189,6 +189,11 @@ public class AkarinGlobalConfig {
|
||||
asyncLightingThreads = getInt("core.async-lighting.executor-threads", 2);
|
||||
}
|
||||
|
||||
public static boolean asyncLightingWorkStealing;
|
||||
private static void asyncLightingWorkStealing() {
|
||||
asyncLightingWorkStealing = getBoolean("core.async-lighting.use-work-stealing", false);
|
||||
}
|
||||
|
||||
public static boolean enableMockPlugin;
|
||||
private static void enableMockPlugin() {
|
||||
enableMockPlugin = getBoolean("core.thread-safe.enable-mock-plugins", false);
|
||||
@@ -209,13 +214,13 @@ public class AkarinGlobalConfig {
|
||||
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
|
||||
}
|
||||
|
||||
public static boolean asyncLightingWorkStealing;
|
||||
private static void asyncLightingWorkStealing() {
|
||||
asyncLightingWorkStealing = getBoolean("core.async-lighting.use-work-stealing", false);
|
||||
}
|
||||
|
||||
public static boolean allowSpawnerModify;
|
||||
private static void allowSpawnerModify() {
|
||||
allowSpawnerModify = getBoolean("alternative.allow-spawner-modify", true);
|
||||
}
|
||||
|
||||
public static boolean noResponseDoGC;
|
||||
private static void noResponseDoGC() {
|
||||
noResponseDoGC = getBoolean("alternative.gc-before-stuck-restart", true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
@Mixin(value = WatchdogThread.class, remap = false)
|
||||
@@ -74,6 +75,11 @@ public class Watchcat extends Thread {
|
||||
}
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
|
||||
if (AkarinGlobalConfig.noResponseDoGC) {
|
||||
log.log(Level.SEVERE, "Attempting to garbage collect, may takes a few seconds");
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
}
|
||||
if (restart) RestartCommand.restart();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class MixinAsyncCatcher {
|
||||
|
||||
@Overwrite
|
||||
public static void catchOp(String reason) {
|
||||
if (AkarinGlobalConfig.enableAsyncCatcher && enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
|
||||
if (AkarinGlobalConfig.enableAsyncCatcher && Akari.mayEnableAsyncCathcer && enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
|
||||
if (AkarinGlobalConfig.throwOnAsyncCaught) {
|
||||
throw new IllegalStateException("Asynchronous " + reason + "!");
|
||||
} else {
|
||||
|
||||
@@ -138,6 +138,7 @@ public abstract class MixinMinecraftServer {
|
||||
}
|
||||
}
|
||||
Akari.silentTiming = true; // Disable timings
|
||||
Akari.mayEnableAsyncCathcer = false;
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
// Never tick one world concurrently!
|
||||
// TODO better treat world index
|
||||
@@ -157,6 +158,7 @@ public abstract class MixinMinecraftServer {
|
||||
}
|
||||
|
||||
Akari.STAGE_TICK.take();
|
||||
Akari.mayEnableAsyncCathcer = true;
|
||||
Akari.silentTiming = false; // Enable timings
|
||||
Akari.worldTiming.stopTiming();
|
||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
||||
|
||||
@@ -36,7 +36,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.akarin.api.Akari;
|
||||
import io.akarin.api.mixin.IMixinChunk;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.MCUtil;
|
||||
@@ -99,8 +101,10 @@ public abstract class MixinChunk implements IMixinChunk {
|
||||
|
||||
@Inject(method = "addEntities", at = @At("RETURN"))
|
||||
public void onLoadReturn(CallbackInfo ci) {
|
||||
BlockPosition origin = new BlockPosition(locX, 0, locZ);
|
||||
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), locX, locZ);
|
||||
BlockPosition shift = origin.shift(direction);
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
|
||||
if (neighbor != null) {
|
||||
int neighborIndex = directionToIndex(direction);
|
||||
int oppositeNeighborIndex = directionToIndex(direction.opposite());
|
||||
@@ -112,8 +116,10 @@ public abstract class MixinChunk implements IMixinChunk {
|
||||
|
||||
@Inject(method = "removeEntities", at = @At("RETURN"))
|
||||
public void onUnload(CallbackInfo ci) {
|
||||
BlockPosition origin = new BlockPosition(locX, 0, locZ);
|
||||
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), locX, locZ);
|
||||
BlockPosition shift = origin.shift(direction);
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
|
||||
if (neighbor != null) {
|
||||
int neighborIndex = directionToIndex(direction);
|
||||
int oppositeNeighborIndex = directionToIndex(direction.opposite());
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import io.akarin.api.Akari;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkProviderServer;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class MixinCraftWorld {
|
||||
opcode = Opcodes.INVOKEINTERFACE
|
||||
))
|
||||
public boolean regenChunk(Set<Long> set, Object chunkHash) {
|
||||
world.getChunkProviderServer().unload(world.getChunkProviderServer().chunks.get(chunkHash));
|
||||
world.getChunkProviderServer().chunks.get(chunkHash).setShouldUnload(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public abstract class MixinChunk implements IMixinChunk {
|
||||
@Inject(method = "b(Z)V", at = @At("HEAD"), cancellable = true)
|
||||
private void onTickHead(boolean skipRecheckGaps, CallbackInfo ci) {
|
||||
final List<Chunk> neighbors = this.getSurroundingChunks();
|
||||
if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // PAIL: isGapLightingUpdated - hasSkyLight
|
||||
if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // PAIL: hasSkyLight
|
||||
this.lightExecutorService.execute(() -> {
|
||||
this.recheckGapsAsync(neighbors);
|
||||
});
|
||||
@@ -274,13 +274,13 @@ public abstract class MixinChunk implements IMixinChunk {
|
||||
BlockPosition blockpos = new BlockPosition(this.locX << 4, 0, this.locZ << 4);
|
||||
|
||||
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
|
||||
label44:
|
||||
reCheckLight:
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
if (!this.checkLightAsync(i, j, neighbors)) {
|
||||
this.isLightPopulated = false;
|
||||
break label44;
|
||||
break reCheckLight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.util.concurrent.Executors;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import io.akarin.api.Akari;
|
||||
@@ -55,7 +54,7 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
|
||||
private static final short XZ_MASK = 0xF;
|
||||
private static final short Y_SHORT_MASK = 0xFF;
|
||||
|
||||
private final ExecutorService lightExecutorService = getExecutorService();;
|
||||
private final ExecutorService lightExecutorService = getExecutorService();
|
||||
|
||||
private ExecutorService getExecutorService() {
|
||||
return AkarinGlobalConfig.asyncLightingWorkStealing ?
|
||||
@@ -130,10 +129,10 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
i = 0;
|
||||
}
|
||||
|
||||
|
||||
while (i < j) {
|
||||
int i5 = this.J[i++]; // PAIL: lightUpdateBlockList
|
||||
int j5 = (i5 & 63) - 32 + x;
|
||||
@@ -151,7 +150,7 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
|
||||
int l6 = Math.abs(k5 - y);
|
||||
int i7 = Math.abs(l5 - z);
|
||||
boolean flag = j < this.J.length - 6; // PAIL: lightUpdateBlockList
|
||||
|
||||
|
||||
if (k6 + l6 + i7 < 17 && flag) {
|
||||
// Sponge start - use thread safe method getLightForAsync
|
||||
if (this.getLightForAsync(lightType, blockpos1.west(), currentChunk, neighbors) < j6) {
|
||||
@@ -204,22 +203,22 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
|
||||
if (currentChunk == null) {
|
||||
currentChunk = MCUtil.getLoadedChunkWithoutMarkingActive(chunkProvider, pos.getX() >> 4, pos.getZ() >> 4);
|
||||
}
|
||||
|
||||
|
||||
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
|
||||
if (currentChunk == null || currentChunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final short shortPos = this.blockPosToShort(pos);
|
||||
if (spongeChunk.getQueuedLightingUpdates(lightType).contains(shortPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final Chunk chunk = currentChunk;
|
||||
spongeChunk.getQueuedLightingUpdates(lightType).add(shortPos);
|
||||
spongeChunk.getPendingLightUpdates().incrementAndGet();
|
||||
spongeChunk.setLightUpdateTime(chunk.getWorld().getTime());
|
||||
|
||||
|
||||
List<Chunk> neighbors = spongeChunk.getNeighbors();
|
||||
// add diagonal chunks
|
||||
Chunk southEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(2);
|
||||
@@ -281,7 +280,7 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private int getLightForAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
if (pos.getY() < 0) {
|
||||
pos = new BlockPosition(pos.getX(), 0, pos.getZ());
|
||||
@@ -289,10 +288,10 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
|
||||
if (!pos.isValidLocation()) {
|
||||
return lightType.c; // PAIL: defaultLightValue
|
||||
}
|
||||
|
||||
|
||||
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk == null || chunk.isUnloading()) {
|
||||
return 0; // Akarin - fixes cave light - defaultLightValue -> 0
|
||||
return lightType.c; // PAIL: defaultLightValue
|
||||
}
|
||||
|
||||
return chunk.getBrightness(lightType, pos);
|
||||
@@ -347,11 +346,11 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
|
||||
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk != null && !chunk.isUnloading()) {
|
||||
chunk.a(type, pos, lightValue); // PAIL: setLightFor
|
||||
// this.notifyLightSet(pos); - client side
|
||||
// this.notifyLightSet(pos); // client side
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private short blockPosToShort(BlockPosition pos) {
|
||||
short serialized = (short) setNibble(0, pos.getX() & XZ_MASK, 0, NUM_XZ_BITS);
|
||||
serialized = (short) setNibble(serialized, pos.getY() & Y_SHORT_MASK, 1, NUM_SHORT_Y_BITS);
|
||||
|
||||
@@ -42,9 +42,9 @@ public class Chunk {
|
||||
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; private boolean isTicked() { return r; }; // Paper - OBFHELPER
|
||||
private volatile boolean done; // Akarin - volatile
|
||||
private volatile boolean lit; // Akarin - volatile
|
||||
private volatile boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER // Akarin - volatile
|
||||
private boolean s;
|
||||
private boolean t;
|
||||
private long lastSaved;
|
||||
@@ -1143,7 +1143,7 @@ public class Chunk {
|
||||
* For now at least we will simply send all chunks, in accordance with pre 1.7 behaviour.
|
||||
*/
|
||||
// Paper Start
|
||||
// if randomLightUpdates are enabled, we should always return true, otherwise chunks may never send
|
||||
// if randomLightUpdates are disabled, we should always return true, otherwise chunks may never send
|
||||
// to the client due to not being lit, otherwise retain standard behavior and only send properly lit chunks.
|
||||
return !this.world.spigotConfig.randomLightUpdates || (this.isTicked() && this.done && this.lit);
|
||||
// Paper End
|
||||
|
||||
Reference in New Issue
Block a user