diff options
author | Nassim Jahnke <[email protected]> | 2024-06-17 13:36:43 +0200 |
---|---|---|
committer | Nassim Jahnke <[email protected]> | 2024-06-17 13:36:43 +0200 |
commit | 152f4feff3dc3390fe2ff5198ac86adbab452290 (patch) | |
tree | ab4ab44c50e920b126aff8746657e280ebcddbf9 /patches/unapplied | |
parent | a7f66333625febf891e5126bbd432707d38d1b1c (diff) | |
download | Paper-152f4feff3dc3390fe2ff5198ac86adbab452290.tar.gz Paper-152f4feff3dc3390fe2ff5198ac86adbab452290.zip |
Add back improved watchdog support patch
Diffstat (limited to 'patches/unapplied')
-rw-r--r-- | patches/unapplied/server/1010-Improved-Watchdog-Support.patch | 544 |
1 files changed, 0 insertions, 544 deletions
diff --git a/patches/unapplied/server/1010-Improved-Watchdog-Support.patch b/patches/unapplied/server/1010-Improved-Watchdog-Support.patch deleted file mode 100644 index 88ad35da42..0000000000 --- a/patches/unapplied/server/1010-Improved-Watchdog-Support.patch +++ /dev/null @@ -1,544 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar <[email protected]> -Date: Sun, 12 Apr 2020 15:50:48 -0400 -Subject: [PATCH] Improved Watchdog Support - -Forced Watchdog Crash support and Improve Async Shutdown - -If the request to shut down the server is received while we are in -a watchdog hang, immediately treat it as a crash and begin the shutdown -process. Shutdown process is now improved to also shutdown cleanly when -not using restart scripts either. - -If a server is deadlocked, a server owner can send SIGUP (or any other signal -the JVM understands to shut down as it currently does) and the watchdog -will no longer need to wait until the full timeout, allowing you to trigger -a close process and try to shut the server down gracefully, saving player and -world data. - -Previously there was no way to trigger this outside of waiting for a full watchdog -timeout, which may be set to a really long time... - -Additionally, fix everything to do with shutting the server down asynchronously. - -Previously, nearly everything about the process was fragile and unsafe. Main might -not have actually been frozen, and might still be manipulating state. - -Or, some reuest might ask main to do something in the shutdown but main is dead. - -Or worse, other things might start closing down items such as the Console or Thread Pool -before we are fully shutdown. - -This change tries to resolve all of these issues by moving everything into the stop -method and guaranteeing only one thread is stopping the server. - -We then issue Thread Death to the main thread of another thread initiates the stop process. -We have to ensure Thread Death propagates correctly though to stop main completely. - -This is to ensure that if main isn't truely stuck, it's not manipulating state we are trying to save. - -This also moves all plugins who register "delayed init" tasks to occur just before "Done" so they -are properly accounted for and wont trip watchdog on init. - -diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 6aaed8e8bf8c721fc834da5c76ac72a4c3e92458..4b002e8b75d117b726b0de274a76d3596fce015b 100644 ---- a/src/main/java/com/destroystokyo/paper/Metrics.java -+++ b/src/main/java/com/destroystokyo/paper/Metrics.java -@@ -92,7 +92,12 @@ public class Metrics { - * Starts the Scheduler which submits our data every 30 minutes. - */ - private void startSubmitting() { -- final Runnable submitTask = this::submitData; -+ final Runnable submitTask = () -> { -+ if (MinecraftServer.getServer().hasStopped()) { -+ return; -+ } -+ submitData(); -+ }; - - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay. -diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java -index bcb6a3b3cd17ce5db9aaf6bd3ec7a0ec1b44b979..4f3cc14d48690bb183d09bb7a5ba1e23e8a0c08a 100644 ---- a/src/main/java/net/minecraft/CrashReport.java -+++ b/src/main/java/net/minecraft/CrashReport.java -@@ -234,6 +234,7 @@ public class CrashReport { - } - - public static CrashReport forThrowable(Throwable cause, String title) { -+ if (cause instanceof ThreadDeath) com.destroystokyo.paper.util.SneakyThrow.sneaky(cause); // Paper - while (cause instanceof CompletionException && cause.getCause() != null) { - cause = cause.getCause(); - } -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 02fbf3c44a46f4871deeb42a2678697d23235b2e..03552ca486807d057147f6a05f024989d593d046 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -300,7 +300,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>(); - public int autosavePeriod; - public Commands vanillaCommandDispatcher; -- private boolean forceTicks; -+ public boolean forceTicks; // Paper - // CraftBukkit end - // Spigot start - public static final int TPS = 20; -@@ -313,6 +313,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - public static long currentTickLong = 0L; // Paper - track current tick as a long - public boolean isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked - -+ public volatile Thread shutdownThread; // Paper -+ public volatile boolean abnormalExit = false; // Paper -+ - public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) { - AtomicReference<S> atomicreference = new AtomicReference(); - Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system -@@ -930,6 +933,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - // CraftBukkit start - private boolean hasStopped = false; - private boolean hasLoggedStop = false; // Paper - Debugging -+ public volatile boolean hasFullyShutdown = false; // Paper - private final Object stopLock = new Object(); - public final boolean hasStopped() { - synchronized (this.stopLock) { -@@ -945,6 +949,19 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - this.hasStopped = true; - } - if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging -+ // Paper start - kill main thread, and kill it hard -+ shutdownThread = Thread.currentThread(); -+ org.spigotmc.WatchdogThread.doStop(); // Paper -+ if (!isSameThread()) { -+ MinecraftServer.LOGGER.info("Stopping main thread (Ignore any thread death message you see! - DO NOT REPORT THREAD DEATH TO PAPER)"); -+ while (this.getRunningThread().isAlive()) { -+ this.getRunningThread().stop(); -+ try { -+ Thread.sleep(1); -+ } catch (InterruptedException e) {} -+ } -+ } -+ // Paper end - // CraftBukkit end - if (this.metricsRecorder.isRecording()) { - this.cancelRecordingMetrics(); -@@ -1001,7 +1018,21 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - } - // Spigot end - -+ // Paper start - move final shutdown items here -+ LOGGER.info("Flushing Chunk IO"); -+ // Paper end - move final shutdown items here - io.papermc.paper.chunk.system.io.RegionFileIOThread.close(true); // Paper - rewrite chunk system -+ // Paper start - move final shutdown items here -+ LOGGER.info("Closing Thread Pool"); -+ Util.shutdownExecutors(); // Paper -+ LOGGER.info("Closing Server"); -+ try { -+ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender -+ } catch (Exception e) { -+ } -+ io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown -+ this.onServerExit(); -+ // Paper end - move final shutdown items here - } - - public String getLocalIp() { -@@ -1098,6 +1129,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - - protected void runServer() { - try { -+ long serverStartTime = Util.getNanos(); // Paper - if (!this.initServer()) { - throw new IllegalStateException("Failed to initialize server"); - } -@@ -1107,6 +1139,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - this.status = this.buildServerStatus(); - - // Spigot start -+ // Paper start - move done tracking -+ LOGGER.info("Running delayed init tasks"); -+ this.server.getScheduler().mainThreadHeartbeat(this.tickCount); // run all 1 tick delay tasks during init, -+ // this is going to be the first thing the tick process does anyways, so move done and run it after -+ // everything is init before watchdog tick. -+ // anything at 3+ won't be caught here but also will trip watchdog.... -+ // tasks are default scheduled at -1 + delay, and first tick will tick at 1 -+ String doneTime = String.format(java.util.Locale.ROOT, "%.3fs", (double) (Util.getNanos() - serverStartTime) / 1.0E9D); -+ LOGGER.info("Done ({})! For help, type \"help\"", doneTime); -+ // Paper end -+ -+ org.spigotmc.WatchdogThread.tick(); // Paper - org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Arrays.fill( this.recentTps, 20 ); - // Paper start - further improve server tick loop -@@ -1201,6 +1245,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - JvmProfiler.INSTANCE.onServerTick(this.smoothedTickTimeMillis); - } - } catch (Throwable throwable) { -+ // Paper start -+ if (throwable instanceof ThreadDeath) { -+ MinecraftServer.LOGGER.error("Main thread terminated by WatchDog due to hard crash", throwable); -+ return; -+ } -+ // Paper end - MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable); - CrashReport crashreport = MinecraftServer.constructOrExtractCrashReport(throwable); - -@@ -1225,15 +1275,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - this.services.profileCache().clearExecutor(); - } - -- org.spigotmc.WatchdogThread.doStop(); // Spigot -+ //org.spigotmc.WatchdogThread.doStop(); // Spigot // Paper - move into stop - // CraftBukkit start - Restore terminal to original settings - try { -- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender -+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop - } catch (Exception ignored) { - } - // CraftBukkit end -- io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown -- this.onServerExit(); -+ //io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown -+ //this.onServerExit(); // Paper - moved into stop - } - - } -@@ -1340,6 +1390,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - - @Override - public TickTask wrapRunnable(Runnable runnable) { -+ // Paper start - anything that does try to post to main during watchdog crash, run on watchdog -+ if (this.hasStopped && Thread.currentThread().equals(shutdownThread)) { -+ runnable.run(); -+ runnable = () -> {}; -+ } -+ // Paper end - return new TickTask(this.tickCount, runnable); - } - -@@ -2175,7 +2231,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - this.worldData.setDataConfiguration(worlddataconfiguration); - this.resources.managers.updateRegistryTags(); - this.potionBrewing = this.potionBrewing.reload(this.worldData.enabledFeatures()); // Paper - Custom Potion Mixes -- this.getPlayerList().saveAll(); -+ // Paper start -+ if (Thread.currentThread() != this.serverThread) { -+ return; -+ } -+ // this.getPlayerList().saveAll(); // Paper - we don't need to save everything, just advancements // TODO Move this to a different patch -+ for (ServerPlayer player : this.getPlayerList().getPlayers()) { -+ player.getAdvancements().save(); -+ } -+ // Paper end - this.getPlayerList().reloadResources(); - this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary()); - this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager); -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index e365ed1be9739f57d0e1851f0593229dc1286796..5678414a608623a7698f72173fef87bde44f6aaa 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -325,7 +325,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - long j = Util.getNanos() - i; - String s = String.format(Locale.ROOT, "%.3fs", (double) j / 1.0E9D); - -- DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s); -+ //DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s); // Paper moved to after init - if (dedicatedserverproperties.announcePlayerAchievements != null) { - ((GameRules.BooleanValue) this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)).set(dedicatedserverproperties.announcePlayerAchievements, this.overworld()); // CraftBukkit - per-world - } -@@ -459,7 +459,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - // this.remoteStatusListener.stop(); // Paper - don't wait for remote connections - } - -- System.exit(0); // CraftBukkit -+ hasFullyShutdown = true; // Paper -+ System.exit(this.abnormalExit ? 70 : 0); // CraftBukkit // Paper - } - - @Override -@@ -840,7 +841,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - @Override - public void stopServer() { - super.stopServer(); -- Util.shutdownExecutors(); -+ //Util.shutdownExecutors(); // Paper - moved into super - SkullBlockEntity.clear(); - } - -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 29b35b8c0a6001d626e327a82eaff26d068660db..60ba289e724463129dfb27aa5e3b6daf3dd7386e 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -611,7 +611,7 @@ public abstract class PlayerList { - this.cserver.getPluginManager().callEvent(playerQuitEvent); - entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); - -- entityplayer.doTick(); // SPIGOT-924 -+ if (server.isSameThread()) entityplayer.doTick(); // SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog) - // CraftBukkit end - - // Paper start - Configurable player collision; Remove from collideRule team if needed -diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -index 2510589400b3012b827efcab477c6483d9d55901..43487a9ee202c5b0e5a416519939111f77b3059c 100644 ---- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -@@ -150,6 +150,7 @@ public abstract class BlockableEventLoop<R extends Runnable> implements Profiler - try { - task.run(); - } catch (Exception var3) { -+ if (var3.getCause() instanceof ThreadDeath) throw var3; // Paper - LOGGER.error(LogUtils.FATAL_MARKER, "Error executing task on {}", this.name(), var3); - } - } -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index ff8d947c5a498e097a4c480a5dc9af71b0be5ee9..ee49f59ba8ce5708fc5e244eba7b1f910cf69263 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -926,6 +926,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - try { - tickConsumer.accept(entity); - } catch (Throwable throwable) { -+ if (throwable instanceof ThreadDeath) throw throwable; // Paper - // Paper start - Prevent block entity and entity crashes - final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); - MinecraftServer.LOGGER.error(msg, throwable); -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 424c4613e202c6ba50fa0de65d2526d400a8e299..2a8609e33716949ff1877b6d10f64a9d7a7c81e9 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -1179,6 +1179,7 @@ public class LevelChunk extends ChunkAccess { - - gameprofilerfiller.pop(); - } catch (Throwable throwable) { -+ if (throwable instanceof ThreadDeath) throw throwable; // Paper - // Paper start - Prevent block entity and entity crashes - final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ()); - net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable); -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 9743c945d560b7a86122c7f8cd3ebd54b81189a3..b86ba59158964f73abd6622341a9acb98a33fa44 100644 ---- a/src/main/java/org/bukkit/craftbukkit/Main.java -+++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -188,6 +188,36 @@ public class Main { - - OptionSet options = null; - -+ // Paper start - preload logger classes to avoid plugins mixing versions -+ tryPreloadClass("org.apache.logging.log4j.core.Core"); -+ tryPreloadClass("org.apache.logging.log4j.core.appender.AsyncAppender"); -+ tryPreloadClass("org.apache.logging.log4j.core.Appender"); -+ tryPreloadClass("org.apache.logging.log4j.core.ContextDataInjector"); -+ tryPreloadClass("org.apache.logging.log4j.core.Filter"); -+ tryPreloadClass("org.apache.logging.log4j.core.ErrorHandler"); -+ tryPreloadClass("org.apache.logging.log4j.core.LogEvent"); -+ tryPreloadClass("org.apache.logging.log4j.core.Logger"); -+ tryPreloadClass("org.apache.logging.log4j.core.LoggerContext"); -+ tryPreloadClass("org.apache.logging.log4j.core.LogEventListener"); -+ tryPreloadClass("org.apache.logging.log4j.core.AbstractLogEvent"); -+ tryPreloadClass("org.apache.logging.log4j.message.AsynchronouslyFormattable"); -+ tryPreloadClass("org.apache.logging.log4j.message.FormattedMessage"); -+ tryPreloadClass("org.apache.logging.log4j.message.ParameterizedMessage"); -+ tryPreloadClass("org.apache.logging.log4j.message.Message"); -+ tryPreloadClass("org.apache.logging.log4j.message.MessageFactory"); -+ tryPreloadClass("org.apache.logging.log4j.message.TimestampMessage"); -+ tryPreloadClass("org.apache.logging.log4j.message.SimpleMessage"); -+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLogger"); -+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLoggerContext"); -+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncQueueFullPolicy"); -+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLoggerDisruptor"); -+ tryPreloadClass("org.apache.logging.log4j.core.async.RingBufferLogEvent"); -+ tryPreloadClass("org.apache.logging.log4j.core.async.DisruptorUtil"); -+ tryPreloadClass("org.apache.logging.log4j.core.async.RingBufferLogEventHandler"); -+ tryPreloadClass("org.apache.logging.log4j.core.impl.ThrowableProxy"); -+ tryPreloadClass("org.apache.logging.log4j.core.impl.ExtendedClassInfo"); -+ tryPreloadClass("org.apache.logging.log4j.core.impl.ExtendedStackTraceElement"); -+ // Paper end - try { - options = parser.parse(args); - } catch (joptsimple.OptionException ex) { -@@ -299,8 +329,65 @@ public class Main { - } catch (Throwable t) { - t.printStackTrace(); - } -+ // Paper start -+ // load some required classes to avoid errors during shutdown if jar is replaced -+ // also to guarantee our version loads over plugins -+ tryPreloadClass("com.destroystokyo.paper.util.SneakyThrow"); -+ tryPreloadClass("com.google.common.collect.Iterators$PeekingImpl"); -+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$Values"); -+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$ValueIterator"); -+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$WriteThroughEntry"); -+ tryPreloadClass("com.google.common.collect.Iterables"); -+ for (int i = 1; i <= 15; i++) { -+ tryPreloadClass("com.google.common.collect.Iterables$" + i, false); -+ } -+ tryPreloadClass("org.apache.commons.lang3.mutable.MutableBoolean"); -+ tryPreloadClass("org.apache.commons.lang3.mutable.MutableInt"); -+ tryPreloadClass("org.jline.terminal.impl.MouseSupport"); -+ tryPreloadClass("org.jline.terminal.impl.MouseSupport$1"); -+ tryPreloadClass("org.jline.terminal.Terminal$MouseTracking"); -+ tryPreloadClass("co.aikar.timings.TimingHistory"); -+ tryPreloadClass("co.aikar.timings.TimingHistory$MinuteReport"); -+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext"); -+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext$11"); -+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext$12"); -+ tryPreloadClass("io.netty.channel.AbstractChannel$AbstractUnsafe$8"); -+ tryPreloadClass("io.netty.util.concurrent.DefaultPromise"); -+ tryPreloadClass("io.netty.util.concurrent.DefaultPromise$1"); -+ tryPreloadClass("io.netty.util.internal.PromiseNotificationUtil"); -+ tryPreloadClass("io.netty.util.internal.SystemPropertyUtil"); -+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler"); -+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$1"); -+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$2"); -+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$3"); -+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$4"); -+ tryPreloadClass("org.slf4j.helpers.MessageFormatter"); -+ tryPreloadClass("org.slf4j.helpers.FormattingTuple"); -+ tryPreloadClass("org.slf4j.helpers.BasicMarker"); -+ tryPreloadClass("org.slf4j.helpers.Util"); -+ tryPreloadClass("com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent"); -+ tryPreloadClass("com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent"); -+ // Minecraft, seen during saving -+ tryPreloadClass(net.minecraft.world.level.lighting.LayerLightEventListener.DummyLightLayerEventListener.class.getName()); -+ tryPreloadClass(net.minecraft.world.level.lighting.LayerLightEventListener.class.getName()); -+ tryPreloadClass(net.minecraft.util.ExceptionCollector.class.getName()); -+ tryPreloadClass(io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.PlayerChunkLoaderData.class.getName()); -+ // Paper end -+ } -+ } -+ -+ // Paper start -+ private static void tryPreloadClass(String className) { -+ tryPreloadClass(className, true); -+ } -+ private static void tryPreloadClass(String className, boolean printError) { -+ try { -+ Class.forName(className); -+ } catch (ClassNotFoundException e) { -+ if (printError) System.err.println("An expected class " + className + " was not found for preloading: " + e.getMessage()); - } - } -+ // Paper end - - private static List<String> asList(String... params) { - return Arrays.asList(params); -diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java -index c6e8441e299f477ddb22c1ce2618710763978f1a..e8e93538dfd71de86515d9405f728db1631e949a 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java -@@ -12,11 +12,27 @@ public class ServerShutdownThread extends Thread { - @Override - public void run() { - try { -+ // Paper start - try to shutdown on main -+ server.safeShutdown(false, false); -+ for (int i = 1000; i > 0 && !server.hasStopped(); i -= 100) { -+ Thread.sleep(100); -+ } -+ if (server.hasStopped()) { -+ while (!server.hasFullyShutdown) Thread.sleep(1000); -+ return; -+ } -+ // Looks stalled, close async - org.spigotmc.AsyncCatcher.enabled = false; // Spigot -+ server.forceTicks = true; - this.server.close(); -+ while (!server.hasFullyShutdown) Thread.sleep(1000); -+ } catch (InterruptedException e) { -+ e.printStackTrace(); -+ // Paper end - } finally { -+ org.apache.logging.log4j.LogManager.shutdown(); // Paper - try { -- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender -+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop - } catch (Exception e) { - } - } -diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java -index e3b262add194a126e731c68e68f3139a00cacacb..da7d5efd76c9ef92e9ce22860fec791890a687be 100644 ---- a/src/main/java/org/spigotmc/RestartCommand.java -+++ b/src/main/java/org/spigotmc/RestartCommand.java -@@ -138,7 +138,7 @@ public class RestartCommand extends Command - // Paper end - - // Paper start - copied from above and modified to return if the hook registered -- private static boolean addShutdownHook(String restartScript) -+ public static boolean addShutdownHook(String restartScript) - { - String[] split = restartScript.split( " " ); - if ( split.length > 0 && new File( split[0] ).isFile() ) -diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java -index 9e5d08f57aa448552d100ca892c211d44441ef68..577f29519156f33b49ee319a64a52249630e28d8 100644 ---- a/src/main/java/org/spigotmc/WatchdogThread.java -+++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -11,6 +11,7 @@ import org.bukkit.Bukkit; - public final class WatchdogThread extends io.papermc.paper.util.TickThread // Paper - rewrite chunk system - { - -+ public static final boolean DISABLE_WATCHDOG = Boolean.getBoolean("disable.watchdog"); // Paper - private static WatchdogThread instance; - private long timeoutTime; - private boolean restart; -@@ -39,6 +40,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - { - if ( WatchdogThread.instance == null ) - { -+ if (timeoutTime <= 0) timeoutTime = 300; // Paper - WatchdogThread.instance = new WatchdogThread( timeoutTime * 1000L, restart ); - WatchdogThread.instance.start(); - } else -@@ -70,12 +72,13 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - // Paper start - Logger log = Bukkit.getServer().getLogger(); - long currentTime = WatchdogThread.monotonicMillis(); -- if ( this.lastTick != 0 && this.timeoutTime > 0 && currentTime > this.lastTick + this.earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable -+ MinecraftServer server = MinecraftServer.getServer(); -+ if ( this.lastTick != 0 && this.timeoutTime > 0 && WatchdogThread.hasStarted && (!server.isRunning() || (currentTime > this.lastTick + this.earlyWarningEvery && !DISABLE_WATCHDOG) )) // Paper - add property to disable - { -- boolean isLongTimeout = currentTime > lastTick + timeoutTime; -+ boolean isLongTimeout = currentTime > lastTick + timeoutTime || (!server.isRunning() && !server.hasStopped() && currentTime > lastTick + 1000); - // Don't spam early warning dumps - if ( !isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay)) continue; -- if ( !isLongTimeout && MinecraftServer.getServer().hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this... -+ if ( !isLongTimeout && server.hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this... - lastEarlyWarning = currentTime; - if (isLongTimeout) { - // Paper end -@@ -136,9 +139,24 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - - if ( isLongTimeout ) - { -- if ( this.restart && !MinecraftServer.getServer().hasStopped() ) -+ if ( !server.hasStopped() ) - { -- RestartCommand.restart(); -+ AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us -+ server.forceTicks = true; -+ if (restart) { -+ RestartCommand.addShutdownHook( SpigotConfig.restartScript ); -+ } -+ // try one last chance to safe shutdown on main incase it 'comes back' -+ server.abnormalExit = true; -+ server.safeShutdown(false, restart); -+ try { -+ Thread.sleep(1000); -+ } catch (InterruptedException e) { -+ e.printStackTrace(); -+ } -+ if (!server.hasStopped()) { -+ server.close(); -+ } - } - break; - } // Paper end -diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml -index 637d64da9938e51a97338b9253b43889585c67bb..d2a75850af9c6ad2aca66a5f994f1b587d73eac4 100644 ---- a/src/main/resources/log4j2.xml -+++ b/src/main/resources/log4j2.xml -@@ -1,5 +1,5 @@ - <?xml version="1.0" encoding="UTF-8"?> --<Configuration status="WARN"> -+<Configuration status="WARN" shutdownHook="disable"> - <Appenders> - <Queue name="ServerGuiConsole"> - <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" /> |