diff --git a/sources/src/main/java/io/akarin/api/Akari.java b/sources/src/main/java/io/akarin/api/Akari.java index cc32887e4..e5b10c9fd 100644 --- a/sources/src/main/java/io/akarin/api/Akari.java +++ b/sources/src/main/java/io/akarin/api/Akari.java @@ -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); diff --git a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java index 87ab03c09..a4deea0f8 100644 --- a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java +++ b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java @@ -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); + } } diff --git a/sources/src/main/java/io/akarin/server/mixin/bootstrap/Watchcat.java b/sources/src/main/java/io/akarin/server/mixin/bootstrap/Watchcat.java index c03909ecf..4deb6dbd4 100644 --- a/sources/src/main/java/io/akarin/server/mixin/bootstrap/Watchcat.java +++ b/sources/src/main/java/io/akarin/server/mixin/bootstrap/Watchcat.java @@ -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; } diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinAsyncCatcher.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinAsyncCatcher.java index 031497649..ba9d4a75f 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinAsyncCatcher.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinAsyncCatcher.java @@ -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 { diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java index d5d7a21b6..ee050738f 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java @@ -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) { diff --git a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunk.java b/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunk.java index e340679d6..c5180815f 100644 --- a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunk.java +++ b/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunk.java @@ -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()); diff --git a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java b/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java index 56f6e5e12..5d61b69f3 100644 --- a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java +++ b/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java @@ -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; diff --git a/sources/src/main/java/io/akarin/server/mixin/cps/MixinCraftWorld.java b/sources/src/main/java/io/akarin/server/mixin/cps/MixinCraftWorld.java index 015d3696e..39ce2aafc 100644 --- a/sources/src/main/java/io/akarin/server/mixin/cps/MixinCraftWorld.java +++ b/sources/src/main/java/io/akarin/server/mixin/cps/MixinCraftWorld.java @@ -30,7 +30,7 @@ public class MixinCraftWorld { opcode = Opcodes.INVOKEINTERFACE )) public boolean regenChunk(Set set, Object chunkHash) { - world.getChunkProviderServer().unload(world.getChunkProviderServer().chunks.get(chunkHash)); + world.getChunkProviderServer().chunks.get(chunkHash).setShouldUnload(false); return true; } } diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java index 76f586368..3913be053 100644 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java +++ b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java @@ -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 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; } } } diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorldServer.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorldServer.java index fdccd4036..b756a01bd 100644 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorldServer.java +++ b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorldServer.java @@ -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 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 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); diff --git a/sources/src/main/java/net/minecraft/server/Chunk.java b/sources/src/main/java/net/minecraft/server/Chunk.java index 02addf33d..fca977485 100644 --- a/sources/src/main/java/net/minecraft/server/Chunk.java +++ b/sources/src/main/java/net/minecraft/server/Chunk.java @@ -42,9 +42,9 @@ public class Chunk { public final Map tileEntities; public final List[] 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