diff --git a/CraftBukkit-Patches/0030-Watchdog-Thread.patch b/CraftBukkit-Patches/0030-Watchdog-Thread.patch
new file mode 100644
index 0000000000..8f3dcfec46
--- /dev/null
+++ b/CraftBukkit-Patches/0030-Watchdog-Thread.patch
@@ -0,0 +1,272 @@
+From 6c8c769a97f22a7d9d5f8fa7692ae087dd717497 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   | 64 ++++++++++++++-
+ src/main/java/org/spigotmc/RestartCommand.java     | 23 ++++++
+ src/main/java/org/spigotmc/WatchdogThread.java     | 93 ++++++++++++++++++++++
+ 4 files changed, 180 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 128016f..3a6b620 100644
+--- a/src/main/java/net/minecraft/server/MinecraftServer.java
++++ b/src/main/java/net/minecraft/server/MinecraftServer.java
+@@ -418,6 +418,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
+                     lastTick = curTime;
+                     MinecraftServer.currentTick++;
+                     this.q();
++                    org.spigotmc.WatchdogThread.tick();
+                 }
+                 // Spigot end
+             } else {
+@@ -445,6 +446,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 5729cd6..6a4cbac 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.*;
+@@ -7,16 +8,20 @@ import org.bukkit.command.SimpleCommandMap;
+ 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 {
++
+     public static boolean tabPing = false;
+     private static Metrics metrics;
+ 
+     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);
+@@ -26,12 +31,21 @@ public class Spigot {
+         server.spamGuardExclusions = configuration.getStringList("settings.spam-exclusions");
+         server.mapSendInterval = configuration.getInt("settings.map-send-interval", server.mapSendInterval);
+ 
++        int configVersion = configuration.getInt("config-version");
++        switch (configVersion) {
++            case 0:
++                configuration.set("settings.timeout-time", 30);
++        }
++        configuration.set("config-version", 1);
++
++        WatchdogThread.doStart(configuration.getInt("settings.timeout-time", 30), 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) {
+@@ -194,4 +208,50 @@ public class Spigot {
+         return (entity instanceof EntityArrow && !((EntityArrow) entity).inGround);
+ 
+     }
++
++    public static void restart() {
++        try {
++            String startupScript = MinecraftServer.getServer().server.configuration.getString("settings.restart-script-location", "");
++            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) {
++                }
++
++                String os = System.getProperty("os.name").toLowerCase();
++                if (os.contains("win")) {
++                    Runtime.getRuntime().exec("cmd /c start " + file.getPath());
++                } else {
++                    Runtime.getRuntime().exec(file.getPath());
++                }
++                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();
++            }
++        }
++    }
++}
+-- 
+1.8.1-rc2
+