Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,15 @@ public void channelRead(final @NotNull ChannelHandlerContext ctx, final Object m

// Let our verification handler process the packet
if (listener != null) {
listener.handle(packet);
try {
listener.handle(packet);
} catch (QuietDecoderException expected) {
// fail() already called disconnect via closeWith() and scheduled
// a delayed close. Don't let the exception propagate to
// TailExceptionsHandler — it would close the channel immediately,
// bypassing the delay and potentially discarding the disconnect packet.
return;
}
// Make sure to let the timeout handler know about this packet
ctx.fireChannelRead(packet);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,25 @@ public static void checkNettyVersion() {
public static void closeWith(final @NotNull Channel channel,
final @NotNull ProtocolVersion protocolVersion,
final @NotNull Object msg) {
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
channel.eventLoop().execute(() -> {
channel.config().setAutoRead(false);
channel.eventLoop().schedule(() -> {
channel.writeAndFlush(msg).addListener(ChannelFutureListener.CLOSE);
}, 250L, TimeUnit.MILLISECONDS);
});
} else {
channel.writeAndFlush(msg).addListener(ChannelFutureListener.CLOSE);
}
// Prevent reading any more packets from the client
channel.config().setAutoRead(false);

final long delay = protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8) ? 250L : 50L;

// Vanilla Minecraft adds a small delay between writing the
// disconnect/transfer packet and closing the connection.
// Without this, the TCP close can arrive at the client before
// the packet is processed, causing "Connection Reset".
channel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
final Channel ch = future.channel();
if (ch.isActive()) {
ch.eventLoop().schedule(() -> {
if (ch.isActive()) {
ch.close();
}
}, delay, TimeUnit.MILLISECONDS);
}
});
}

public static @NotNull UUID readUUID(final @NotNull ByteBuf byteBuf) {
Expand Down