2013-09-20 03:46:45 +02:00
|
|
|
From 3cd709b7763070249f9d6c8c7c805a227de91696 Mon Sep 17 00:00:00 2001
|
2013-02-23 02:37:58 +01:00
|
|
|
From: md_5 <md_5@live.com.au>
|
|
|
|
Date: Sat, 23 Feb 2013 12:33:20 +1100
|
|
|
|
Subject: [PATCH] Watchdog Thread.
|
|
|
|
|
|
|
|
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
2013-09-20 03:46:45 +02:00
|
|
|
index fba219f..e5b8391 100644
|
2013-02-23 02:37:58 +01:00
|
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
2013-09-20 03:46:45 +02:00
|
|
|
@@ -415,6 +415,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
|
2013-07-02 05:03:56 +02:00
|
|
|
this.s();
|
2013-02-26 18:21:40 +01:00
|
|
|
SpigotTimings.serverTickTimer.stopTiming();
|
2013-06-02 03:29:44 +02:00
|
|
|
org.spigotmc.CustomTimingsHandler.tick();
|
2013-02-23 02:37:58 +01:00
|
|
|
+ org.spigotmc.WatchdogThread.tick();
|
|
|
|
}
|
|
|
|
// Spigot end
|
|
|
|
} else {
|
2013-09-20 03:46:45 +02:00
|
|
|
@@ -442,6 +443,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
|
2013-02-23 02:37:58 +01:00
|
|
|
this.a(crashreport);
|
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
+ org.spigotmc.WatchdogThread.doStop();
|
|
|
|
this.stop();
|
|
|
|
this.isStopped = true;
|
|
|
|
} catch (Throwable throwable1) {
|
2013-06-20 11:07:32 +02:00
|
|
|
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
|
|
|
|
new file mode 100644
|
2013-07-02 05:36:58 +02:00
|
|
|
index 0000000..c8125c2
|
2013-06-20 11:07:32 +02:00
|
|
|
--- /dev/null
|
|
|
|
+++ b/src/main/java/org/spigotmc/RestartCommand.java
|
|
|
|
@@ -0,0 +1,111 @@
|
|
|
|
+package org.spigotmc;
|
|
|
|
+
|
2013-02-23 02:37:58 +01:00
|
|
|
+import java.io.File;
|
2013-06-20 11:07:32 +02:00
|
|
|
+import java.util.List;
|
2013-04-25 01:01:05 +02:00
|
|
|
+import net.minecraft.server.EntityPlayer;
|
2013-06-20 11:07:32 +02:00
|
|
|
+import net.minecraft.server.MinecraftServer;
|
2013-04-25 01:01:05 +02:00
|
|
|
+import net.minecraft.server.Packet255KickDisconnect;
|
2013-06-20 11:07:32 +02:00
|
|
|
+import org.bukkit.command.Command;
|
|
|
|
+import org.bukkit.command.CommandSender;
|
2013-02-23 02:37:58 +01:00
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+public class RestartCommand extends Command
|
|
|
|
+{
|
2013-02-23 02:37:58 +01:00
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ 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 ) )
|
|
|
|
+ {
|
|
|
|
+ restart();
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static void restart()
|
|
|
|
+ {
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ final File file = new File( SpigotConfig.restartScript );
|
|
|
|
+ if ( file.isFile() )
|
|
|
|
+ {
|
|
|
|
+ System.out.println( "Attempting to restart with " + SpigotConfig.restartScript );
|
2013-02-23 02:37:58 +01:00
|
|
|
+
|
|
|
|
+ // Kick all players
|
2013-06-20 11:07:32 +02:00
|
|
|
+ for ( EntityPlayer p : (List< EntityPlayer>) MinecraftServer.getServer().getPlayerList().players )
|
|
|
|
+ {
|
|
|
|
+ p.playerConnection.networkManager.queue( new Packet255KickDisconnect( "Server is restarting" ) );
|
2013-03-28 08:38:42 +01:00
|
|
|
+ p.playerConnection.networkManager.d();
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
|
|
|
+ // Give the socket a chance to send the packets
|
2013-06-20 11:07:32 +02:00
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ Thread.sleep( 100 );
|
|
|
|
+ } catch ( InterruptedException ex )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
|
|
|
+ // Close the socket so we can rebind with the new process
|
2013-07-02 05:36:58 +02:00
|
|
|
+ MinecraftServer.getServer().ag().a();
|
2013-02-23 02:37:58 +01:00
|
|
|
+
|
|
|
|
+ // Give time for it to kick in
|
2013-06-20 11:07:32 +02:00
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ Thread.sleep( 100 );
|
|
|
|
+ } catch ( InterruptedException ex )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Actually shutdown
|
2013-06-20 11:07:32 +02:00
|
|
|
+ try
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ MinecraftServer.getServer().stop();
|
2013-06-20 11:07:32 +02:00
|
|
|
+ } catch ( Throwable t )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
|
|
|
+
|
2013-02-24 01:30:30 +01:00
|
|
|
+ // This will be done AFTER the server has completely halted
|
2013-06-20 11:07:32 +02:00
|
|
|
+ Thread shutdownHook = new Thread()
|
|
|
|
+ {
|
2013-02-24 01:30:30 +01:00
|
|
|
+ @Override
|
2013-06-20 11:07:32 +02:00
|
|
|
+ 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()
|
|
|
|
+ } );
|
2013-02-24 01:30:30 +01:00
|
|
|
+ }
|
2013-06-20 11:07:32 +02:00
|
|
|
+ } catch ( Exception e )
|
|
|
|
+ {
|
2013-02-24 01:30:30 +01:00
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ shutdownHook.setDaemon( true );
|
|
|
|
+ Runtime.getRuntime().addShutdownHook( shutdownHook );
|
|
|
|
+ } else
|
|
|
|
+ {
|
|
|
|
+ System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
2013-06-20 11:07:32 +02:00
|
|
|
+ System.exit( 0 );
|
|
|
|
+ } catch ( Exception ex )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ ex.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ }
|
2013-06-20 11:07:32 +02:00
|
|
|
+}
|
|
|
|
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
|
2013-08-03 11:15:22 +02:00
|
|
|
index 8ef108d..8499c7f 100644
|
2013-06-20 11:07:32 +02:00
|
|
|
--- a/src/main/java/org/spigotmc/SpigotConfig.java
|
|
|
|
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
|
2013-07-13 03:37:21 +02:00
|
|
|
@@ -142,4 +142,16 @@ public class SpigotConfig
|
2013-06-20 11:07:32 +02:00
|
|
|
{
|
2013-06-21 10:25:45 +02:00
|
|
|
commands.put( "tps", new TicksPerSecondCommand( "tps" ) );
|
2013-06-20 11:07:32 +02:00
|
|
|
}
|
2013-04-23 03:27:55 +02:00
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ public static int timeoutTime = 60;
|
|
|
|
+ public static boolean restartOnCrash = true;
|
|
|
|
+ public static String restartScript = "./start.sh";
|
|
|
|
+ private static void watchdog()
|
|
|
|
+ {
|
|
|
|
+ timeoutTime = getInt( "settings.timeout-time", timeoutTime );
|
|
|
|
+ restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash );
|
|
|
|
+ restartScript = getString( "settings.restart-script", restartScript );
|
2013-06-21 10:25:45 +02:00
|
|
|
+ commands.put( "restart", new RestartCommand( "restart" ) );
|
2013-06-20 11:07:32 +02:00
|
|
|
+ WatchdogThread.doStart( timeoutTime, restartOnCrash );
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
2013-06-20 11:07:32 +02:00
|
|
|
}
|
2013-02-23 02:37:58 +01:00
|
|
|
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
|
|
new file mode 100644
|
2013-08-09 12:34:05 +02:00
|
|
|
index 0000000..0a646d6
|
2013-02-23 02:37:58 +01:00
|
|
|
--- /dev/null
|
|
|
|
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
2013-08-03 11:15:22 +02:00
|
|
|
@@ -0,0 +1,122 @@
|
2013-02-23 02:37:58 +01:00
|
|
|
+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;
|
2013-08-03 11:15:22 +02:00
|
|
|
+import net.minecraft.server.MinecraftServer;
|
2013-02-23 02:37:58 +01:00
|
|
|
+import org.bukkit.Bukkit;
|
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+public class WatchdogThread extends Thread
|
|
|
|
+{
|
2013-02-23 02:37:58 +01:00
|
|
|
+
|
|
|
|
+ private static WatchdogThread instance;
|
|
|
|
+ private final long timeoutTime;
|
|
|
|
+ private final boolean restart;
|
|
|
|
+ private volatile long lastTick;
|
|
|
|
+ private volatile boolean stopping;
|
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ private WatchdogThread(long timeoutTime, boolean restart)
|
|
|
|
+ {
|
|
|
|
+ super( "Spigot Watchdog Thread" );
|
2013-02-23 02:37:58 +01:00
|
|
|
+ this.timeoutTime = timeoutTime;
|
|
|
|
+ this.restart = restart;
|
|
|
|
+ }
|
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ public static void doStart(int timeoutTime, boolean restart)
|
|
|
|
+ {
|
|
|
|
+ if ( instance == null )
|
|
|
|
+ {
|
|
|
|
+ instance = new WatchdogThread( timeoutTime * 1000L, restart );
|
2013-02-23 02:37:58 +01:00
|
|
|
+ instance.start();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ public static void tick()
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ instance.lastTick = System.currentTimeMillis();
|
|
|
|
+ }
|
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ public static void doStop()
|
|
|
|
+ {
|
|
|
|
+ if ( instance != null )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ instance.stopping = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
2013-06-20 11:07:32 +02:00
|
|
|
+ public void run()
|
|
|
|
+ {
|
|
|
|
+ while ( !stopping )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ //
|
2013-06-20 11:07:32 +02:00
|
|
|
+ if ( lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ Logger log = Bukkit.getServer().getLogger();
|
2013-06-20 11:07:32 +02:00
|
|
|
+ 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() );
|
2013-02-23 02:37:58 +01:00
|
|
|
+ //
|
2013-08-03 11:15:22 +02:00
|
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
|
|
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
|
2013-08-09 12:34:05 +02:00
|
|
|
+ dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE ), log );
|
2013-08-03 11:15:22 +02:00
|
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
|
|
+ //
|
|
|
|
+ log.log( Level.SEVERE, "Entire Thread Dump:" );
|
|
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
2013-06-20 11:07:32 +02:00
|
|
|
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true );
|
|
|
|
+ for ( ThreadInfo thread : threads )
|
|
|
|
+ {
|
2013-08-03 11:15:22 +02:00
|
|
|
+ dumpThread( thread, log );
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
2013-06-20 11:07:32 +02:00
|
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
2013-02-23 02:37:58 +01:00
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ if ( restart )
|
|
|
|
+ {
|
|
|
|
+ RestartCommand.restart();
|
2013-02-23 02:37:58 +01:00
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
2013-06-20 11:07:32 +02:00
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ sleep( 10000 );
|
|
|
|
+ } catch ( InterruptedException ex )
|
|
|
|
+ {
|
2013-02-23 02:37:58 +01:00
|
|
|
+ interrupt();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
2013-08-03 11:15:22 +02:00
|
|
|
+
|
|
|
|
+ private static void dumpThread(ThreadInfo thread, Logger log)
|
|
|
|
+ {
|
|
|
|
+ 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() );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
2013-02-23 02:37:58 +01:00
|
|
|
+}
|
|
|
|
--
|
2013-06-02 03:29:44 +02:00
|
|
|
1.8.1.2
|
2013-02-23 02:37:58 +01:00
|
|
|
|