562 lines
20 KiB
Diff
562 lines
20 KiB
Diff
From 91e137f00b53a82e6acc86c8eb6f773e79aa8875 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.
|
|
|
|
|
|
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
|
|
new file mode 100644
|
|
index 0000000..d7b8891
|
|
--- /dev/null
|
|
+++ b/dependency-reduced-pom.xml
|
|
@@ -0,0 +1,262 @@
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
+ <parent>
|
|
+ <artifactId>spigot-parent</artifactId>
|
|
+ <groupId>org.spigotmc</groupId>
|
|
+ <version>dev-SNAPSHOT</version>
|
|
+ </parent>
|
|
+ <modelVersion>4.0.0</modelVersion>
|
|
+ <groupId>org.spigotmc</groupId>
|
|
+ <artifactId>spigot</artifactId>
|
|
+ <name>Spigot</name>
|
|
+ <version>1.7.9-R0.1-SNAPSHOT</version>
|
|
+ <url>http://www.spigotmc.org</url>
|
|
+ <build>
|
|
+ <defaultGoal>install</defaultGoal>
|
|
+ <pluginManagement>
|
|
+ <plugins>
|
|
+ <plugin>
|
|
+ <groupId>org.eclipse.m2e</groupId>
|
|
+ <artifactId>lifecycle-mapping</artifactId>
|
|
+ <version>1.0.0</version>
|
|
+ <configuration>
|
|
+ <lifecycleMappingMetadata>
|
|
+ <pluginExecutions>
|
|
+ <pluginExecution>
|
|
+ <pluginExecutionFilter>
|
|
+ <groupId>com.lukegb.mojo</groupId>
|
|
+ <artifactId>gitdescribe-maven-plugin</artifactId>
|
|
+ <versionRange>[1.3,)</versionRange>
|
|
+ <goals>
|
|
+ <goal>gitdescribe</goal>
|
|
+ </goals>
|
|
+ </pluginExecutionFilter>
|
|
+ <action>
|
|
+ <ignore />
|
|
+ </action>
|
|
+ </pluginExecution>
|
|
+ </pluginExecutions>
|
|
+ </lifecycleMappingMetadata>
|
|
+ </configuration>
|
|
+ </plugin>
|
|
+ </plugins>
|
|
+ </pluginManagement>
|
|
+ <plugins>
|
|
+ <plugin>
|
|
+ <groupId>com.lukegb.mojo</groupId>
|
|
+ <artifactId>gitdescribe-maven-plugin</artifactId>
|
|
+ <version>1.3</version>
|
|
+ <executions>
|
|
+ <execution>
|
|
+ <phase>compile</phase>
|
|
+ <goals>
|
|
+ <goal>gitdescribe</goal>
|
|
+ </goals>
|
|
+ </execution>
|
|
+ </executions>
|
|
+ <configuration>
|
|
+ <outputPrefix>git-Spigot-</outputPrefix>
|
|
+ <outputPostfix />
|
|
+ </configuration>
|
|
+ </plugin>
|
|
+ <plugin>
|
|
+ <artifactId>maven-jar-plugin</artifactId>
|
|
+ <version>2.1</version>
|
|
+ <configuration>
|
|
+ <archive>
|
|
+ <manifestEntries>
|
|
+ <Main-Class>org.bukkit.craftbukkit.Main</Main-Class>
|
|
+ <Implementation-Title>CraftBukkit</Implementation-Title>
|
|
+ <Implementation-Version>${describe}</Implementation-Version>
|
|
+ <Implementation-Vendor>Bukkit Team</Implementation-Vendor>
|
|
+ <Specification-Title>Bukkit</Specification-Title>
|
|
+ <Specification-Version>${api.version}</Specification-Version>
|
|
+ <Specification-Vendor>Bukkit Team</Specification-Vendor>
|
|
+ <Sealed>true</Sealed>
|
|
+ </manifestEntries>
|
|
+ <manifestSections>
|
|
+ <manifestSection>
|
|
+ <name>net/bukkit/</name>
|
|
+ <manifestEntries>
|
|
+ <Sealed>true</Sealed>
|
|
+ </manifestEntries>
|
|
+ </manifestSection>
|
|
+ <manifestSection>
|
|
+ <name>com/bukkit/</name>
|
|
+ <manifestEntries>
|
|
+ <Sealed>true</Sealed>
|
|
+ </manifestEntries>
|
|
+ </manifestSection>
|
|
+ <manifestSection>
|
|
+ <name>org/bukkit/</name>
|
|
+ <manifestEntries>
|
|
+ <Sealed>true</Sealed>
|
|
+ </manifestEntries>
|
|
+ </manifestSection>
|
|
+ </manifestSections>
|
|
+ </archive>
|
|
+ </configuration>
|
|
+ </plugin>
|
|
+ <plugin>
|
|
+ <artifactId>maven-shade-plugin</artifactId>
|
|
+ <version>2.1</version>
|
|
+ <executions>
|
|
+ <execution>
|
|
+ <phase>package</phase>
|
|
+ <goals>
|
|
+ <goal>shade</goal>
|
|
+ </goals>
|
|
+ <configuration>
|
|
+ <relocations>
|
|
+ <relocation>
|
|
+ <pattern>org.bouncycastle</pattern>
|
|
+ <shadedPattern>net.minecraft.v${minecraft_version}.org.bouncycastle</shadedPattern>
|
|
+ </relocation>
|
|
+ <relocation>
|
|
+ <pattern>joptsimple</pattern>
|
|
+ <shadedPattern>org.bukkit.craftbukkit.libs.joptsimple</shadedPattern>
|
|
+ </relocation>
|
|
+ <relocation>
|
|
+ <pattern>jline</pattern>
|
|
+ <shadedPattern>org.bukkit.craftbukkit.libs.jline</shadedPattern>
|
|
+ </relocation>
|
|
+ <relocation>
|
|
+ <pattern>org.ibex</pattern>
|
|
+ <shadedPattern>org.bukkit.craftbukkit.libs.org.ibex</shadedPattern>
|
|
+ </relocation>
|
|
+ <relocation>
|
|
+ <pattern>org.gjt</pattern>
|
|
+ <shadedPattern>org.bukkit.craftbukkit.libs.org.gjt</shadedPattern>
|
|
+ </relocation>
|
|
+ <relocation>
|
|
+ <pattern>com.google.gson</pattern>
|
|
+ <shadedPattern>org.bukkit.craftbukkit.libs.com.google.gson</shadedPattern>
|
|
+ </relocation>
|
|
+ <relocation>
|
|
+ <pattern>org.bukkit.craftbukkit</pattern>
|
|
+ <shadedPattern>org.bukkit.craftbukkit.v${minecraft_version}</shadedPattern>
|
|
+ <excludes>
|
|
+ <exclude>org.bukkit.craftbukkit.Main*</exclude>
|
|
+ </excludes>
|
|
+ </relocation>
|
|
+ <relocation>
|
|
+ <pattern>net.minecraft.server</pattern>
|
|
+ <shadedPattern>net.minecraft.server.v${minecraft_version}</shadedPattern>
|
|
+ </relocation>
|
|
+ </relocations>
|
|
+ </configuration>
|
|
+ </execution>
|
|
+ </executions>
|
|
+ </plugin>
|
|
+ <plugin>
|
|
+ <artifactId>maven-compiler-plugin</artifactId>
|
|
+ <version>2.3.2</version>
|
|
+ <configuration>
|
|
+ <source>1.6</source>
|
|
+ <target>1.6</target>
|
|
+ </configuration>
|
|
+ </plugin>
|
|
+ <plugin>
|
|
+ <artifactId>maven-surefire-plugin</artifactId>
|
|
+ <version>2.11</version>
|
|
+ <configuration>
|
|
+ <workingDirectory>${basedir}/target/test-server</workingDirectory>
|
|
+ <excludes>
|
|
+ <exclude>org/bukkit/craftbukkit/updater/BukkitDLUpdaterServiceTest.java</exclude>
|
|
+ <exclude>org/bukkit/craftbukkit/inventory/ItemStack*Test.java</exclude>
|
|
+ </excludes>
|
|
+ </configuration>
|
|
+ </plugin>
|
|
+ <plugin>
|
|
+ <artifactId>maven-resources-plugin</artifactId>
|
|
+ <version>2.4.3</version>
|
|
+ <executions>
|
|
+ <execution>
|
|
+ <id>maps-file</id>
|
|
+ <phase>package</phase>
|
|
+ <goals>
|
|
+ <goal>copy-resources</goal>
|
|
+ </goals>
|
|
+ <configuration>
|
|
+ <outputDirectory>${basedir}/target</outputDirectory>
|
|
+ <resources>
|
|
+ <resource>
|
|
+ <directory>${basedir}</directory>
|
|
+ <filtering>true</filtering>
|
|
+ <includes>
|
|
+ <include>maps.yml</include>
|
|
+ </includes>
|
|
+ </resource>
|
|
+ </resources>
|
|
+ </configuration>
|
|
+ </execution>
|
|
+ </executions>
|
|
+ </plugin>
|
|
+ <plugin>
|
|
+ <groupId>com.wolvereness</groupId>
|
|
+ <artifactId>overmapped</artifactId>
|
|
+ <version>0.0.2</version>
|
|
+ <executions>
|
|
+ <execution>
|
|
+ <phase>package</phase>
|
|
+ <goals>
|
|
+ <goal>map</goal>
|
|
+ </goals>
|
|
+ <configuration>
|
|
+ <maps>${basedir}/target/maps.yml</maps>
|
|
+ <input>${basedir}/target/${project.artifactId}-${project.version}.jar</input>
|
|
+ <original>${basedir}/target/unmapped-${project.artifactId}-${project.version}.jar</original>
|
|
+ </configuration>
|
|
+ </execution>
|
|
+ </executions>
|
|
+ </plugin>
|
|
+ </plugins>
|
|
+ </build>
|
|
+ <repositories>
|
|
+ <repository>
|
|
+ <id>repobo-snap</id>
|
|
+ <url>http://repo.bukkit.org/content/groups/public</url>
|
|
+ </repository>
|
|
+ </repositories>
|
|
+ <pluginRepositories>
|
|
+ <pluginRepository>
|
|
+ <id>bukkit-plugins</id>
|
|
+ <url>http://repo.bukkit.org/content/groups/public</url>
|
|
+ </pluginRepository>
|
|
+ </pluginRepositories>
|
|
+ <dependencies>
|
|
+ <dependency>
|
|
+ <groupId>junit</groupId>
|
|
+ <artifactId>junit</artifactId>
|
|
+ <version>4.11</version>
|
|
+ <scope>test</scope>
|
|
+ <exclusions>
|
|
+ <exclusion>
|
|
+ <artifactId>hamcrest-core</artifactId>
|
|
+ <groupId>org.hamcrest</groupId>
|
|
+ </exclusion>
|
|
+ </exclusions>
|
|
+ </dependency>
|
|
+ <dependency>
|
|
+ <groupId>org.hamcrest</groupId>
|
|
+ <artifactId>hamcrest-library</artifactId>
|
|
+ <version>1.3</version>
|
|
+ <scope>test</scope>
|
|
+ <exclusions>
|
|
+ <exclusion>
|
|
+ <artifactId>hamcrest-core</artifactId>
|
|
+ <groupId>org.hamcrest</groupId>
|
|
+ </exclusion>
|
|
+ </exclusions>
|
|
+ </dependency>
|
|
+ </dependencies>
|
|
+ <properties>
|
|
+ <minecraft_version>1_7_R3</minecraft_version>
|
|
+ <buildtag.prefix>git-Bukkit-</buildtag.prefix>
|
|
+ <api.version>unknown</api.version>
|
|
+ <minecraft.version>1.7.9</minecraft.version>
|
|
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
+ <junit.version>4.11</junit.version>
|
|
+ </properties>
|
|
+</project>
|
|
+
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 007fb86..51f1761 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -498,6 +498,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) {
|
|
@@ -688,6 +689,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
|
|
SpigotTimings.tickablesTimer.stopTiming(); // Spigot
|
|
|
|
this.methodProfiler.b();
|
|
+ org.spigotmc.WatchdogThread.tick(); // Spigot
|
|
SpigotTimings.serverTickTimer.stopTiming(); // Spigot
|
|
org.spigotmc.CustomTimingsHandler.tick(); // Spigot
|
|
}
|
|
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
|
|
new file mode 100644
|
|
index 0000000..3e4203f
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/spigotmc/RestartCommand.java
|
|
@@ -0,0 +1,113 @@
|
|
+package org.spigotmc;
|
|
+
|
|
+import java.io.File;
|
|
+import java.util.List;
|
|
+import net.minecraft.server.EntityPlayer;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+
|
|
+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 ) )
|
|
+ {
|
|
+ restart();
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public static void restart()
|
|
+ {
|
|
+ AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
|
|
+ try
|
|
+ {
|
|
+ final File file = new File( SpigotConfig.restartScript );
|
|
+ if ( file.isFile() )
|
|
+ {
|
|
+ System.out.println( "Attempting to restart with " + SpigotConfig.restartScript );
|
|
+
|
|
+ // Disable Watchdog
|
|
+ WatchdogThread.doStop();
|
|
+
|
|
+ // Kick all players
|
|
+ for ( EntityPlayer p : (List< EntityPlayer>) MinecraftServer.getServer().getPlayerList().players )
|
|
+ {
|
|
+ p.playerConnection.disconnect(SpigotConfig.restartMessage);
|
|
+ }
|
|
+ // 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().getServerConnection().b();
|
|
+
|
|
+ // 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 );
|
|
+ } else
|
|
+ {
|
|
+ System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
|
|
+ }
|
|
+ System.exit( 0 );
|
|
+ } catch ( Exception ex )
|
|
+ {
|
|
+ ex.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
|
|
index efcd193..2b499fe 100644
|
|
--- a/src/main/java/org/spigotmc/SpigotConfig.java
|
|
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
|
|
@@ -178,4 +178,18 @@ public class SpigotConfig
|
|
outdatedClientMessage = transform( getString( "messages.outdated-client", outdatedClientMessage ) );
|
|
outdatedServerMessage = transform( getString( "messages.outdated-server", outdatedServerMessage ) );
|
|
}
|
|
+
|
|
+ public static int timeoutTime = 60;
|
|
+ public static boolean restartOnCrash = true;
|
|
+ public static String restartScript = "./start.sh";
|
|
+ public static String restartMessage;
|
|
+ private static void watchdog()
|
|
+ {
|
|
+ timeoutTime = getInt( "settings.timeout-time", timeoutTime );
|
|
+ restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash );
|
|
+ restartScript = getString( "settings.restart-script", restartScript );
|
|
+ restartMessage = transform( getString( "messages.restart", "Server is restarting" ) );
|
|
+ commands.put( "restart", new RestartCommand( "restart" ) );
|
|
+ WatchdogThread.doStart( timeoutTime, restartOnCrash );
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
new file mode 100644
|
|
index 0000000..de08ad6
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
@@ -0,0 +1,117 @@
|
|
+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 net.minecraft.server.MinecraftServer;
|
|
+import org.bukkit.Bukkit;
|
|
+
|
|
+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, "------------------------------" );
|
|
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
|
|
+ dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE ), log );
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
+ //
|
|
+ log.log( Level.SEVERE, "Entire Thread Dump:" );
|
|
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true );
|
|
+ for ( ThreadInfo thread : threads )
|
|
+ {
|
|
+ dumpThread( thread, log );
|
|
+ }
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
+
|
|
+ if ( restart )
|
|
+ {
|
|
+ RestartCommand.restart();
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ try
|
|
+ {
|
|
+ sleep( 10000 );
|
|
+ } catch ( InterruptedException ex )
|
|
+ {
|
|
+ interrupt();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static void dumpThread(ThreadInfo thread, Logger log)
|
|
+ {
|
|
+ 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:" );
|
|
+ //
|
|
+ for ( StackTraceElement stack : thread.getStackTrace() )
|
|
+ {
|
|
+ log.log( Level.SEVERE, "\t\t" + stack );
|
|
+ }
|
|
+ }
|
|
+}
|
|
--
|
|
1.9.1
|
|
|