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 super Void> 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 super Void> 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",