mirror of
https://github.com/PaperMC/Paper.git
synced 2025-08-06 15:12:13 -07:00
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.
This commit is contained in:
@@ -81,7 +81,29 @@
|
|||||||
if (this.delayedDisconnect != null) {
|
if (this.delayedDisconnect != null) {
|
||||||
this.disconnect(this.delayedDisconnect);
|
this.disconnect(this.delayedDisconnect);
|
||||||
}
|
}
|
||||||
@@ -141,8 +183,10 @@
|
@@ -134,6 +176,21 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
+ if (packet.packetTooLarge(this)) {
|
||||||
|
+ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, packet);
|
||||||
|
+ return;
|
||||||
|
+ } else if (packet.isSkippable()) {
|
||||||
|
+ Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
|
||||||
|
+ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, 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 {
|
||||||
|
@@ -141,8 +198,10 @@
|
||||||
|
|
||||||
this.handlingFault = true;
|
this.handlingFault = true;
|
||||||
if (this.channel.isOpen()) {
|
if (this.channel.isOpen()) {
|
||||||
@@ -92,7 +114,7 @@
|
|||||||
this.disconnect((Component) Component.translatable("disconnect.timeout"));
|
this.disconnect((Component) Component.translatable("disconnect.timeout"));
|
||||||
} else {
|
} else {
|
||||||
MutableComponent ichatmutablecomponent = Component.translatable("disconnect.genericReason", "Internal Exception: " + String.valueOf(throwable));
|
MutableComponent ichatmutablecomponent = Component.translatable("disconnect.genericReason", "Internal Exception: " + String.valueOf(throwable));
|
||||||
@@ -155,6 +199,7 @@
|
@@ -155,6 +214,7 @@
|
||||||
disconnectiondetails = new DisconnectionDetails(ichatmutablecomponent);
|
disconnectiondetails = new DisconnectionDetails(ichatmutablecomponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +122,7 @@
|
|||||||
if (flag) {
|
if (flag) {
|
||||||
Connection.LOGGER.debug("Failed to sent packet", throwable);
|
Connection.LOGGER.debug("Failed to sent packet", throwable);
|
||||||
if (this.getSending() == PacketFlow.CLIENTBOUND) {
|
if (this.getSending() == PacketFlow.CLIENTBOUND) {
|
||||||
@@ -176,6 +221,7 @@
|
@@ -176,6 +236,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +130,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
|
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
|
||||||
@@ -185,11 +231,61 @@
|
@@ -185,11 +246,61 @@
|
||||||
if (packetlistener == null) {
|
if (packetlistener == null) {
|
||||||
throw new IllegalStateException("Received a packet before the packet listener was initialized");
|
throw new IllegalStateException("Received a packet before the packet listener was initialized");
|
||||||
} else {
|
} else {
|
||||||
@@ -170,7 +192,7 @@
|
|||||||
} catch (RejectedExecutionException rejectedexecutionexception) {
|
} catch (RejectedExecutionException rejectedexecutionexception) {
|
||||||
this.disconnect((Component) Component.translatable("multiplayer.disconnect.server_shutdown"));
|
this.disconnect((Component) Component.translatable("multiplayer.disconnect.server_shutdown"));
|
||||||
} catch (ClassCastException classcastexception) {
|
} catch (ClassCastException classcastexception) {
|
||||||
@@ -205,7 +301,7 @@
|
@@ -205,7 +316,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends PacketListener> void genericsFtw(Packet<T> packet, PacketListener listener) {
|
private static <T extends PacketListener> void genericsFtw(Packet<T> packet, PacketListener listener) {
|
||||||
@@ -179,7 +201,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void validateListener(ProtocolInfo<?> state, PacketListener listener) {
|
private void validateListener(ProtocolInfo<?> state, PacketListener listener) {
|
||||||
@@ -418,12 +514,26 @@
|
@@ -418,12 +529,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +228,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isConnected() && !this.disconnectionHandled) {
|
if (!this.isConnected() && !this.disconnectionHandled) {
|
||||||
@@ -431,7 +541,7 @@
|
@@ -431,7 +556,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.channel != null) {
|
if (this.channel != null) {
|
||||||
@@ -215,7 +237,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.tickCount++ % 20 == 0) {
|
if (this.tickCount++ % 20 == 0) {
|
||||||
@@ -464,12 +574,15 @@
|
@@ -464,12 +589,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnect(DisconnectionDetails disconnectionInfo) {
|
public void disconnect(DisconnectionDetails disconnectionInfo) {
|
||||||
@@ -232,7 +254,7 @@
|
|||||||
this.disconnectionDetails = disconnectionInfo;
|
this.disconnectionDetails = disconnectionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,7 +650,7 @@
|
@@ -537,7 +665,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void configurePacketHandler(ChannelPipeline pipeline) {
|
public void configurePacketHandler(ChannelPipeline pipeline) {
|
||||||
@@ -241,7 +263,7 @@
|
|||||||
public void write(ChannelHandlerContext channelhandlercontext, Object object, ChannelPromise channelpromise) throws Exception {
|
public void write(ChannelHandlerContext channelhandlercontext, Object object, ChannelPromise channelpromise) throws Exception {
|
||||||
super.write(channelhandlercontext, object, channelpromise);
|
super.write(channelhandlercontext, object, channelpromise);
|
||||||
}
|
}
|
||||||
@@ -633,6 +746,7 @@
|
@@ -633,6 +761,7 @@
|
||||||
} else {
|
} else {
|
||||||
this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
|
this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
|
||||||
}
|
}
|
||||||
@@ -249,7 +271,7 @@
|
|||||||
} else {
|
} else {
|
||||||
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
|
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
|
||||||
this.channel.pipeline().remove("decompress");
|
this.channel.pipeline().remove("decompress");
|
||||||
@@ -641,6 +755,7 @@
|
@@ -641,6 +770,7 @@
|
||||||
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
|
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
|
||||||
this.channel.pipeline().remove("compress");
|
this.channel.pipeline().remove("compress");
|
||||||
}
|
}
|
||||||
@@ -257,10 +279,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -661,6 +776,27 @@
|
@@ -660,7 +790,28 @@
|
||||||
|
});
|
||||||
|
|
||||||
packetlistener1.onDisconnect(disconnectiondetails);
|
packetlistener1.onDisconnect(disconnectiondetails);
|
||||||
}
|
+ }
|
||||||
+ this.pendingActions.clear(); // Free up packet queue.
|
+ this.pendingActions.clear(); // Free up packet queue.
|
||||||
+ // Paper start - Add PlayerConnectionCloseEvent
|
+ // Paper start - Add PlayerConnectionCloseEvent
|
||||||
+ final PacketListener packetListener = this.getPacketListener();
|
+ final PacketListener packetListener = this.getPacketListener();
|
||||||
@@ -280,7 +303,7 @@
|
|||||||
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(),
|
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(),
|
||||||
+ ((InetSocketAddress) this.address).getAddress(), false).callEvent();
|
+ ((InetSocketAddress) this.address).getAddress(), false).callEvent();
|
||||||
+ }
|
+ }
|
||||||
+ }
|
}
|
||||||
+ // Paper end - Add PlayerConnectionCloseEvent
|
+ // Paper end - Add PlayerConnectionCloseEvent
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
this.protocolInfo.codec().encode(byteBuf, packet);
|
this.protocolInfo.codec().encode(byteBuf, packet);
|
||||||
int i = byteBuf.readableBytes();
|
int i = byteBuf.readableBytes();
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
@@ -31,7 +33,7 @@
|
@@ -31,14 +33,40 @@
|
||||||
|
|
||||||
JvmProfiler.INSTANCE.onPacketSent(this.protocolInfo.id(), packetType, channelHandlerContext.channel().remoteAddress(), i);
|
JvmProfiler.INSTANCE.onPacketSent(this.protocolInfo.id(), packetType, channelHandlerContext.channel().remoteAddress(), i);
|
||||||
} catch (Throwable var9) {
|
} catch (Throwable var9) {
|
||||||
@@ -22,3 +22,36 @@
|
|||||||
if (packet.isSkippable()) {
|
if (packet.isSkippable()) {
|
||||||
throw new SkipPacketException(var9);
|
throw new SkipPacketException(var9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw var9;
|
||||||
|
} finally {
|
||||||
|
+ // Paper start - Handle large packets disconnecting client
|
||||||
|
+ int packetLength = byteBuf.readableBytes();
|
||||||
|
+ if (packetLength > MAX_PACKET_SIZE || (packetLength > MAX_FINAL_PACKET_SIZE && packet.hasLargePacketFallback())) {
|
||||||
|
+ throw new PacketTooLargeException(packet, packetLength);
|
||||||
|
+ }
|
||||||
|
+ // Paper end - Handle large packets disconnecting client
|
||||||
|
ProtocolSwapHandler.handleOutboundTerminalPacket(channelHandlerContext, 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;
|
||||||
|
+
|
||||||
|
+ PacketTooLargeException(Packet<?> packet, int packetLength) {
|
||||||
|
+ super("PacketTooLarge - " + packet.getClass().getSimpleName() + " is " + packetLength + ". Max is " + MAX_PACKET_SIZE);
|
||||||
|
+ this.packet = packet;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ public Packet<?> getPacket() {
|
||||||
|
+ return this.packet;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
}
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
--- a/net/minecraft/network/protocol/Packet.java
|
||||||
|
+++ b/net/minecraft/network/protocol/Packet.java
|
||||||
|
@@ -11,6 +11,19 @@
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
--- a/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
|
||||||
|
+++ b/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
|
||||||
|
@@ -36,6 +36,21 @@
|
||||||
|
this.carriedItem = ItemStack.OPTIONAL_STREAM_CODEC.decode(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ // Paper start - Handle large packets disconnecting client
|
||||||
|
+ @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 - Handle large packets disconnecting client
|
||||||
|
+
|
||||||
|
private void write(RegistryFriendlyByteBuf buf) {
|
||||||
|
buf.writeContainerId(this.containerId);
|
||||||
|
buf.writeVarInt(this.stateId);
|
@@ -1,5 +1,14 @@
|
|||||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||||
|
@@ -52,7 +52,7 @@
|
||||||
|
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];
|
||||||
@@ -154,6 +154,7 @@
|
@@ -154,6 +154,7 @@
|
||||||
CompoundTag compoundTag = blockEntity.getUpdateTag(blockEntity.getLevel().registryAccess());
|
CompoundTag compoundTag = blockEntity.getUpdateTag(blockEntity.getLevel().registryAccess());
|
||||||
BlockPos blockPos = blockEntity.getBlockPos();
|
BlockPos blockPos = blockEntity.getBlockPos();
|
||||||
|
Reference in New Issue
Block a user