diff --git a/sources/pom.xml b/sources/pom.xml index 8144a330a..c770e3120 100644 --- a/sources/pom.xml +++ b/sources/pom.xml @@ -131,12 +131,7 @@ org.spongepowered mixin - 0.7.8-SNAPSHOT - - - me.nallar.whocalled - WhoCalled - 1.1 + 0.7.10-SNAPSHOT diff --git a/sources/src/main/java/io/akarin/api/internal/Akari.java b/sources/src/main/java/io/akarin/api/internal/Akari.java index 6a076d6bc..245b93523 100644 --- a/sources/src/main/java/io/akarin/api/internal/Akari.java +++ b/sources/src/main/java/io/akarin/api/internal/Akari.java @@ -13,6 +13,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import co.aikar.timings.Timing; import co.aikar.timings.Timings; +import io.akarin.server.core.AkarinGlobalConfig; import net.minecraft.server.MinecraftServer; @SuppressWarnings("restriction") @@ -22,30 +23,40 @@ public abstract class Akari { */ public final static Logger logger = LogManager.getLogger("Akarin"); - /** - * Temporarily disable desync timings error, moreover it's worthless to trace async operation - */ - public static volatile boolean silentTiming; - /** * A common thread pool factory */ - public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Schedule Thread").build(); + public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Parallel Registry Thread - %1$d").build(); /** * Main thread callback tasks */ public static final Queue callbackQueue = Queues.newConcurrentLinkedQueue(); + public static class AssignableThread extends Thread { + public AssignableThread(Runnable run) { + super(run); + } + } + + private static class AssignableFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable run) { + Thread thread = new AssignableThread(run); + thread.setName("Akarin Parallel Schedule Thread"); + thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair + return thread; + } + } + /** * A common tick pool */ - public static final ExecutorCompletionService STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(Akari.STAGE_FACTORY)); - - public static boolean mayEnableAsyncCathcer; + public static final ExecutorCompletionService STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(new AssignableFactory())); public static boolean isPrimaryThread() { - return Thread.currentThread().equals(MinecraftServer.getServer().primaryThread); + Thread current = Thread.currentThread(); + return current == MinecraftServer.getServer().primaryThread || current instanceof AssignableThread; } public static final String EMPTY_STRING = ""; @@ -69,7 +80,9 @@ public abstract class Akari { /* * Timings */ - public final static Timing worldTiming = getTiming("Akarin - World"); + public final static Timing worldTiming = getTiming("Akarin - Full World Tick"); + + public final static Timing entityCallbackTiming = getTiming("Akarin - Entity Callback"); public final static Timing callbackTiming = getTiming("Akarin - Callback"); 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 64b93a1e0..ea778b1c5 100644 --- a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java +++ b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java @@ -195,21 +195,6 @@ public class AkarinGlobalConfig { 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); - } - - public static List mockPackageList; - private static void mockPluginList() { - mockPackageList = getList("core.thread-safe.mock-package-name-contains", Lists.newArrayList("me.konsolas.aac")); - } - - public static boolean enableAsyncCatcher; - private static void enableAsyncCatcher() { - enableAsyncCatcher = getBoolean("core.thread-safe.async-catcher.enable", false); - } - public static boolean throwOnAsyncCaught; private static void throwOnAsyncCaught() { throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true); diff --git a/sources/src/main/java/io/akarin/server/core/ChannelAdapter.java b/sources/src/main/java/io/akarin/server/core/ChannelAdapter.java deleted file mode 100644 index b27c71309..000000000 --- a/sources/src/main/java/io/akarin/server/core/ChannelAdapter.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.akarin.server.core; - -import java.util.List; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.handler.timeout.ReadTimeoutHandler; -import net.minecraft.server.EnumProtocolDirection; -import net.minecraft.server.HandshakeListener; -import net.minecraft.server.LegacyPingHandler; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.NetworkManager; -import net.minecraft.server.PacketDecoder; -import net.minecraft.server.PacketEncoder; -import net.minecraft.server.PacketPrepender; -import net.minecraft.server.PacketSplitter; - -public class ChannelAdapter extends ChannelInitializer { - private final List managers; - - public ChannelAdapter(List list) { - managers = list; - } - - public static ChannelAdapter create(List managers) { - return new ChannelAdapter(managers); - } - - @Override - protected void initChannel(Channel channel) { - try { - channel.config().setOption(ChannelOption.TCP_NODELAY, true); - } catch (ChannelException ex) { - ; - } - channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)) - .addLast("legacy_query", new LegacyPingHandler(MinecraftServer.getServer().getServerConnection())) - .addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND)) - .addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND)); - - NetworkManager manager = new NetworkManager(EnumProtocolDirection.SERVERBOUND); - managers.add(manager); - - channel.pipeline().addLast("packet_handler", manager); - manager.setPacketListener(new HandshakeListener(MinecraftServer.getServer(), manager)); - } -} diff --git a/sources/src/main/java/io/akarin/server/core/NetworkCloseHandler.java b/sources/src/main/java/io/akarin/server/core/NetworkCloseHandler.java deleted file mode 100644 index 776123493..000000000 --- a/sources/src/main/java/io/akarin/server/core/NetworkCloseHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.akarin.server.core; - -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.GenericFutureListener; -import net.minecraft.server.ChatComponentText; -import net.minecraft.server.NetworkManager; - -public class NetworkCloseHandler implements GenericFutureListener> { - private final NetworkManager manager; - private final ChatComponentText message; - - public NetworkCloseHandler(NetworkManager instance, ChatComponentText text) { - manager = instance; - message = text; - } - - @Override - public void operationComplete(Future future) throws Exception { - manager.close(message); - } -} 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 f23c59ce0..bf329d5a1 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 @@ -7,7 +7,6 @@ import org.spongepowered.asm.mixin.Shadow; import io.akarin.api.internal.Akari; import io.akarin.server.core.AkarinGlobalConfig; -import net.minecraft.server.MinecraftServer; @Mixin(value = AsyncCatcher.class, remap = false) public abstract class MixinAsyncCatcher { @@ -15,7 +14,9 @@ public abstract class MixinAsyncCatcher { @Overwrite public static void catchOp(String reason) { - if (AkarinGlobalConfig.enableAsyncCatcher && Akari.mayEnableAsyncCathcer && enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { + if (enabled) { + if (Akari.isPrimaryThread()) return; + if (AkarinGlobalConfig.throwOnAsyncCaught) { throw new IllegalStateException("Asynchronous " + reason + "!"); } else { diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinCraftServer.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinCraftServer.java index 553b2d5fd..5a4d10db9 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinCraftServer.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinCraftServer.java @@ -9,7 +9,6 @@ import org.spongepowered.asm.mixin.Shadow; import io.akarin.api.internal.Akari; import io.akarin.server.core.AkarinGlobalConfig; -import me.nallar.whocalled.WhoCalled; import net.minecraft.server.MinecraftServer; @Mixin(value = CraftServer.class, remap = false) @@ -31,15 +30,6 @@ public abstract class MixinCraftServer { @Overwrite public boolean isPrimaryThread() { - if (AkarinGlobalConfig.enableMockPlugin && !AkarinGlobalConfig.mockPackageList.isEmpty()) { - // Mock forcely main thread plugins - String callerPackage = WhoCalled.$.getCallingClass().getPackage().getName(); - if (callerPackage.startsWith("net.minecraft") || callerPackage.startsWith("org.bukkit") || - callerPackage.startsWith("co.aikar") || callerPackage.startsWith("io.akarin")) return Thread.currentThread().equals(console.primaryThread); - for (String contains : AkarinGlobalConfig.mockPackageList) { - if (callerPackage.contains(contains)) return true; - } - } - return Thread.currentThread().equals(console.primaryThread); + return Akari.isPrimaryThread(); } } diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinMCUtil.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinMCUtil.java index cfbf66066..1e04036dc 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinMCUtil.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinMCUtil.java @@ -1,16 +1,38 @@ package io.akarin.server.mixin.core; +import java.util.concurrent.ExecutionException; import java.util.function.Supplier; +import org.bukkit.craftbukkit.util.Waitable; +import org.spigotmc.AsyncCatcher; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; +import io.akarin.api.internal.Akari; import net.minecraft.server.MCUtil; +import net.minecraft.server.MinecraftServer; @Mixin(value = MCUtil.class, remap = false) public abstract class MixinMCUtil { @Overwrite public static T ensureMain(String reason, Supplier run) { + if (AsyncCatcher.enabled && !Akari.isPrimaryThread()) { + new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace(); + Waitable wait = new Waitable() { + @Override + protected T evaluate() { + return run.get(); + } + }; + MinecraftServer.getServer().processQueue.add(wait); + try { + return wait.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + return null; + } + return run.get(); } } 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 87d1ea87f..3ed40e4a8 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 @@ -58,6 +58,11 @@ public abstract class MixinMinecraftServer { } + @Overwrite + public boolean isMainThread() { + return Akari.isPrimaryThread(); + } + /* * Forcely disable snooper */ @@ -147,8 +152,6 @@ public abstract class MixinMinecraftServer { worlds.get(i).timings.doTick.startTiming(); } } - Akari.silentTiming = true; // Disable timings - Akari.mayEnableAsyncCathcer = false; Akari.STAGE_TICK.submit(() -> { // Never tick one world concurrently! for (int i = 1; i <= worlds.size(); ++i) { @@ -166,9 +169,10 @@ public abstract class MixinMinecraftServer { } } + Akari.entityCallbackTiming.startTiming(); Akari.STAGE_TICK.take(); - Akari.mayEnableAsyncCathcer = true; - Akari.silentTiming = false; // Enable timings + Akari.entityCallbackTiming.stopTiming(); + Akari.worldTiming.stopTiming(); if (AkarinGlobalConfig.legacyWorldTimings) { for (int i = 0; i < worlds.size(); ++i) { diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java index a9d847432..cb834a8ed 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java @@ -36,12 +36,12 @@ public abstract class MixinTimingHandler { @SuppressWarnings({ "rawtypes", "unchecked" }) @Inject(method = "startTiming", at = @At("HEAD"), cancellable = true) public void onStartTiming(CallbackInfoReturnable ci) { - if (Akari.silentTiming || !Akari.isPrimaryThread()) ci.setReturnValue(this); // Avoid modify any field + if (!Akari.isPrimaryThread()) ci.setReturnValue(this); // Avoid modify any field } @Overwrite public void stopTimingIfSync() { - if (Akari.isPrimaryThread()) { // Use non-mock method + if (Akari.isPrimaryThread()) { stopTiming(true); // Avoid twice thread check } } @@ -52,12 +52,12 @@ public abstract class MixinTimingHandler { } public void stopTiming(boolean alreadySync) { - if (!enabled || Akari.silentTiming) return; + if (!enabled) return; if (!alreadySync && !Akari.isPrimaryThread()) { if (AkarinGlobalConfig.silentAsyncTimings) return; Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name); - new Throwable().printStackTrace(); + Thread.dumpStack(); } // Main thread ensured diff --git a/sources/src/main/java/io/akarin/server/mixin/nsc/NonblockingServerConnection.java b/sources/src/main/java/io/akarin/server/mixin/nsc/NonblockingServerConnection.java index 6d4948f1d..6cc0e7c5a 100644 --- a/sources/src/main/java/io/akarin/server/mixin/nsc/NonblockingServerConnection.java +++ b/sources/src/main/java/io/akarin/server/mixin/nsc/NonblockingServerConnection.java @@ -20,20 +20,31 @@ import com.google.common.collect.Lists; import io.akarin.api.internal.LocalAddress; import io.akarin.server.core.AkarinGlobalConfig; -import io.akarin.server.core.ChannelAdapter; -import io.akarin.server.core.NetworkCloseHandler; import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.ServerChannel; import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import net.minecraft.server.ChatComponentText; +import net.minecraft.server.EnumProtocolDirection; +import net.minecraft.server.HandshakeListener; +import net.minecraft.server.LegacyPingHandler; import net.minecraft.server.MinecraftServer; import net.minecraft.server.NetworkManager; +import net.minecraft.server.PacketDecoder; +import net.minecraft.server.PacketEncoder; import net.minecraft.server.PacketPlayOutKickDisconnect; +import net.minecraft.server.PacketPrepender; +import net.minecraft.server.PacketSplitter; import net.minecraft.server.ServerConnection; @Mixin(value = ServerConnection.class, remap = false) @@ -76,7 +87,26 @@ public abstract class NonblockingServerConnection { logger.info("Using nio channel type"); } - ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(ChannelAdapter.create(networkManagers)).group(loopGroup); + ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + try { + channel.config().setOption(ChannelOption.TCP_NODELAY, true); + } catch (ChannelException ex) { + ; + } + channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)) + .addLast("legacy_query", new LegacyPingHandler(MinecraftServer.getServer().getServerConnection())) + .addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND)) + .addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND)); + + NetworkManager manager = new NetworkManager(EnumProtocolDirection.SERVERBOUND); + networkManagers.add(manager); + + channel.pipeline().addLast("packet_handler", manager); + manager.setPacketListener(new HandshakeListener(MinecraftServer.getServer(), manager)); + } + }).group(loopGroup); synchronized (endPoints) { data.addAll(Lists.transform(AkarinGlobalConfig.extraAddress, s -> { String[] info = s.split(":"); @@ -112,9 +142,14 @@ public abstract class NonblockingServerConnection { manager.a(); // PAIL: NetworkManager::processReceivedPackets } catch (Exception ex) { logger.warn("Failed to handle packet for {}", manager.getSocketAddress(), ex); - final ChatComponentText kick = new ChatComponentText("Internal server error"); + final ChatComponentText message = new ChatComponentText("Internal server error"); - manager.sendPacket(new PacketPlayOutKickDisconnect(kick), new NetworkCloseHandler(manager, kick), new GenericFutureListener[0]); + manager.sendPacket(new PacketPlayOutKickDisconnect(message), new GenericFutureListener>() { + @Override + public void operationComplete(Future future) throws Exception { + manager.close(message); + } + }, new GenericFutureListener[0]); manager.stopReading(); } } diff --git a/sources/src/main/resources/mixins.akarin.core.json b/sources/src/main/resources/mixins.akarin.core.json index 26fd6af58..fd4432cc5 100644 --- a/sources/src/main/resources/mixins.akarin.core.json +++ b/sources/src/main/resources/mixins.akarin.core.json @@ -1,6 +1,6 @@ { "required": true, - "minVersion": "0.7.8", + "minVersion": "0.7.10", "package": "io.akarin.server.mixin", "target": "@env(DEFAULT)", "compatibilityLevel": "JAVA_8", diff --git a/sources/src/main/resources/mixins.akarin.optimization.lighting.json b/sources/src/main/resources/mixins.akarin.optimization.lighting.json index db72947a7..28cc496c4 100644 --- a/sources/src/main/resources/mixins.akarin.optimization.lighting.json +++ b/sources/src/main/resources/mixins.akarin.optimization.lighting.json @@ -1,6 +1,6 @@ { "required": true, - "minVersion": "0.7.8", + "minVersion": "0.7.10", "package": "io.akarin.server.mixin", "target": "@env(DEFAULT)", "compatibilityLevel": "JAVA_8", diff --git a/sources/src/main/resources/mixins.akarin.optimization.pandawire.json b/sources/src/main/resources/mixins.akarin.optimization.pandawire.json index d715102e8..794ce2fdb 100644 --- a/sources/src/main/resources/mixins.akarin.optimization.pandawire.json +++ b/sources/src/main/resources/mixins.akarin.optimization.pandawire.json @@ -1,6 +1,6 @@ { "required": true, - "minVersion": "0.7.8", + "minVersion": "0.7.10", "package": "io.akarin.server.mixin", "target": "@env(DEFAULT)", "compatibilityLevel": "JAVA_8", diff --git a/sources/src/main/resources/mixins.akarin.optimization.realtime.json b/sources/src/main/resources/mixins.akarin.optimization.realtime.json index c8458ae9a..e87c12e8c 100644 --- a/sources/src/main/resources/mixins.akarin.optimization.realtime.json +++ b/sources/src/main/resources/mixins.akarin.optimization.realtime.json @@ -1,6 +1,6 @@ { "required": true, - "minVersion": "0.7.8", + "minVersion": "0.7.10", "package": "io.akarin.server.mixin", "target": "@env(DEFAULT)", "compatibilityLevel": "JAVA_8",