From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Wed, 16 Oct 2024 06:41:32 -0700 Subject: [PATCH] Add proper async player disconnections Blocking can cause performance problems diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java index f454df4ea912a127b3a2483ff19e697fb419630e..aed2c5b70900def6b1c4b97010b1db26c258f501 100644 --- a/src/main/java/net/minecraft/network/Connection.java +++ b/src/main/java/net/minecraft/network/Connection.java @@ -743,6 +743,14 @@ public class Connection extends SimpleChannelInboundHandler> { } + // Paper start - add proper async disconnect + public void enableAutoRead() { + if (this.channel != null) { + this.channel.config().setAutoRead(true); + } + } + // Paper end - add proper async disconnect + public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) { if (compressionThreshold >= 0) { ChannelHandler channelhandler = this.channel.pipeline().get("decompress"); diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java index fc242acade3ff06c9213428cde103cf078216382..b0bc66dc7248aae691dcab68b925b52a1695e63f 100644 --- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java @@ -143,11 +143,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack this.latency = (this.latency * 3 + i) / 4; this.keepAlivePending = false; } else if (!this.isSingleplayerOwner()) { - // Paper start - This needs to be handled on the main thread for plugins - server.submit(() -> { - this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause - }); - // Paper end - This needs to be handled on the main thread for plugins + this.disconnectAsync(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - add proper async disconnect } } @@ -411,6 +407,31 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper } + // Paper start - add proper async disconnect + public void disconnectAsync(net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) { + this.disconnectAsync(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause); + } + + public void disconnectAsync(Component reason, PlayerKickEvent.Cause cause) { + this.disconnectAsync(new DisconnectionDetails(reason), cause); + } + + public void disconnectAsync(DisconnectionDetails disconnectionInfo, PlayerKickEvent.Cause cause) { + if (this.cserver.isPrimaryThread()) { + this.disconnect(disconnectionInfo, cause); + return; + } + this.connection.setReadOnly(); + this.server.scheduleOnMain(() -> { + ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause); + if (ServerCommonPacketListenerImpl.this.player.quitReason == null) { + // cancelled + ServerCommonPacketListenerImpl.this.connection.enableAutoRead(); + } + }); + } + // Paper end - add proper async disconnect + protected boolean isSingleplayerOwner() { return this.server.isSingleplayerOwner(this.playerProfile()); } diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java index dd8635efd225cef74924db01a84d41cb80e262b9..fbc885e38b9568a51b0d1a604d5ebff408ee071e 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -767,7 +767,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - AsyncTabCompleteEvent; run this async // CraftBukkit start if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { // Paper - configurable tab spam limits - this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause + this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause // Paper - add proper async disconnect return; } // CraftBukkit end @@ -779,7 +779,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl // Paper start final int index; if (packet.getCommand().length() > 64 && ((index = packet.getCommand().indexOf(' ')) == -1 || index >= 64)) { - this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); + this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - add proper async disconnect return; } // Paper end @@ -1225,14 +1225,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl if (byteTotal > byteAllowed) { ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size()); - this.disconnect(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + this.disconnectAsync(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect return; } } // Paper end - Book size limits // CraftBukkit start if (this.lastBookTick + 20 > MinecraftServer.currentTick) { - this.disconnect(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + this.disconnectAsync(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect return; } this.lastBookTick = MinecraftServer.currentTick; @@ -2364,7 +2364,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl private void tryHandleChat(String s, Runnable runnable, boolean sync) { // CraftBukkit if (ServerGamePacketListenerImpl.isChatMessageIllegal(s)) { - this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper + this.disconnectAsync((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper // Paper - add proper async disconnect } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false)); } else { @@ -2387,7 +2387,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl if (optional.isEmpty()) { ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); - this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + this.disconnectAsync(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes // Paper - add proper async disconnect } return optional; @@ -2558,7 +2558,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl // this.chatSpamThrottler.increment(); if (!this.chatSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { // CraftBukkit end - this.disconnect((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause + this.disconnectAsync((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect } } @@ -2570,7 +2570,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl synchronized (this.lastSeenMessages) { if (!this.lastSeenMessages.applyOffset(packet.offset())) { ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); - this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + this.disconnectAsync(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes // Paper - add proper async disconnect } } @@ -2723,7 +2723,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl } if (i > 4096) { - this.disconnect((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause + this.disconnectAsync((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause // Paper - add proper async disconnect } } @@ -3334,7 +3334,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl // Paper start - auto recipe limit if (!org.bukkit.Bukkit.isPrimaryThread()) { if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) { - this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause + this.disconnectAsync(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect return; } }