[Major] Finally correct async lighting w/ Add watchcat gc feature

This commit is contained in:
Sotr
2018-06-13 00:31:08 +08:00
parent c11a88d1ba
commit 8a7ebe839c
11 changed files with 51 additions and 35 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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