From 188fc31d79348fa24f5f77ca4e98b6c87e015aad Mon Sep 17 00:00:00 2001 From: Hugo Planque <12386279+HookWoods@users.noreply.github.com> Date: Sun, 24 Jan 2021 16:23:34 +0100 Subject: [PATCH] Fix network bug (#355) --- patches/server/0066-New-Network-System.patch | 62 +++-- patches/server/0068-Port-krypton.patch | 279 +------------------ 2 files changed, 38 insertions(+), 303 deletions(-) diff --git a/patches/server/0066-New-Network-System.patch b/patches/server/0066-New-Network-System.patch index 0c0613a56..936e1e532 100644 --- a/patches/server/0066-New-Network-System.patch +++ b/patches/server/0066-New-Network-System.patch @@ -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 a = new LazyInitVar<>(() -> { - return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build()); - }); - public static final LazyInitVar 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 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"); diff --git a/patches/server/0068-Port-krypton.patch b/patches/server/0068-Port-krypton.patch index aad5b8486..bfda1a2db 100644 --- a/patches/server/0068-Port-krypton.patch +++ b/patches/server/0068-Port-krypton.patch @@ -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 { -+ -+ 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 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 { -+ -+ 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 { -+ -+ private final VelocityCipher cipher; -+ -+ public MinecraftCipherDecoder(VelocityCipher cipher) { -+ this.cipher = Preconditions.checkNotNull(cipher, "cipher"); -+ } -+ -+ @Override -+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List 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 { -+ -+ private final VelocityCipher cipher; -+ -+ public MinecraftCipherEncoder(VelocityCipher cipher) { -+ this.cipher = Preconditions.checkNotNull(cipher, "cipher"); -+ } -+ -+ @Override -+ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List 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> { - } - // 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