aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0273-Handle-Large-Packets-disconnecting-client.patch
blob: 183b46bcde3d186891fbd2839713df9f55bf1c70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 02b3f5c67b47a098f7fe15ddba0df6cb586a9ae5..157f055df00faf3a7870df8109e84fdb12f55964 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -156,6 +156,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
     }
 
     public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
+        // Paper start - Handle large packets disconnecting client
+        if (throwable instanceof io.netty.handler.codec.EncoderException && throwable.getCause() instanceof PacketEncoder.PacketTooLargeException packetTooLargeException) {
+            final Packet<?> packet = packetTooLargeException.getPacket();
+            final io.netty.util.Attribute<ConnectionProtocol.CodecData<?>> codecDataAttribute = channelhandlercontext.channel().attr(packetTooLargeException.codecKey);
+            if (packet.packetTooLarge(this)) {
+                ProtocolSwapHandler.swapProtocolIfNeeded(codecDataAttribute, packet);
+                return;
+            } else if (packet.isSkippable()) {
+                Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
+                ProtocolSwapHandler.swapProtocolIfNeeded(codecDataAttribute, packet);
+                return;
+            } else {
+                throwable = throwable.getCause();
+            }
+        }
+        // Paper end - Handle large packets disconnecting client
         if (throwable instanceof SkipPacketException) {
             Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
         } else {
diff --git a/src/main/java/net/minecraft/network/PacketEncoder.java b/src/main/java/net/minecraft/network/PacketEncoder.java
index 0d80fcee1831af59b06c4d00dc713bd4dad947fc..3fe047a6ea4eedb92a795dc747d30c0815c2baf2 100644
--- a/src/main/java/net/minecraft/network/PacketEncoder.java
+++ b/src/main/java/net/minecraft/network/PacketEncoder.java
@@ -41,7 +41,7 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
                     int j = friendlyByteBuf.writerIndex();
                     packet.write(friendlyByteBuf);
                     int k = friendlyByteBuf.writerIndex() - j;
-                    if (k > 8388608) {
+                    if (false && k > 8388608) { // Paper - Handle large packets disconnecting client; disable
                         throw new IllegalArgumentException("Packet too big (is " + k + ", should be less than 8388608): " + packet);
                     }
 
@@ -54,9 +54,37 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
 
                     throw var13;
                 } finally {
+                    // Paper start - Handle large packets disconnecting client
+                    int packetLength = friendlyByteBuf.readableBytes();
+                    if (packetLength > MAX_PACKET_SIZE || (packetLength > MAX_FINAL_PACKET_SIZE && packet.hasLargePacketFallback())) {
+                        throw new PacketTooLargeException(packet, this.codecKey, packetLength);
+                    }
+                    // Paper end - Handle large packets disconnecting client
                     ProtocolSwapHandler.swapProtocolIfNeeded(attribute, packet);
                 }
             }
         }
     }
+
+    // Paper start
+    // packet size is encoded into 3-byte varint
+    private static final int MAX_FINAL_PACKET_SIZE = (1 << 21) - 1;
+    // Vanilla Max size for the encoder (before compression)
+    private static final int MAX_PACKET_SIZE = 8388608;
+
+    public static class PacketTooLargeException extends RuntimeException {
+        private final Packet<?> packet;
+        public final AttributeKey<ConnectionProtocol.CodecData<?>> codecKey;
+
+        PacketTooLargeException(Packet<?> packet, AttributeKey<ConnectionProtocol.CodecData<?>> codecKey, int packetLength) {
+            super("PacketTooLarge - " + packet.getClass().getSimpleName() + " is " + packetLength + ". Max is " + MAX_PACKET_SIZE);
+            this.packet = packet;
+            this.codecKey = codecKey;
+        }
+
+        public Packet<?> getPacket() {
+            return this.packet;
+        }
+    }
+    // Paper end
 }
diff --git a/src/main/java/net/minecraft/network/protocol/Packet.java b/src/main/java/net/minecraft/network/protocol/Packet.java
index 700418bb0c9fbed3f161611881b1e222248ca4eb..c920bf67002f1ca969b5e1559cdfdc2704dead4b 100644
--- a/src/main/java/net/minecraft/network/protocol/Packet.java
+++ b/src/main/java/net/minecraft/network/protocol/Packet.java
@@ -10,6 +10,19 @@ public interface Packet<T extends PacketListener> {
 
     void handle(T listener);
 
+    // Paper start
+    default boolean hasLargePacketFallback() {
+        return false;
+    }
+
+    /**
+     * override {@link #hasLargePacketFallback()} to return true when overriding in subclasses
+     */
+    default boolean packetTooLarge(net.minecraft.network.Connection manager) {
+        return false;
+    }
+    // Paper end
+
     default boolean isSkippable() {
         return false;
     }
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
index 6206d4d71dfe95b454b22f5b3055623638e145c0..3d52fd6de8b33e45450bb601697920bf94493fb9 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
@@ -31,6 +31,21 @@ public class ClientboundContainerSetContentPacket implements Packet<ClientGamePa
         this.carriedItem = buf.readItem();
     }
 
+    // Paper start
+    @Override
+    public boolean hasLargePacketFallback() {
+        return true;
+    }
+
+    @Override
+    public boolean packetTooLarge(net.minecraft.network.Connection manager) {
+        for (int i = 0 ; i < this.items.size() ; i++) {
+            manager.send(new ClientboundContainerSetSlotPacket(this.containerId, this.stateId, i, this.items.get(i)));
+        }
+        return true;
+    }
+    // Paper end
+
     @Override
     public void write(FriendlyByteBuf buf) {
         buf.writeByte(this.containerId);
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index dc657312889da4fc3222a6981223a01406b77deb..a44a82d2d5ed4d675dc1a184d5b6b935fda575dd 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -49,7 +49,7 @@ public class ClientboundLevelChunkPacketData {
             throw new RuntimeException("Can't read heightmap in packet for [" + x + ", " + z + "]");
         } else {
             int i = buf.readVarInt();
-            if (i > 2097152) {
+            if (i > 2097152) { // Paper - diff on change - if this changes, update PacketEncoder
                 throw new RuntimeException("Chunk Packet trying to allocate too much memory on read.");
             } else {
                 this.buffer = new byte[i];