PaperMC/CraftBukkit-Patches/0028-Watchdog-Thread.patch
Aikar 577140d9ab Optimize checkIfActive to only check if chunks are loaded if entity is active
This will cut down on the call to areChunksLoaded drastically, which is pretty slow
2013-02-27 19:10:22 +11:00

301 lines
12 KiB
Diff

From 51ae704ad3290541373ec51570d48c9e66743f5c Mon Sep 17 00:00:00 2001
From: md_5 <md_5@live.com.au>
Date: Sat, 23 Feb 2013 12:33:20 +1100
Subject: [PATCH] Watchdog Thread.
---
.../java/net/minecraft/server/MinecraftServer.java | 2 +
src/main/java/org/bukkit/craftbukkit/Spigot.java | 78 +++++++++++++++++-
src/main/java/org/spigotmc/RestartCommand.java | 23 ++++++
src/main/java/org/spigotmc/WatchdogThread.java | 93 ++++++++++++++++++++++
src/main/resources/configurations/bukkit.yml | 3 +
5 files changed, 197 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/org/spigotmc/RestartCommand.java
create mode 100644 src/main/java/org/spigotmc/WatchdogThread.java
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index aa6a14a..6005fac 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -421,6 +421,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
this.q();
SpigotTimings.serverTickTimer.stopTiming();
org.bukkit.CustomTimingsHandler.tick();
+ org.spigotmc.WatchdogThread.tick();
}
// Spigot end
} else {
@@ -448,6 +449,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
this.a(crashreport);
} finally {
try {
+ org.spigotmc.WatchdogThread.doStop();
this.stop();
this.isStopped = true;
} catch (Throwable throwable1) {
diff --git a/src/main/java/org/bukkit/craftbukkit/Spigot.java b/src/main/java/org/bukkit/craftbukkit/Spigot.java
index 83018b0..931f6fd 100644
--- a/src/main/java/org/bukkit/craftbukkit/Spigot.java
+++ b/src/main/java/org/bukkit/craftbukkit/Spigot.java
@@ -1,5 +1,6 @@
package org.bukkit.craftbukkit;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import net.minecraft.server.*;
@@ -8,9 +9,11 @@ import org.bukkit.configuration.file.YamlConfiguration;
import java.util.List;
import java.util.logging.Level;
-import java.util.logging.Logger;
import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
import org.spigotmc.Metrics;
+import org.spigotmc.RestartCommand;
+import org.spigotmc.WatchdogThread;
public class Spigot {
static AxisAlignedBB maxBB = AxisAlignedBB.a(0,0,0,0,0,0);
@@ -23,6 +26,7 @@ public class Spigot {
public static void initialize(CraftServer server, SimpleCommandMap commandMap, YamlConfiguration configuration) {
commandMap.register("bukkit", new org.bukkit.craftbukkit.command.TicksPerSecondCommand("tps"));
+ commandMap.register("restart", new RestartCommand("restart"));
server.whitelistMessage = configuration.getString("settings.whitelist-message", server.whitelistMessage);
server.stopMessage = configuration.getString("settings.stop-message", server.stopMessage);
@@ -31,12 +35,23 @@ public class Spigot {
server.commandComplete = configuration.getBoolean("settings.command-complete", true);
server.spamGuardExclusions = configuration.getStringList("settings.spam-exclusions");
+ int configVersion = configuration.getInt("config-version");
+ switch (configVersion) {
+ case 0:
+ configuration.set("settings.timeout-time", 30);
+ case 1:
+ configuration.set("settings.timeout-time", 60);
+ }
+ configuration.set("config-version", 2);
+
+ WatchdogThread.doStart(configuration.getInt("settings.timeout-time", 60), configuration.getBoolean("settings.restart-on-crash", false));
+
server.orebfuscatorEnabled = configuration.getBoolean("orebfuscator.enable", false);
server.orebfuscatorEngineMode = configuration.getInt("orebfuscator.engine-mode", 1);
server.orebfuscatorUpdateRadius = configuration.getInt("orebfuscator.update-radius", 2);
server.orebfuscatorDisabledWorlds = configuration.getStringList("orebfuscator.disabled-worlds");
if (server.orebfuscatorEngineMode != 1 && server.orebfuscatorEngineMode != 2) {
- server.orebfuscatorEngineMode = 1;
+ server.orebfuscatorEngineMode = 1;
}
if (server.chunkGCPeriod == 0) {
@@ -264,4 +279,63 @@ public class Spigot {
SpigotTimings.checkIfActiveTimer.stopTiming();
return isActive;
}
+
+ public static void restart() {
+ try {
+ String startupScript = MinecraftServer.getServer().server.configuration.getString("settings.restart-script-location", "");
+ final File file = new File(startupScript);
+ if (file.isFile()) {
+ System.out.println("Attempting to restart with " + startupScript);
+
+ // Kick all players
+ for (Player p : Bukkit.getServer().getOnlinePlayers()) {
+ ((org.bukkit.craftbukkit.entity.CraftPlayer) p).kickPlayer("Server is restarting", true);
+ }
+ // Give the socket a chance to send the packets
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ }
+ // Close the socket so we can rebind with the new process
+ MinecraftServer.getServer().ae().a();
+
+ // Give time for it to kick in
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ }
+
+ // Actually shutdown
+ try {
+ MinecraftServer.getServer().stop();
+ } catch (Throwable t) {
+ }
+
+ // This will be done AFTER the server has completely halted
+ Thread shutdownHook = new Thread() {
+ @Override
+ public void run(){
+ try {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.contains("win")) {
+ Runtime.getRuntime().exec("cmd /c start " + file.getPath());
+ } else {
+ Runtime.getRuntime().exec(new String[] { "sh", file.getPath()});
+ }
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ };
+
+ shutdownHook.setDaemon(true);
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
+ System.exit(0);
+ } else {
+ System.out.println("Startup script '" + startupScript + "' does not exist!");
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
}
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
new file mode 100644
index 0000000..2d5c89f
--- /dev/null
+++ b/src/main/java/org/spigotmc/RestartCommand.java
@@ -0,0 +1,23 @@
+package org.spigotmc;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.Spigot;
+
+public class RestartCommand extends Command {
+
+ public RestartCommand(String name) {
+ super(name);
+ this.description = "Restarts the server";
+ this.usageMessage = "/restart";
+ this.setPermission("bukkit.command.restart");
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String currentAlias, String[] args) {
+ if (testPermission(sender)) {
+ Spigot.restart();
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
new file mode 100644
index 0000000..10390b8
--- /dev/null
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -0,0 +1,93 @@
+package org.spigotmc;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.Spigot;
+
+public class WatchdogThread extends Thread {
+
+ private static WatchdogThread instance;
+ private final long timeoutTime;
+ private final boolean restart;
+ private volatile long lastTick;
+ private volatile boolean stopping;
+
+ private WatchdogThread(long timeoutTime, boolean restart) {
+ super("Spigot Watchdog Thread");
+ this.timeoutTime = timeoutTime;
+ this.restart = restart;
+ }
+
+ public static void doStart(int timeoutTime, boolean restart) {
+ if (instance == null) {
+ instance = new WatchdogThread(timeoutTime * 1000L, restart);
+ instance.start();
+ }
+ }
+
+ public static void tick() {
+ instance.lastTick = System.currentTimeMillis();
+ }
+
+ public static void doStop() {
+ if (instance != null) {
+ instance.stopping = true;
+ }
+ }
+
+ @Override
+ public void run() {
+ while (!stopping) {
+ //
+ if (lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime) {
+ Logger log = Bukkit.getServer().getLogger();
+ log.log(Level.SEVERE, "The server has stopped responding!");
+ log.log(Level.SEVERE, "Please report this to http://www.spigotmc.org/");
+ log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
+ log.log(Level.SEVERE, "Spigot version: " + Bukkit.getServer().getVersion());
+ //
+ log.log(Level.SEVERE, "Current Thread State:");
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
+ for (ThreadInfo thread : threads) {
+ if (thread.getThreadState() != State.WAITING) {
+ log.log(Level.SEVERE, "------------------------------");
+ //
+ log.log(Level.SEVERE, "Current Thread: " + thread.getThreadName());
+ log.log(Level.SEVERE, "\tPID: " + thread.getThreadId()
+ + " | Suspended: " + thread.isSuspended()
+ + " | Native: " + thread.isInNative()
+ + " | State: " + thread.getThreadState());
+ if (thread.getLockedMonitors().length != 0) {
+ log.log(Level.SEVERE, "\tThread is waiting on monitor(s):");
+ for (MonitorInfo monitor : thread.getLockedMonitors()) {
+ log.log(Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame());
+ }
+ }
+ log.log(Level.SEVERE, "\tStack:");
+ //
+ StackTraceElement[] stack = thread.getStackTrace();
+ for (int line = 0; line < stack.length; line++) {
+ log.log(Level.SEVERE, "\t\t" + stack[line].toString());
+ }
+ }
+ }
+ log.log(Level.SEVERE, "------------------------------");
+
+ if (restart) {
+ Spigot.restart();
+ }
+ break;
+ }
+
+ try {
+ sleep(10000);
+ } catch (InterruptedException ex) {
+ interrupt();
+ }
+ }
+ }
+}
diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml
index e568bf6..7c18391 100644
--- a/src/main/resources/configurations/bukkit.yml
+++ b/src/main/resources/configurations/bukkit.yml
@@ -31,6 +31,9 @@ settings:
command-complete: true
spam-exclusions:
- /skill
+ timeout-time: 30
+ restart-on-crash: false
+ restart-script-location: /path/to/server/start.sh
world-settings:
default:
growth-chunks-per-tick: 650
--
1.8.1.1