From 0d8e6831c3cb7e4d3c1743a1fb3605a538fb5aa6 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Fri, 12 Apr 2024 20:37:00 +0100 Subject: [PATCH] Fix StackOverflowException thrown on shutdown (Fixes #10404) (#10408) paper previously migrated away from using executeIfPossible as this throws a RejectedExecutionException when the server is shutting down, which is then picked up by the Connection handler object and causes the player to be kicked without the intended disconnection message that comes from commands such as /stop, /restart This was fine, because previously changes made in spigot would just prevent these packets from being executed anyways. Instead, we'll just use a marker exception to try to detect this specific state. --- ...l-more-information-in-watchdog-dumps.patch | 2 +- .../server/Fix-player-kick-on-shutdown.patch | 23 ------- ...x-premature-player-kicks-on-shutdown.patch | 61 +++++++++++++++++++ 3 files changed, 62 insertions(+), 24 deletions(-) delete mode 100644 patches/server/Fix-player-kick-on-shutdown.patch create mode 100644 patches/server/Fix-premature-player-kicks-on-shutdown.patch diff --git a/patches/server/Detail-more-information-in-watchdog-dumps.patch b/patches/server/Detail-more-information-in-watchdog-dumps.patch index 871514f0b..ad606606b 100644 --- a/patches/server/Detail-more-information-in-watchdog-dumps.patch +++ b/patches/server/Detail-more-information-in-watchdog-dumps.patch @@ -56,7 +56,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public class PacketUtils { public static void ensureRunningOnSameThread(Packet packet, T listener, BlockableEventLoop engine) throws RunningOnDifferentThreadException { if (!engine.isSameThread()) { - engine.execute(() -> { // Paper - Fix preemptive player kick on a server shutdown + engine.executeIfPossible(() -> { + packetProcessing.push(listener); // Paper - detailed watchdog information + try { // Paper - detailed watchdog information if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players diff --git a/patches/server/Fix-player-kick-on-shutdown.patch b/patches/server/Fix-player-kick-on-shutdown.patch deleted file mode 100644 index b909eee77..000000000 --- a/patches/server/Fix-player-kick-on-shutdown.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Denery -Date: Sun, 6 Nov 2022 02:02:46 +0300 -Subject: [PATCH] Fix player kick on shutdown - -Fix preemptive player kick on a server shutdown. -If you update minecraft version / upstream and something is changed in this method make sure that a server doesn't disconnect a player preemptively, -also check if all packets are ignored during the shutdown process. -See net.minecraft.network.Connection#channelRead0(ChannelHandlerContext, Packet) and net.minecraft.util.thread.BlockableEventLoop#executeIfPossible(Runnable) - -diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java -+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -@@ -0,0 +0,0 @@ public class PacketUtils { - - public static void ensureRunningOnSameThread(Packet packet, T listener, BlockableEventLoop engine) throws RunningOnDifferentThreadException { - if (!engine.isSameThread()) { -- engine.executeIfPossible(() -> { -+ engine.execute(() -> { // Paper - Fix preemptive player kick on a server shutdown - if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players - if (listener.shouldHandleMessage(packet)) { - co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings diff --git a/patches/server/Fix-premature-player-kicks-on-shutdown.patch b/patches/server/Fix-premature-player-kicks-on-shutdown.patch new file mode 100644 index 000000000..20d95b984 --- /dev/null +++ b/patches/server/Fix-premature-player-kicks-on-shutdown.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder +Date: Thu, 11 Apr 2024 16:37:44 +0100 +Subject: [PATCH] Fix premature player kicks on shutdown + +When the server is stopping, the default execution handler method will throw a +RejectedExecutionException in order to prevent further execution, this causes +us to lose the actual kick reason. To mitigate this, we'll use a seperate marked +class in order to gracefully ignore these. + +diff --git a/src/main/java/io/papermc/paper/util/ServerStopRejectedExecutionException.java b/src/main/java/io/papermc/paper/util/ServerStopRejectedExecutionException.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/ServerStopRejectedExecutionException.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.util; ++ ++import java.util.concurrent.RejectedExecutionException; ++ ++public class ServerStopRejectedExecutionException extends RejectedExecutionException { ++ public ServerStopRejectedExecutionException() { ++ } ++ ++ public ServerStopRejectedExecutionException(final String message) { ++ super(message); ++ } ++ ++ public ServerStopRejectedExecutionException(final String message, final Throwable cause) { ++ super(message, cause); ++ } ++ ++ public ServerStopRejectedExecutionException(final Throwable cause) { ++ super(cause); ++ } ++} +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler> { + Connection.genericsFtw(packet, packetlistener); + } catch (RunningOnDifferentThreadException cancelledpackethandleexception) { + ; ++ } catch (io.papermc.paper.util.ServerStopRejectedExecutionException ignored) { // Paper - do not prematurely disconnect players on stop + } catch (RejectedExecutionException rejectedexecutionexception) { + this.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown")); + } catch (ClassCastException classcastexception) { +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop