Fix network bug (#355)

This commit is contained in:
Hugo Planque 2021-01-24 16:23:34 +01:00 committed by GitHub
parent 39ad366e3b
commit 188fc31d79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 303 deletions

View File

@ -41,7 +41,7 @@ index 87cf9cd88d1fb5ae70d19e5618ebfb67d281304a..a1c2bea7c93433434b4e4dfd0bb4b962
public boolean getPVP() {
diff --git a/src/main/java/net/minecraft/server/ServerConnection.java b/src/main/java/net/minecraft/server/ServerConnection.java
index 0668d383db1f3a81d1053954d72678c7ac5aecec..f20be527bec58bad8e4a5bb7bb887949cb6d1d99 100644
index 0668d383db1f3a81d1053954d72678c7ac5aecec..d22f0ee3f7f2daa8323d454aca1f94733b249333 100644
--- a/src/main/java/net/minecraft/server/ServerConnection.java
+++ b/src/main/java/net/minecraft/server/ServerConnection.java
@@ -11,6 +11,7 @@ import io.netty.channel.ChannelInitializer;
@ -52,7 +52,7 @@ index 0668d383db1f3a81d1053954d72678c7ac5aecec..f20be527bec58bad8e4a5bb7bb887949
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
@@ -26,16 +27,20 @@ import java.util.List;
@@ -26,6 +27,7 @@ import java.util.List;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -60,20 +60,7 @@ index 0668d383db1f3a81d1053954d72678c7ac5aecec..f20be527bec58bad8e4a5bb7bb887949
public class ServerConnection {
private static final Logger LOGGER = LogManager.getLogger();
+ /* // Yatopia Start - New network system - Remove unused fields
public static final LazyInitVar<NioEventLoopGroup> a = new LazyInitVar<>(() -> {
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build());
});
public static final LazyInitVar<EpollEventLoopGroup> b = new LazyInitVar<>(() -> {
return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build());
});
+ */
+ // Yatopia end
private final MinecraftServer e;
public volatile boolean c;
private final List<ChannelFuture> listeningChannels = Collections.synchronizedList(Lists.newArrayList());
@@ -52,15 +57,29 @@ public class ServerConnection {
@@ -52,15 +54,29 @@ public class ServerConnection {
}
// Paper end
@ -87,7 +74,7 @@ index 0668d383db1f3a81d1053954d72678c7ac5aecec..f20be527bec58bad8e4a5bb7bb887949
this.c = true;
+
+ // Yatopia start - New network system
+ this.networkType = NetworkType.bestType(minecraftserver);
+ this.networkType = NetworkType.bestType(minecraftserver, LOGGER);
+ this.boss = networkType.createEventLoopGroup(NetworkType.LoopGroupType.BOSS);
+ this.worker = networkType.createEventLoopGroup(NetworkType.LoopGroupType.WORKER);
+ // Yatopia end
@ -103,7 +90,7 @@ index 0668d383db1f3a81d1053954d72678c7ac5aecec..f20be527bec58bad8e4a5bb7bb887949
Class oclass;
LazyInitVar lazyinitvar;
@@ -73,16 +92,25 @@ public class ServerConnection {
@@ -73,16 +89,25 @@ public class ServerConnection {
lazyinitvar = ServerConnection.a;
ServerConnection.LOGGER.info("Using default channel type");
}
@ -130,7 +117,7 @@ index 0668d383db1f3a81d1053954d72678c7ac5aecec..f20be527bec58bad8e4a5bb7bb887949
} catch (ChannelException channelexception) {
;
}
@@ -97,7 +125,8 @@ public class ServerConnection {
@@ -97,7 +122,8 @@ public class ServerConnection {
channel.pipeline().addLast("packet_handler", (ChannelHandler) object);
((NetworkManager) object).setPacketListener(new HandshakeListener(ServerConnection.this.e, (NetworkManager) object));
}
@ -141,18 +128,24 @@ index 0668d383db1f3a81d1053954d72678c7ac5aecec..f20be527bec58bad8e4a5bb7bb887949
}
diff --git a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java
index 35f212c2ac43ebea6ce9c4a333738c7a869ebc18..c4d0dabd408c7a943dafd6ca89b598cb4afb440e 100644
index 35f212c2ac43ebea6ce9c4a333738c7a869ebc18..6fb4fdaaba092cce04e3600122e534ce8dff073c 100644
--- a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java
+++ b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java
@@ -303,4 +303,11 @@ public class YatopiaConfig {
@@ -303,4 +303,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);
+ tcpFastOpen = getBoolean("network.ftcp-fastopen", tcpFastOpen);
+ 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);
+ }
+
}
@ -191,10 +184,10 @@ index 0000000000000000000000000000000000000000..3e74e23f3cc44b7547d9f8575b411059
+}
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..6b9d788dfef2c51111e9f2129a04fce7754c51ba
index 0000000000000000000000000000000000000000..6c387f29e4db7e1574a2bbb119465c48b8ed2f90
--- /dev/null
+++ b/src/main/java/org/yatopiamc/yatopia/server/network/NetworkType.java
@@ -0,0 +1,110 @@
@@ -0,0 +1,129 @@
+package org.yatopiamc.yatopia.server.network;
+
+import io.netty.channel.ChannelFactory;
@ -216,6 +209,8 @@ index 0000000000000000000000000000000000000000..6b9d788dfef2c51111e9f2129a04fce7
+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;
+
@ -264,7 +259,7 @@ index 0000000000000000000000000000000000000000..6b9d788dfef2c51111e9f2129a04fce7
+ return new NettyThreadFactory(name, type.toString());
+ }
+
+ public static NetworkType bestType(MinecraftServer minecraftServer) {
+ public static NetworkType bestType(MinecraftServer minecraftServer, Logger logger) {
+ if (!minecraftServer.isUsingNativeTransport()) {
+ return NIO;
+ }
@ -275,20 +270,37 @@ index 0000000000000000000000000000000000000000..6b9d788dfef2c51111e9f2129a04fce7
+ 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");

View File

@ -59,232 +59,6 @@ index 0000000000000000000000000000000000000000..cdf5a3b1f7ec27171f3825f89cfb8d39
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/steinborn/krypton/mod/shared/network/compression/MinecraftCompressDecoder.java b/src/main/java/me/steinborn/krypton/mod/shared/network/compression/MinecraftCompressDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9a51c71e136be14ebe8a240c4b21205079fc71b
--- /dev/null
+++ b/src/main/java/me/steinborn/krypton/mod/shared/network/compression/MinecraftCompressDecoder.java
@@ -0,0 +1,68 @@
+package me.steinborn.krypton.mod.shared.network.compression;
+
+import com.velocitypowered.natives.compression.VelocityCompressor;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.CorruptedFrameException;
+import io.netty.handler.codec.MessageToMessageDecoder;
+import net.minecraft.server.PacketDataSerializer;
+
+import java.util.List;
+
+import static com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible;
+import static com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer;
+
+public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
+
+ private static final int VANILLA_MAXIMUM_UNCOMPRESSED_SIZE = 2 * 1024 * 1024; // 2MiB
+ private static final int HARD_MAXIMUM_UNCOMPRESSED_SIZE = 16 * 1024 * 1024; // 16MiB
+
+ private static final int UNCOMPRESSED_CAP =
+ Boolean.getBoolean("velocity.increased-compression-cap")
+ ? HARD_MAXIMUM_UNCOMPRESSED_SIZE : VANILLA_MAXIMUM_UNCOMPRESSED_SIZE;
+
+ private final int threshold;
+ private final VelocityCompressor compressor;
+
+ public MinecraftCompressDecoder(int threshold, VelocityCompressor compressor) {
+ this.threshold = threshold;
+ this.compressor = compressor;
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+ PacketDataSerializer wrappedBuf = new PacketDataSerializer(in);
+ int claimedUncompressedSize = wrappedBuf.readVarInt();
+ if (claimedUncompressedSize == 0) {
+ // This message is not compressed.
+ out.add(in.retainedSlice());
+ return;
+ }
+
+ if (claimedUncompressedSize < threshold) {
+ throw new CorruptedFrameException("Uncompressed size " + claimedUncompressedSize + " is less than"
+ + " threshold " + threshold);
+ }
+ if (claimedUncompressedSize > UNCOMPRESSED_CAP) {
+ throw new CorruptedFrameException("Uncompressed size " + claimedUncompressedSize + " exceeds hard " +
+ "threshold of " + UNCOMPRESSED_CAP);
+ }
+
+ ByteBuf compatibleIn = ensureCompatible(ctx.alloc(), compressor, in);
+ ByteBuf uncompressed = preferredBuffer(ctx.alloc(), compressor, claimedUncompressedSize);
+ try {
+ compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
+ out.add(uncompressed);
+ } catch (Exception e) {
+ uncompressed.release();
+ throw e;
+ } finally {
+ compatibleIn.release();
+ }
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ compressor.close();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/steinborn/krypton/mod/shared/network/compression/MinecraftCompressEncoder.java b/src/main/java/me/steinborn/krypton/mod/shared/network/compression/MinecraftCompressEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..1114e1db59476353ad609a519b71479438db7b0c
--- /dev/null
+++ b/src/main/java/me/steinborn/krypton/mod/shared/network/compression/MinecraftCompressEncoder.java
@@ -0,0 +1,59 @@
+package me.steinborn.krypton.mod.shared.network.compression;
+
+
+import com.velocitypowered.natives.compression.VelocityCompressor;
+import com.velocitypowered.natives.util.MoreByteBufUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+import net.minecraft.server.PacketDataSerializer;
+
+public class MinecraftCompressEncoder extends MessageToByteEncoder<ByteBuf> {
+
+ private final int threshold;
+ private final VelocityCompressor compressor;
+
+ public MinecraftCompressEncoder(int threshold, VelocityCompressor compressor) {
+ this.threshold = threshold;
+ this.compressor = compressor;
+ }
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
+ PacketDataSerializer wrappedBuf = new PacketDataSerializer(out);
+ int uncompressed = msg.readableBytes();
+ if (uncompressed < threshold) {
+ // Under the threshold, there is nothing to do.
+ wrappedBuf.writeVarInt(0);
+ out.writeBytes(msg);
+ } else {
+ wrappedBuf.writeVarInt(uncompressed);
+ ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, msg);
+ try {
+ compressor.deflate(compatibleIn, out);
+ } finally {
+ compatibleIn.release();
+ }
+ }
+ }
+
+ @Override
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect)
+ throws Exception {
+ // We allocate bytes to be compressed plus 1 byte. This covers two cases:
+ //
+ // - Compression
+ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103,
+ // if the data compresses well (and we do not have some pathological case) then the maximum
+ // size the compressed size will ever be is the input size minus one.
+ // - Uncompressed
+ // This is fairly obvious - we will then have one more than the uncompressed size.
+ int initialBufferSize = msg.readableBytes() + 1;
+ return MoreByteBufUtils.preferredBuffer(ctx.alloc(), compressor, initialBufferSize);
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ compressor.close();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/steinborn/krypton/mod/shared/network/pipeline/MinecraftCipherDecoder.java b/src/main/java/me/steinborn/krypton/mod/shared/network/pipeline/MinecraftCipherDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..2612c350446b172629b8030602ed812fa69f24a4
--- /dev/null
+++ b/src/main/java/me/steinborn/krypton/mod/shared/network/pipeline/MinecraftCipherDecoder.java
@@ -0,0 +1,36 @@
+package me.steinborn.krypton.mod.shared.network.pipeline;
+
+import com.google.common.base.Preconditions;
+import com.velocitypowered.natives.encryption.VelocityCipher;
+import com.velocitypowered.natives.util.MoreByteBufUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageDecoder;
+
+import java.util.List;
+
+public class MinecraftCipherDecoder extends MessageToMessageDecoder<ByteBuf> {
+
+ private final VelocityCipher cipher;
+
+ public MinecraftCipherDecoder(VelocityCipher cipher) {
+ this.cipher = Preconditions.checkNotNull(cipher, "cipher");
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+ ByteBuf compatible = MoreByteBufUtils.ensureCompatible(ctx.alloc(), cipher, in).slice();
+ try {
+ cipher.process(compatible);
+ out.add(compatible);
+ } catch (Exception e) {
+ compatible.release(); // compatible will never be used if we throw an exception
+ throw e;
+ }
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ cipher.close();
+ }
+}
diff --git a/src/main/java/me/steinborn/krypton/mod/shared/network/pipeline/MinecraftCipherEncoder.java b/src/main/java/me/steinborn/krypton/mod/shared/network/pipeline/MinecraftCipherEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..068b0d51daf11045a5054cddd53897ecdd117a37
--- /dev/null
+++ b/src/main/java/me/steinborn/krypton/mod/shared/network/pipeline/MinecraftCipherEncoder.java
@@ -0,0 +1,36 @@
+package me.steinborn.krypton.mod.shared.network.pipeline;
+
+import com.google.common.base.Preconditions;
+import com.velocitypowered.natives.encryption.VelocityCipher;
+import com.velocitypowered.natives.util.MoreByteBufUtils;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageEncoder;
+
+import java.util.List;
+
+public class MinecraftCipherEncoder extends MessageToMessageEncoder<ByteBuf> {
+
+ private final VelocityCipher cipher;
+
+ public MinecraftCipherEncoder(VelocityCipher cipher) {
+ this.cipher = Preconditions.checkNotNull(cipher, "cipher");
+ }
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
+ ByteBuf compatible = MoreByteBufUtils.ensureCompatible(ctx.alloc(), cipher, msg);
+ try {
+ cipher.process(compatible);
+ out.add(compatible);
+ } catch (Exception e) {
+ compatible.release(); // compatible will never be used if we throw an exception
+ throw e;
+ }
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ cipher.close();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/server/LegacyPingHandler.java b/src/main/java/net/minecraft/server/LegacyPingHandler.java
index 4a49fe4cc600e2b70963302ddae0c4479849f3f5..3abc3869b8012f060e1997822ffdb321f4884929 100644
--- a/src/main/java/net/minecraft/server/LegacyPingHandler.java
@ -302,7 +76,7 @@ index 4a49fe4cc600e2b70963302ddae0c4479849f3f5..3abc3869b8012f060e1997822ffdb321
// Paper start - Make legacy ping handler more reliable
diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java
index 847122f76f6d951b24b22c86276140e02aaf37d6..174197f41bedab6a45e96323adff4f3f6238cef2 100644
index 847122f76f6d951b24b22c86276140e02aaf37d6..fe15c450bc1f792dba74823e08fc70f0757ceac3 100644
--- a/src/main/java/net/minecraft/server/LoginListener.java
+++ b/src/main/java/net/minecraft/server/LoginListener.java
@@ -8,6 +8,7 @@ import java.math.BigInteger;
@ -313,57 +87,6 @@ index 847122f76f6d951b24b22c86276140e02aaf37d6..174197f41bedab6a45e96323adff4f3f
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.Random;
@@ -256,8 +257,8 @@ public class LoginListener implements PacketLoginInListener {
s = (new BigInteger(MinecraftEncryption.a("", this.server.getKeyPair().getPublic(), this.loginKey))).toString(16);
this.g = LoginListener.EnumProtocolState.AUTHENTICATING;
- this.networkManager.a(this.loginKey); // Tuinity
- } catch (CryptographyException cryptographyexception) {
+ this.networkManager.setupEncryption(this.loginKey); // Tuinity // Yatopia - New network system
+ } catch (CryptographyException | GeneralSecurityException cryptographyexception) { // Yatopia
throw new IllegalStateException("Protocol error", cryptographyexception);
}
diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java
index 08227ab446d6332af76491a063653f7f13f43560..a6890caa3c78dea773a7f71e919d5f352db02e1b 100644
--- a/src/main/java/net/minecraft/server/NetworkManager.java
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
@@ -21,6 +21,17 @@ import java.net.SocketAddress;
import java.util.Queue;
import javax.annotation.Nullable;
import javax.crypto.Cipher;
+// Yatopia start
+import javax.crypto.SecretKey;
+import me.steinborn.krypton.mod.shared.network.compression.MinecraftCompressDecoder;
+import me.steinborn.krypton.mod.shared.network.compression.MinecraftCompressEncoder;
+import me.steinborn.krypton.mod.shared.network.pipeline.MinecraftCipherDecoder;
+import me.steinborn.krypton.mod.shared.network.pipeline.MinecraftCipherEncoder;
+import com.velocitypowered.natives.compression.VelocityCompressor;
+import com.velocitypowered.natives.encryption.VelocityCipher;
+import com.velocitypowered.natives.util.Natives;
+import java.security.GeneralSecurityException;
+// Yatopia end
import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -107,6 +118,17 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
}
// Tuinity end - allow controlled flushing
+ // Yatopia start
+ public void setupEncryption(SecretKey key) throws GeneralSecurityException {
+ VelocityCipher decryption = Natives.cipher.get().forDecryption(key);
+ VelocityCipher encryption = Natives.cipher.get().forEncryption(key);
+
+ this.n = true;
+ this.channel.pipeline().addBefore("splitter", "decrypt", new MinecraftCipherDecoder(decryption));
+ this.channel.pipeline().addBefore("prepender", "encrypt", new MinecraftCipherEncoder(encryption));
+ }
+ // Yatopia end
+
public NetworkManager(EnumProtocolDirection enumprotocoldirection) {
this.h = enumprotocoldirection;
}
diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/main/java/net/minecraft/server/PacketDataSerializer.java
index f43193c1090238f2241b878120247d1b3d0d4e57..7dc31ee3211a895993c522a7155a0d8641fd442c 100644
--- a/src/main/java/net/minecraft/server/PacketDataSerializer.java