From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Hugo Planque Date: Mon, 18 Jan 2021 11:27:08 +0100 Subject: [PATCH] New Network System Co-authored-by: Ivan Pekov diff --git a/pom.xml b/pom.xml index 6109699411c349c4965de6dbdbd9f8454bc18b10..0bbc7badb1f597357d0fce7cd0f5faf5eb1d0eb5 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,14 @@ 4.1.58.Final + + + io.netty.incubator + netty-incubator-transport-native-io_uring + 0.0.3.Final + linux-x86_64 + + io.papermc minecraft-server diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 87cf9cd88d1fb5ae70d19e5618ebfb67d281304a..a1c2bea7c93433434b4e4dfd0bb4b9620657c40d 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1740,6 +1740,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant() { + ServerConnection.LOGGER.info("Yatopia: Using " + networkType.getName() + " network type."); + ServerBootstrap serverBootstrap = new ServerBootstrap(); + + if (org.yatopiamc.yatopia.server.YatopiaConfig.tcpFastOpen && networkType == NetworkType.EPOLL) { + serverBootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3); // Will improve the connection with some speed + } + this.listeningChannels.add((((serverBootstrap).channelFactory(networkType.getServerSocketChannelFactory())).childHandler(new ChannelInitializer() { protected void initChannel(Channel channel) throws Exception { try { channel.config().setOption(ChannelOption.TCP_NODELAY, true); + channel.config().setOption(ChannelOption.IP_TOS, 0x18); // Change the priority of the packet to immediate } catch (ChannelException channelexception) { ; } @@ -97,7 +122,8 @@ public class ServerConnection { channel.pipeline().addLast("packet_handler", (ChannelHandler) object); ((NetworkManager) object).setPacketListener(new HandshakeListener(ServerConnection.this.e, (NetworkManager) object)); } - }).group((EventLoopGroup) lazyinitvar.a()).localAddress(inetaddress, i)).option(ChannelOption.AUTO_READ, false).bind().syncUninterruptibly()); // CraftBukkit + }).group(boss, worker).localAddress(inetaddress, i)).option(ChannelOption.AUTO_READ, false).bind().syncUninterruptibly()); // CraftBukkit + // Yatopia end } } diff --git a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java index c2dc5265552ebd429111253c70481003a4be29dd..c67654e8701b9da6499d442048429149daae7150 100644 --- a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java +++ b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java @@ -271,4 +271,17 @@ public class YatopiaConfig { logPlayerLoginLoc = getBoolean("settings.log-player-login-location", logPlayerLoginLoc); } + public static boolean ioUringBeta = false; + public static boolean tcpFastOpen = false; + public static boolean debugNetwork = false; + private static void newNetworkSystem() { + ioUringBeta = getBoolean("network.io-uring", ioUringBeta); + if (config.get("network.ftcp-fastopen") != null) { + config.set("network.tcp-fastopen", getBoolean("network.ftcp-fastopen", tcpFastOpen)); + config.set("network.ftcp-fastopen", null); + } + tcpFastOpen = getBoolean("network.tcp-fastopen", tcpFastOpen); + debugNetwork = getBoolean("network.debug", debugNetwork); + } + } diff --git a/src/main/java/org/yatopiamc/yatopia/server/network/NettyThreadFactory.java b/src/main/java/org/yatopiamc/yatopia/server/network/NettyThreadFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3e74e23f3cc44b7547d9f8575b411059e0e65449 --- /dev/null +++ b/src/main/java/org/yatopiamc/yatopia/server/network/NettyThreadFactory.java @@ -0,0 +1,27 @@ +package org.yatopiamc.yatopia.server.network; + +import io.netty.util.concurrent.FastThreadLocalThread; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Based off of Velocity's VelocityNettyThreadFactory + */ +public class NettyThreadFactory implements ThreadFactory { + + private final AtomicInteger threadNumber = new AtomicInteger(); + private final String networkTypeName, loopGroupTypeName; + + public NettyThreadFactory(String networkTypeName, String loopGroupTypeName) { + this.networkTypeName = networkTypeName; + this.loopGroupTypeName = loopGroupTypeName; + } + + @Override + public Thread newThread(Runnable r) { + return new FastThreadLocalThread( + r, + "Netty " + networkTypeName + " " + loopGroupTypeName + " #" + threadNumber.getAndIncrement() + ); + } +} diff --git a/src/main/java/org/yatopiamc/yatopia/server/network/NetworkType.java b/src/main/java/org/yatopiamc/yatopia/server/network/NetworkType.java new file mode 100644 index 0000000000000000000000000000000000000000..6c387f29e4db7e1574a2bbb119465c48b8ed2f90 --- /dev/null +++ b/src/main/java/org/yatopiamc/yatopia/server/network/NetworkType.java @@ -0,0 +1,129 @@ +package org.yatopiamc.yatopia.server.network; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollServerSocketChannel; +import io.netty.channel.kqueue.KQueue; +import io.netty.channel.kqueue.KQueueEventLoopGroup; +import io.netty.channel.kqueue.KQueueServerSocketChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.incubator.channel.uring.IOUring; +import io.netty.incubator.channel.uring.IOUringEventLoopGroup; +import io.netty.incubator.channel.uring.IOUringServerSocketChannel; + +import java.util.concurrent.ThreadFactory; +import java.util.function.BiFunction; + +import net.minecraft.server.MinecraftServer; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.spigotmc.SpigotConfig; +import org.yatopiamc.yatopia.server.YatopiaConfig; + +/** + * Based off of Velocity's TransportType + */ +public enum NetworkType { + NIO("NIO", NioServerSocketChannel::new, (name, type) -> new NioEventLoopGroup(8, createThreadFactory(name, type))), + + EPOLL("Epoll", EpollServerSocketChannel::new, (name, type) -> new EpollEventLoopGroup(8, createThreadFactory(name, type))), + + IOURING("IOUring", IOUringServerSocketChannel::new, (name, type) -> new IOUringEventLoopGroup(8, createThreadFactory(name, type))), + + KQUEUE("KQueue", KQueueServerSocketChannel::new, (name, type) -> new KQueueEventLoopGroup(8, createThreadFactory(name, type))); + + public final String name; + public final ChannelFactory serverSocketChannelFactory; + public final BiFunction eventLoopGroupFactory; + + NetworkType(final String name, + final ChannelFactory serverSocketChannelFactory, + final BiFunction eventLoopGroupFactory) { + this.name = name; + this.serverSocketChannelFactory = serverSocketChannelFactory; + this.eventLoopGroupFactory = eventLoopGroupFactory; + } + + public String getName() { + return name; + } + + public ChannelFactory getServerSocketChannelFactory() { + return serverSocketChannelFactory; + } + + @Override + public String toString() { + return this.name; + } + + public EventLoopGroup createEventLoopGroup(final LoopGroupType type) { + return this.eventLoopGroupFactory.apply(this.name, type); + } + + private static ThreadFactory createThreadFactory(final String name, final LoopGroupType type) { + return new NettyThreadFactory(name, type.toString()); + } + + public static NetworkType bestType(MinecraftServer minecraftServer, Logger logger) { + if (!minecraftServer.isUsingNativeTransport()) { + return NIO; + } + // Actually, there is a decompression problem with zlib from bungeecord that makes + // IOUring not available on spigot server with bungeecord + // https://github.com/netty/netty-incubator-transport-io_uring/issues/40 + // Looks like iouring send very small compressed packet and trigger PacketDecompressor + if (!SpigotConfig.bungee && YatopiaConfig.ioUringBeta && MinecraftServer.getServer().ax() < 0) { + if (IOUring.isAvailable()) { + return IOURING; + } else if (YatopiaConfig.debugNetwork) { + logger.log(Level.ERROR, "IOUring is not working: {}", exceptionMessage(IOUring.unavailabilityCause())); + } + } + + if (Epoll.isAvailable()) { + return EPOLL; + } else if (YatopiaConfig.debugNetwork) { + logger.log(Level.ERROR, "Epoll is not working: {}", exceptionMessage(Epoll.unavailabilityCause())); + } + + if (KQueue.isAvailable()) { + return KQUEUE; + } else if (YatopiaConfig.debugNetwork) { + logger.log(Level.ERROR, "KQueue is not working: {}", exceptionMessage(KQueue.unavailabilityCause())); + } + + return NIO; + } + + private static String exceptionMessage(Throwable throwable) { + StackTraceElement[] trace = throwable.getStackTrace(); + return getClassNameFromString(throwable.getClass().getName()) + " : " + throwable.getMessage() + + ((trace.length > 0) ? " @ " + throwable.getStackTrace()[0].getClassName() + ":" + throwable.getStackTrace()[0].getLineNumber() : ""); + } + + private static String getClassNameFromString(String className) { + int i = className.lastIndexOf('.'); + return i > 0 ? className.substring(i + 1) : className; + } + + public enum LoopGroupType { + BOSS("Boss"), + WORKER("Worker"); + + private final String name; + + LoopGroupType(final String name) { + this.name = name; + } + + @Override + public String toString() { + return this.name; + } + } +}