From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 27 Nov 2018 21:18:06 -0500
Subject: [PATCH] Handle Large Packets disconnecting client

If a players inventory is too big to send in a single packet,
split the inventory set into multiple packets instead.

diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java
index 3351389dc122b66c198fb21ff02976491e9b46d9..c90c616860b5157956250e4fe21e1764dd04881c 100644
--- a/src/main/java/net/minecraft/server/NetworkManager.java
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
@@ -12,6 +12,7 @@ import io.netty.channel.epoll.EpollEventLoopGroup;
 import io.netty.channel.local.LocalChannel;
 import io.netty.channel.local.LocalServerChannel;
 import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.handler.codec.EncoderException; // Paper
 import io.netty.handler.timeout.TimeoutException;
 import io.netty.util.AttributeKey;
 import io.netty.util.concurrent.Future;
@@ -97,6 +98,15 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
     }
 
     public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
+        // Paper start
+        if (throwable instanceof EncoderException && throwable.getCause() instanceof PacketEncoder.PacketTooLargeException) {
+            if (((PacketEncoder.PacketTooLargeException) throwable.getCause()).getPacket().packetTooLarge(this)) {
+                return;
+            } else {
+                throwable = throwable.getCause();
+            }
+        }
+        // Paper end
         if (throwable instanceof SkipEncodeException) {
             NetworkManager.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
         } else {
diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java
index 601d4d0fa2a0b3e90f81aed55a2d3473c66c7875..2d8e6a2f4a0c3c5d74a647d7164b0028781d3bf5 100644
--- a/src/main/java/net/minecraft/server/Packet.java
+++ b/src/main/java/net/minecraft/server/Packet.java
@@ -10,6 +10,12 @@ public interface Packet<T extends PacketListener> {
 
     void a(T t0);
 
+    // Paper start
+    default boolean packetTooLarge(NetworkManager manager) {
+        return false;
+    }
+    // Paper end
+
     default boolean a() {
         return false;
     }
diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java
index d3c6bdbefb89b3e668477e0d771015e9b25c24a8..dbf85f49703f9599f6d6b441db566dd8f6d3cd4b 100644
--- a/src/main/java/net/minecraft/server/PacketEncoder.java
+++ b/src/main/java/net/minecraft/server/PacketEncoder.java
@@ -51,7 +51,31 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
                         throw throwable;
                     }
                 }
+
+                // Paper start
+                int packetLength = bytebuf.readableBytes();
+                if (packetLength > MAX_PACKET_SIZE) {
+                    throw new PacketTooLargeException(packet, packetLength);
+                }
+                // Paper end
             }
         }
     }
+
+    // Paper start
+    private static int MAX_PACKET_SIZE = 2097152;
+
+    public static class PacketTooLargeException extends RuntimeException {
+        private final Packet<?> packet;
+
+        PacketTooLargeException(Packet<?> packet, int packetLength) {
+            super("PacketTooLarge - " + packet.getClass().getSimpleName() + " is " + packetLength + ". Max is " + MAX_PACKET_SIZE);
+            this.packet = packet;
+        }
+
+        public Packet<?> getPacket() {
+            return packet;
+        }
+    }
+    // Paper end
 }
diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
index 0ef4fbb19fc816f578ad741c4406790f4185093d..34e92e544f37167317d58af4242acde49e588673 100644
--- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
+++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
@@ -79,7 +79,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
 
         int i = packetdataserializer.i();
 
-        if (i > 2097152) {
+        if (i > 2097152) { // Paper - if this changes, update PacketEncoder
             throw new RuntimeException("Chunk Packet trying to allocate too much memory on read.");
         } else {
             this.f = new byte[i];
diff --git a/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java b/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java
index e32db2baf84dcb192800e6048cadf3511da28181..6fb3168fd6de0074b1b378e992e4706c50ae15ab 100644
--- a/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java
+++ b/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java
@@ -9,6 +9,15 @@ public class PacketPlayOutWindowItems implements Packet<PacketListenerPlayOut> {
     private int a;
     private List<ItemStack> b;
 
+    //Paper start
+    @Override
+    public boolean packetTooLarge(NetworkManager manager) {
+        for (int i = 0 ; i < this.b.size() ; i++) {
+            manager.sendPacket(new PacketPlayOutSetSlot(this.a, i, this.b.get(i)));
+        }
+        return true;
+    }
+    // Paper end
     public PacketPlayOutWindowItems() {}
 
     public PacketPlayOutWindowItems(int i, NonNullList<ItemStack> nonnulllist) {