--- ../src-base/minecraft/net/minecraft/server/MinecraftServer.java
+++ ../src-work/minecraft/net/minecraft/server/MinecraftServer.java
@@ -16,6 +16,7 @@
 import io.netty.buffer.ByteBufOutputStream;
 import io.netty.buffer.Unpooled;
 import io.netty.handler.codec.base64.Base64;
+
 import java.awt.GraphicsEnvironment;
 import java.awt.image.BufferedImage;
 import java.io.File;
@@ -33,7 +34,10 @@
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.Callable;
+import java.util.logging.Level;
+
 import javax.imageio.ImageIO;
+
 import net.minecraft.command.CommandBase;
 import net.minecraft.command.ICommandManager;
 import net.minecraft.command.ICommandSender;
@@ -50,6 +54,7 @@
 import net.minecraft.profiler.PlayerUsageSnooper;
 import net.minecraft.profiler.Profiler;
 import net.minecraft.server.dedicated.DedicatedServer;
+import net.minecraft.server.dedicated.PropertyManager;
 import net.minecraft.server.gui.IUpdatePlayerListBox;
 import net.minecraft.server.management.PlayerProfileCache;
 import net.minecraft.server.management.ServerConfigurationManager;
@@ -72,6 +77,7 @@
 import net.minecraft.world.storage.ISaveFormat;
 import net.minecraft.world.storage.ISaveHandler;
 import net.minecraft.world.storage.WorldInfo;
+
 import org.apache.commons.lang3.Validate;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -80,18 +86,54 @@
 import net.minecraftforge.common.MinecraftForge;
 import net.minecraftforge.event.world.WorldEvent;
 
+
+// CraftBukkit start
+import java.io.IOException;
+
+import jline.console.ConsoleReader;
+import joptsimple.OptionSet;
+import net.minecraft.world.chunk.storage.AnvilSaveHandler;
+
+import org.bukkit.World.Environment;
+import org.bukkit.craftbukkit.SpigotTimings; // Spigot
+import org.bukkit.craftbukkit.util.Waitable;
+import org.bukkit.event.server.RemoteServerCommandEvent;
+import org.bukkit.event.world.WorldSaveEvent;
+
+// CraftBukkit end
+// Cauldron start
+import java.util.Map;
+import java.lang.reflect.Constructor;
+
+import joptsimple.OptionParser;
+import kcauldron.KCauldronConfig;
+import cpw.mods.fml.common.asm.transformers.SideTransformer;
+import net.minecraft.command.ServerCommand;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.WorldProvider;
+import net.minecraftforge.cauldron.CauldronUtils;
+import net.minecraftforge.cauldron.configuration.CauldronConfig;
+import net.minecraftforge.cauldron.configuration.TileEntityConfig;
+import net.minecraftforge.common.util.EnumHelper;
+
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.craftbukkit.CraftServer;
+import org.bukkit.craftbukkit.block.CraftBlock;
+// Cauldron end
+
 public abstract class MinecraftServer implements ICommandSender, Runnable, IPlayerUsage
 {
     private static final Logger logger = LogManager.getLogger();
     public static final File field_152367_a = new File("usercache.json");
     private static MinecraftServer mcServer;
-    private final ISaveFormat anvilConverterForAnvilFile;
+    public ISaveFormat anvilConverterForAnvilFile; // CraftBukkit - private final -> public
     private final PlayerUsageSnooper usageSnooper = new PlayerUsageSnooper("server", this, getSystemTimeMillis());
-    private final File anvilFile;
+    public File anvilFile; // CraftBukkit - private final -> public
     private final List tickables = new ArrayList();
     private final ICommandManager commandManager;
     public final Profiler theProfiler = new Profiler();
-    private final NetworkSystem field_147144_o;
+    private NetworkSystem field_147144_o; // Spigot
     private final ServerStatusResponse field_147147_p = new ServerStatusResponse();
     private final Random field_147146_q = new Random();
     @SideOnly(Side.SERVER)
@@ -135,8 +177,40 @@
     private long field_147142_T = 0L;
     private final GameProfileRepository field_152365_W;
     private final PlayerProfileCache field_152366_X;
+    // CraftBukkit start
+    public List<WorldServer> worlds = new ArrayList<WorldServer>();
+    public org.bukkit.craftbukkit.CraftServer server;
+    public static OptionSet options; // Cauldron
+    public org.bukkit.command.ConsoleCommandSender console;
+    public org.bukkit.command.RemoteConsoleCommandSender remoteConsole;
+    public ConsoleReader reader;
+    public static int currentTick = (int)(System.currentTimeMillis() / 50);
+    public final Thread primaryThread;
+    public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
+    public int autosavePeriod;
+    // CraftBukkit end
+    // Spigot start
+    private static final int TPS = 20;
+    private static final int TICK_TIME = 1000000000 / TPS;
+    public final double[] recentTps = new double[ 3 ];
+    // Spigot end
+    // Cauldron start
+    public static CauldronConfig cauldronConfig;
+    public static KCauldronConfig kcauldronConfig;
+    public static TileEntityConfig tileEntityConfig;
+    public static YamlConfiguration configuration;
+    public static YamlConfiguration commandsConfiguration;
+    public static File configFile;
+    public static File commandFile;
+    public static double currentTps = 0;
+    public static boolean useJline = true;
+    public static boolean useConsole = true;
+    public static boolean callingForgeTick = false;
+    public static List<Class<? extends TileEntity>> bannedTileEntityUpdates = new ArrayList<Class<? extends TileEntity>>();
+    // Cauldron end
     private static final String __OBFID = "CL_00001462";
 
+    // Cauldron start - IntegratedServer requires this
     public MinecraftServer(File p_i45281_1_, Proxy p_i45281_2_)
     {
         this.field_152366_X = new PlayerProfileCache(this, field_152367_a);
@@ -149,10 +223,70 @@
         this.field_152364_T = new YggdrasilAuthenticationService(p_i45281_2_, UUID.randomUUID().toString());
         this.field_147143_S = this.field_152364_T.createMinecraftSessionService();
         this.field_152365_W = this.field_152364_T.createProfileRepository();
+        this.primaryThread = new Thread(this, "Server thread"); // CraftBukkit
+        kcauldronConfig = new KCauldronConfig();
+        cauldronConfig = new CauldronConfig("cauldron.yml", "cauldron");
+        tileEntityConfig = new TileEntityConfig("tileentities.yml", "cauldron_te");
     }
+    // Cauldron end
 
-    protected abstract boolean startServer() throws IOException;
+    public MinecraftServer(OptionSet options, Proxy proxy)   // CraftBukkit - signature file -> OptionSet
+    {
+        this.field_152366_X = new PlayerProfileCache(this, field_152367_a);
+        mcServer = this;
+        this.serverProxy = proxy;
+        // this.anvilFile = p_i45281_1_; // CraftBukkit
+        // this.field_147144_o = new NetworkSystem(this); // Spigot
+        this.commandManager = new ServerCommandManager();
+        // this.anvilConverterForAnvilFile = new AnvilSaveConverter(p_i45281_1_);  // CraftBukkit - moved to DedicatedServer.init
+        this.field_152364_T = new YggdrasilAuthenticationService(proxy, UUID.randomUUID().toString());
+        this.field_147143_S = this.field_152364_T.createMinecraftSessionService();
+        this.field_152365_W = this.field_152364_T.createProfileRepository();
+        // Cauldron start
+        kcauldronConfig = new KCauldronConfig();
+        cauldronConfig = new CauldronConfig("cauldron.yml", "cauldron");
+        tileEntityConfig = new TileEntityConfig("tileentities.yml", "cauldron_te");
+        // Cauldron end
+        // CraftBukkit start
+        this.options = options;
+        // Try to see if we're actually running in a terminal, disable jline if not
+        if (System.console() == null)
+        {
+            System.setProperty("jline.terminal", "jline.UnsupportedTerminal");
+            this.useJline = false; // Cauldron
+        }
 
+        try
+        {
+            this.reader = new ConsoleReader(System.in, System.out);
+            this.reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators
+        }
+        catch (Throwable e)
+        {
+            try
+            {
+                // Try again with jline disabled for Windows users without C++ 2008 Redistributable
+                System.setProperty("jline.terminal", "jline.UnsupportedTerminal");
+                System.setProperty("user.language", "en");
+                this.useJline = false; // Cauldron
+                this.reader = new ConsoleReader(System.in, System.out);
+                this.reader.setExpandEvents(false);
+            }
+            catch (IOException ex)
+            {
+                logger.warn((String) null, ex);
+            }
+        }
+        net.minecraftforge.cauldron.CauldronHooks.enableThreadContentionMonitoring();
+        Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
+        primaryThread = new Thread(this, "Server thread"); // Moved from main
+    }
+    
+    public abstract PropertyManager getPropertyManager();
+    // CraftBukkit end
+
+    protected abstract boolean startServer() throws java.net.UnknownHostException; // CraftBukkit - throws UnknownHostException
+
     protected void convertMapIfNeeded(String p_71237_1_)
     {
         if (this.getActiveAnvilConverter().isOldMapFormat(p_71237_1_))
@@ -172,6 +306,7 @@
                         MinecraftServer.logger.info("Converting... " + p_73718_1_ + "%");
                     }
                 }
+                
                 @SideOnly(Side.CLIENT)
                 public void resetProgressAndMessage(String p_73721_1_) {}
                 @SideOnly(Side.CLIENT)
@@ -195,10 +330,17 @@
 
     protected void loadAllWorlds(String p_71247_1_, String p_71247_2_, long p_71247_3_, WorldType p_71247_5_, String p_71247_6_)
     {
+        // Cauldron start - register vanilla server commands
+        ServerCommandManager vanillaCommandManager = (ServerCommandManager)this.getCommandManager();
+        vanillaCommandManager.registerVanillaCommands();
+        // Cauldron end
         this.convertMapIfNeeded(p_71247_1_);
         this.setUserMessage("menu.loadingLevel");
-        ISaveHandler isavehandler = this.anvilConverterForAnvilFile.getSaveLoader(p_71247_1_, true);
-        WorldInfo worldinfo = isavehandler.loadWorldInfo();
+        // Cauldron start - SaveHandler/WorldInfo below are not used and must be disabled to prevent FML receiving different handlers for overworld
+        //ISaveHandler isavehandler = this.anvilConverterForAnvilFile.getSaveLoader(p_71247_1_, true);
+        //WorldInfo worldinfo = isavehandler.loadWorldInfo();
+        // Cauldron end
+        /* CraftBukkit start - Removed worldsettings
         WorldSettings worldsettings;
 
         if (worldinfo == null)
@@ -215,11 +357,79 @@
         {
             worldsettings.enableBonusChest();
         }
+        // */
 
-        WorldServer overWorld = (isDemo() ? new DemoWorldServer(this, isavehandler, p_71247_2_, 0, theProfiler) : new WorldServer(this, isavehandler, p_71247_2_, 0, worldsettings, theProfiler));
-        for (int dim : DimensionManager.getStaticDimensionIDs())
+        WorldSettings worldsettings = new WorldSettings(p_71247_3_, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), p_71247_5_);
+        worldsettings.func_82750_a(p_71247_6_);
+        WorldServer world;
+
+        // Cauldron - overworld generator is handled in World after plugins load
+        WorldServer overWorld = (isDemo() ? new DemoWorldServer(this, new AnvilSaveHandler(server.getWorldContainer(), p_71247_2_, true), p_71247_2_, 0, theProfiler) : new WorldServer(this, new AnvilSaveHandler(server.getWorldContainer(), p_71247_2_, true), p_71247_2_, 0, worldsettings, theProfiler, Environment.getEnvironment(0), null));
+
+        for (int dimension : DimensionManager.getStaticDimensionIDs())
         {
-            WorldServer world = (dim == 0 ? overWorld : new WorldServerMulti(this, isavehandler, p_71247_2_, dim, worldsettings, overWorld, theProfiler));
+            String worldType = "";
+            String name = "";
+            String oldName = "";
+            org.bukkit.generator.ChunkGenerator gen = null;
+            // Cauldron start
+            Environment env = Environment.getEnvironment(dimension);
+            if (dimension != 0)
+            {
+                if ((dimension == -1 && !this.getAllowNether()) || (dimension == 1 && !this.server.getAllowEnd()))
+                    continue;
+
+                if (env == null)
+                {
+                    WorldProvider provider = WorldProvider.getProviderForDimension(dimension);
+                    worldType = provider.getClass().getSimpleName().toLowerCase();
+                    worldType = worldType.replace("worldprovider", "");
+                    oldName = "world_" + worldType.toLowerCase();
+                    worldType = worldType.replace("provider", "");
+                    env = Environment.getEnvironment(DimensionManager.getProviderType(provider.getClass()));
+                    name = provider.getSaveFolder();
+                    if (name == null) name = "DIM0";
+                }
+                else 
+                {
+                    worldType = env.toString().toLowerCase();
+                    name = "DIM" + dimension;
+                    oldName = p_71247_1_ + "_" + worldType;
+                    oldName = oldName.replaceAll(" ", "_");
+                }
+
+                // check if the world is enabled or not
+                if (!configuration.isBoolean("world-settings." + worldType + ".enabled")) {
+                    configuration.set("world-settings." + worldType + ".enabled", true);
+                }
+                boolean enabled = configuration.getBoolean("world-settings." + worldType + ".enabled");
+                try {
+                    configuration.save(MinecraftServer.configFile);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                if (!enabled)
+                    continue;
+                // end world enabled check
+
+                gen = this.server.getGenerator(name);
+                worldsettings = new WorldSettings(p_71247_3_, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), p_71247_5_);
+                worldsettings.func_82750_a(p_71247_6_);
+
+                CauldronUtils.migrateWorlds(worldType, oldName, p_71247_1_, name);
+
+                this.setUserMessage(name);
+            }
+
+            world = (dimension == 0 ? overWorld : new WorldServerMulti(this, new AnvilSaveHandler(server.getWorldContainer(), name, true), name, dimension, worldsettings, overWorld, this.theProfiler, env, gen));
+            // Cauldron end
+            if (gen != null)
+            {
+                world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld()));
+            }
+
+            this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard());
+            this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld()));
             world.addWorldAccess(new WorldManager(this, world));
 
             if (!this.isSinglePlayer())
@@ -227,12 +437,14 @@
                 world.getWorldInfo().setGameType(this.getGameType());
             }
 
-            MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world));
+            this.serverConfigManager.setPlayerManager(this.worlds.toArray(new WorldServer[this.worlds.size()]));
+            // CraftBukkit end
+            MinecraftForge.EVENT_BUS.post(new WorldEvent.Load((World)world)); // Forge
         }
-
-        this.serverConfigManager.setPlayerManager(new WorldServer[]{ overWorld });
         this.func_147139_a(this.func_147135_j());
         this.initialWorldChunkLoad();
+        CraftBlock.dumpMaterials();
+        // Cauldron end
     }
 
     protected void initialWorldChunkLoad()
@@ -244,9 +456,12 @@
         int i = 0;
         this.setUserMessage("menu.generatingTerrain");
         byte b0 = 0;
+        // Cauldron start - we now handle CraftBukkit's keepSpawnInMemory logic in DimensionManager. Prevents crashes with mods such as DivineRPG and speeds up server startup time by a ton.
         logger.info("Preparing start region for level " + b0);
         WorldServer worldserver = this.worldServers[b0];
         ChunkCoordinates chunkcoordinates = worldserver.getSpawnPoint();
+        boolean before = worldserver.theChunkProviderServer.loadChunkOnProvideRequest;
+        worldserver.theChunkProviderServer.loadChunkOnProvideRequest = true;
         long j = getSystemTimeMillis();
 
         for (int k = -192; k <= 192 && this.isServerRunning(); k += 16)
@@ -265,7 +480,8 @@
                 worldserver.theChunkProviderServer.loadChunk(chunkcoordinates.posX + k >> 4, chunkcoordinates.posZ + l >> 4);
             }
         }
-
+        worldserver.theChunkProviderServer.loadChunkOnProvideRequest = before;
+        // Cauldron end
         this.clearCurrentTask();
     }
 
@@ -292,19 +508,17 @@
     {
         this.currentTask = null;
         this.percentDone = 0;
+        this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); // CraftBukkit
     }
 
-    protected void saveAllWorlds(boolean p_71267_1_)
+    protected void saveAllWorlds(boolean p_71267_1_) throws MinecraftException   // CraftBukkit - added throws
     {
         if (!this.worldIsBeingDeleted)
         {
-            WorldServer[] aworldserver = this.worldServers;
-            if (aworldserver == null) return; //Forge: Just in case, NPE protection as it has been encountered.
-            int i = aworldserver.length;
-
-            for (int j = 0; j < i; ++j)
+            // CraftBukkit start
+            for (int j = 0; j < this.worlds.size(); ++j)
             {
-                WorldServer worldserver = aworldserver[j];
+                WorldServer worldserver = this.worlds.get(j);
 
                 if (worldserver != null)
                 {
@@ -313,25 +527,41 @@
                         logger.info("Saving chunks for level \'" + worldserver.getWorldInfo().getWorldName() + "\'/" + worldserver.provider.getDimensionName());
                     }
 
-                    try
+                    worldserver.saveAllChunks(true, (IProgressUpdate) null);
+                    worldserver.flush();
+                    WorldSaveEvent event = new WorldSaveEvent(worldserver.getWorld());
+                    this.server.getPluginManager().callEvent(event);
+                    // Cauldron start - save world configs
+                    if (worldserver.cauldronConfig != null)
                     {
-                        worldserver.saveAllChunks(true, (IProgressUpdate)null);
+                        worldserver.cauldronConfig.save();
                     }
-                    catch (MinecraftException minecraftexception)
+                    if (worldserver.tileentityConfig != null)
                     {
-                        logger.warn(minecraftexception.getMessage());
+                        worldserver.tileentityConfig.save();
                     }
+                    // Cauldron end
                 }
             }
+
+            // CraftBukkit end
         }
     }
 
-    public void stopServer()
+    public void stopServer() throws MinecraftException // CraftBukkit - added throws
     {
         if (!this.worldIsBeingDeleted && Loader.instance().hasReachedState(LoaderState.SERVER_STARTED) && !serverStopped) // make sure the save is valid and we don't save twice
         {
             logger.info("Stopping server");
 
+            // CraftBukkit start
+            if (this.server != null)
+            {
+                this.server.disablePlugins();
+            }
+
+            // CraftBukkit end
+
             if (this.func_147137_ag() != null)
             {
                 this.func_147137_ag().terminateEndpoints();
@@ -347,7 +577,14 @@
             if (this.worldServers != null)
             {
                 logger.info("Saving worlds");
-                this.saveAllWorlds(false);
+                try
+                {
+                    this.saveAllWorlds(false);
+                }
+                catch (MinecraftException e)
+                {
+                    e.printStackTrace();
+                }
 
                 for (int i = 0; i < this.worldServers.length; ++i)
                 {
@@ -380,6 +617,13 @@
         this.serverRunning = false;
     }
 
+    // Spigot Start
+    private static double calcTps(double avg, double exp, double tps)
+    {
+        return (avg * exp) + (tps * (1 - exp));
+    }
+    // Spigot End
+
     public void run()
     {
         try
@@ -392,45 +636,41 @@
                 this.field_147147_p.func_151315_a(new ChatComponentText(this.motd));
                 this.field_147147_p.func_151321_a(new ServerStatusResponse.MinecraftProtocolVersionIdentifier("1.7.10", 5));
                 this.func_147138_a(this.field_147147_p);
+                DedicatedServer.allowPlayerLogins = true; // Cauldron - server is ready, allow player logins
+                // Spigot start
+                Arrays.fill(recentTps, 20);
+                long lastTick = 0, catchupTime = 0, curTime, wait;
 
                 while (this.serverRunning)
                 {
-                    long j = getSystemTimeMillis();
-                    long k = j - i;
+                    curTime = System.nanoTime();
+                    wait = TICK_TIME - (curTime - lastTick) - catchupTime;
 
-                    if (k > 2000L && i - this.timeOfLastWarning >= 15000L)
+                    if (wait > 0)
                     {
-                        logger.warn("Can\'t keep up! Did the system time change, or is the server overloaded? Running {}ms behind, skipping {} tick(s)", new Object[] {Long.valueOf(k), Long.valueOf(k / 50L)});
-                        k = 2000L;
-                        this.timeOfLastWarning = i;
+                        Thread.sleep(wait / 1000000);
+                        catchupTime = 0;
+                        continue;
                     }
-
-                    if (k < 0L)
+                    else
                     {
-                        logger.warn("Time ran backwards! Did the system time change?");
-                        k = 0L;
+                        catchupTime = Math.min(1000000000, Math.abs(wait));
                     }
 
-                    l += k;
-                    i = j;
-
-                    if (this.worldServers[0].areAllPlayersAsleep())
+                    if (MinecraftServer.currentTick++ % 100 == 0)
                     {
-                        this.tick();
-                        l = 0L;
+                        currentTps = 1E9 / (curTime - lastTick);
+                        recentTps[0] = calcTps(recentTps[0], 0.92, currentTps);   // 1/exp(5sec/1min)
+                        recentTps[1] = calcTps(recentTps[1], 0.9835, currentTps);   // 1/exp(5sec/5min)
+                        recentTps[2] = calcTps(recentTps[2], 0.9945, currentTps);   // 1/exp(5sec/15min)
                     }
-                    else
-                    {
-                        while (l > 50L)
-                        {
-                            l -= 50L;
-                            this.tick();
-                        }
-                    }
 
-                    Thread.sleep(Math.max(1L, 50L - l));
+                    lastTick = curTime;
+                    this.tick();
                     this.serverIsRunning = true;
                 }
+
+                // Spigot end
                 FMLCommonHandler.instance().handleServerStopping();
                 FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions
             }
@@ -448,6 +688,14 @@
         catch (Throwable throwable1)
         {
             logger.error("Encountered an unexpected exception", throwable1);
+
+            // Spigot Start
+            if (throwable1.getCause() != null)
+            {
+                logger.error("\tCause of unexpected exception was", throwable1.getCause());
+            }
+
+            // Spigot End
             CrashReport crashreport = null;
 
             if (throwable1 instanceof ReportedException)
@@ -477,6 +725,7 @@
         {
             try
             {
+                org.spigotmc.WatchdogThread.doStop(); // Spigot
                 this.stopServer();
                 this.serverStopped = true;
             }
@@ -486,6 +735,16 @@
             }
             finally
             {
+                // CraftBukkit start - Restore terminal to original settings
+                try
+                {
+                    this.reader.getTerminal().restore();
+                }
+                catch (Exception e)
+                {
+                }
+
+                // CraftBukkit end
                 FMLCommonHandler.instance().handleServerStopped();
                 this.serverStopped = true;
                 this.systemExitNow();
@@ -532,8 +791,11 @@
 
     public void tick()
     {
+        SpigotTimings.serverTickTimer.startTiming(); // Spigot
         long i = System.nanoTime();
+        callingForgeTick = true; // Cauldron start - handle loadOnProviderRequests during forge tick event
         FMLCommonHandler.instance().onPreServerTick();
+        callingForgeTick = false; // Cauldron end
         ++this.tickCounter;
 
         if (this.startProfiling)
@@ -562,12 +824,21 @@
             this.field_147147_p.func_151318_b().func_151330_a(agameprofile);
         }
 
-        if (this.tickCounter % 900 == 0)
+        if ((this.autosavePeriod > 0) && ((this.tickCounter % this.autosavePeriod) == 0))   // CraftBukkit
         {
+            SpigotTimings.worldSaveTimer.startTiming(); // Spigot
             this.theProfiler.startSection("save");
             this.serverConfigManager.saveAllPlayerData();
-            this.saveAllWorlds(true);
+            try
+            {
+                this.saveAllWorlds(true);
+            }
+            catch (MinecraftException e)
+            {
+                e.printStackTrace();
+            }
             this.theProfiler.endSection();
+            SpigotTimings.worldSaveTimer.stopTiming(); // Spigot
         }
 
         this.theProfiler.startSection("tallying");
@@ -575,25 +846,57 @@
         this.theProfiler.endSection();
         this.theProfiler.startSection("snooper");
 
-        if (!this.usageSnooper.isSnooperRunning() && this.tickCounter > 100)
+        if (isSnooperEnabled() && !this.usageSnooper.isSnooperRunning() && this.tickCounter > 100)   // Spigot
         {
             this.usageSnooper.startSnooper();
         }
 
-        if (this.tickCounter % 6000 == 0)
+        if (isSnooperEnabled() && this.tickCounter % 6000 == 0)   // Spigot
         {
             this.usageSnooper.addMemoryStatsToSnooper();
         }
 
         this.theProfiler.endSection();
         this.theProfiler.endSection();
+        callingForgeTick = true; // Cauldron start - handle loadOnProviderRequests during forge tick event
         FMLCommonHandler.instance().onPostServerTick();
+        callingForgeTick = false; // Cauldron end
+        SpigotTimings.serverTickTimer.stopTiming(); // Spigot
+        org.spigotmc.CustomTimingsHandler.tick(); // Spigot
     }
 
     public void updateTimeLightAndEntities()
     {
         this.theProfiler.startSection("levels");
+        SpigotTimings.schedulerTimer.startTiming(); // Spigot
+        // CraftBukkit start
+        this.server.getScheduler().mainThreadHeartbeat(this.tickCounter);
+        SpigotTimings.schedulerTimer.stopTiming(); // Spigot
+
+        // Run tasks that are waiting on processing
+        SpigotTimings.processQueueTimer.startTiming(); // Spigot
+        while (!processQueue.isEmpty())
+        {
+            processQueue.remove().run();
+        }
+        SpigotTimings.processQueueTimer.stopTiming(); // Spigot
+
+        SpigotTimings.chunkIOTickTimer.startTiming(); // Spigot
         net.minecraftforge.common.chunkio.ChunkIOExecutor.tick();
+        SpigotTimings.chunkIOTickTimer.stopTiming(); // Spigot
+
+        SpigotTimings.timeUpdateTimer.startTiming(); // Spigot
+        // Send time updates to everyone, it will get the right time from the world the player is in.
+        if (this.tickCounter % 20 == 0)
+        {
+            for (int i = 0; i < this.getConfigurationManager().playerEntityList.size(); ++i)
+            {
+                EntityPlayerMP entityplayermp = (EntityPlayerMP) this.getConfigurationManager().playerEntityList.get(i);
+                entityplayermp.playerNetServerHandler.sendPacket(new S03PacketTimeUpdate(entityplayermp.worldObj.getTotalWorldTime(), entityplayermp.getPlayerTime(), entityplayermp.worldObj.getGameRules().getGameRuleBooleanValue("doDaylightCycle"))); // Add support for per player time
+            }
+        }
+        SpigotTimings.timeUpdateTimer.stopTiming(); // Spigot
+
         int i;
 
         Integer[] ids = DimensionManager.getIDs(this.tickCounter % 200 == 0);
@@ -602,19 +905,21 @@
             int id = ids[x];
             long j = System.nanoTime();
 
-            if (id == 0 || this.getAllowNether())
-            {
+            // CraftBukkit start
+            //if (id == 0 || this.getAllowNether())
+            //{
                 WorldServer worldserver = DimensionManager.getWorld(id);
                 this.theProfiler.startSection(worldserver.getWorldInfo().getWorldName());
                 this.theProfiler.startSection("pools");
                 this.theProfiler.endSection();
-
+                /* Drop global time updates
                 if (this.tickCounter % 20 == 0)
                 {
                     this.theProfiler.startSection("timeSync");
                     this.serverConfigManager.sendPacketToAllPlayersInDimension(new S03PacketTimeUpdate(worldserver.getTotalWorldTime(), worldserver.getWorldTime(), worldserver.getGameRules().getGameRuleBooleanValue("doDaylightCycle")), worldserver.provider.dimensionId);
                     this.theProfiler.endSection();
                 }
+                // CraftBukkit end */
 
                 this.theProfiler.startSection("tick");
                 FMLCommonHandler.instance().onPreWorldTick(worldserver);
@@ -622,22 +927,46 @@
 
                 try
                 {
+                    worldserver.timings.doTick.startTiming(); // Spigot
                     worldserver.tick();
+                    worldserver.timings.doTick.stopTiming(); // Spigot
                 }
                 catch (Throwable throwable1)
                 {
-                    crashreport = CrashReport.makeCrashReport(throwable1, "Exception ticking world");
+                    // Spigot Start
+                    try
+                    {
+                        crashreport = CrashReport.makeCrashReport(throwable1, "Exception ticking world");
+                    }
+                    catch (Throwable t)
+                    {
+                        throw new RuntimeException("Error generating crash report", t);
+                    }
+    
+                    // Spigot End
                     worldserver.addWorldInfoToCrashReport(crashreport);
                     throw new ReportedException(crashreport);
                 }
 
                 try
                 {
+                    worldserver.timings.tickEntities.startTiming(); // Spigot
                     worldserver.updateEntities();
+                    worldserver.timings.tickEntities.stopTiming(); // Spigot
                 }
                 catch (Throwable throwable)
                 {
-                    crashreport = CrashReport.makeCrashReport(throwable, "Exception ticking world entities");
+                    // Spigot Start
+                    try
+                    {
+                        crashreport = CrashReport.makeCrashReport(throwable, "Exception ticking world entities");
+                    }
+                    catch (Throwable t)
+                    {
+                        throw new RuntimeException("Error generating crash report", t);
+                    }
+    
+                    // Spigot End
                     worldserver.addWorldInfoToCrashReport(crashreport);
                     throw new ReportedException(crashreport);
                 }
@@ -645,10 +974,12 @@
                 FMLCommonHandler.instance().onPostWorldTick(worldserver);
                 this.theProfiler.endSection();
                 this.theProfiler.startSection("tracker");
+                worldserver.timings.tracker.startTiming(); // Spigot
                 worldserver.getEntityTracker().updateTrackedEntities();
+                worldserver.timings.tracker.stopTiming(); // Spigot
                 this.theProfiler.endSection();
                 this.theProfiler.endSection();
-            }
+            // } // CraftBukkit
 
             worldTickTimes.get(id)[this.tickCounter % 100] = System.nanoTime() - j;
         }
@@ -656,15 +987,21 @@
         this.theProfiler.endStartSection("dim_unloading");
         DimensionManager.unloadWorlds(worldTickTimes);
         this.theProfiler.endStartSection("connection");
+        SpigotTimings.connectionTimer.startTiming(); // Spigot
         this.func_147137_ag().networkTick();
+        SpigotTimings.connectionTimer.stopTiming(); // Spigot
         this.theProfiler.endStartSection("players");
+        SpigotTimings.playerListTimer.startTiming(); // Spigot
         this.serverConfigManager.sendPlayerInfoToAllPlayers();
+        SpigotTimings.playerListTimer.stopTiming(); // Spigot
         this.theProfiler.endStartSection("tickables");
 
+        SpigotTimings.tickablesTimer.startTiming(); // Spigot
         for (i = 0; i < this.tickables.size(); ++i)
         {
             ((IUpdatePlayerListBox)this.tickables.get(i)).update();
         }
+        SpigotTimings.tickablesTimer.stopTiming(); // Spigot
 
         this.theProfiler.endSection();
     }
@@ -699,6 +1036,13 @@
 
     public WorldServer worldServerForDimension(int p_71218_1_)
     {
+        // Cauldron start - this is required for MystCraft agebooks to teleport correctly
+        // verify the nether or the end is allowed, and if not return overworld
+        if ((p_71218_1_ == -1 && !this.getAllowNether()) || (p_71218_1_ == 1 && !this.server.getAllowEnd()))
+        {
+            return DimensionManager.getWorld(0);
+        }
+        // Cauldron end
         WorldServer ret = DimensionManager.getWorld(p_71218_1_);
         if (ret == null)
         {
@@ -784,13 +1128,14 @@
 
     public List getPossibleCompletions(ICommandSender p_71248_1_, String p_71248_2_)
     {
-        ArrayList arraylist = new ArrayList();
+        // Cauldron start - add mod commands to list then pass to bukkit
+        java.util.HashSet arraylist = new java.util.HashSet(); // use a set here to avoid duplicates
 
         if (p_71248_2_.startsWith("/"))
         {
-            p_71248_2_ = p_71248_2_.substring(1);
-            boolean flag = !p_71248_2_.contains(" ");
-            List list = this.commandManager.getPossibleCommands(p_71248_1_, p_71248_2_);
+            String char1 = p_71248_2_.substring(1); // rename var to avoid removing slash from passed message
+            boolean flag = !char1.contains(" ");
+            List list = this.commandManager.getPossibleCommands(p_71248_1_, char1);
 
             if (list != null)
             {
@@ -798,40 +1143,25 @@
 
                 while (iterator.hasNext())
                 {
-                    String s3 = (String)iterator.next();
+                    String command = (String)iterator.next();
 
                     if (flag)
                     {
-                        arraylist.add("/" + s3);
+                        arraylist.add("/" + command);
                     }
                     else
                     {
-                        arraylist.add(s3);
+                        arraylist.add(command);
                     }
                 }
             }
-
-            return arraylist;
         }
-        else
-        {
-            String[] astring = p_71248_2_.split(" ", -1);
-            String s1 = astring[astring.length - 1];
-            String[] astring1 = this.serverConfigManager.getAllUsernames();
-            int i = astring1.length;
 
-            for (int j = 0; j < i; ++j)
-            {
-                String s2 = astring1[j];
-
-                if (CommandBase.doesStringStartWith(s1, s2))
-                {
-                    arraylist.add(s2);
-                }
-            }
-
-            return arraylist;
-        }
+        arraylist.addAll(this.server.tabComplete(p_71248_1_, p_71248_2_));  // add craftbukkit commands
+        ArrayList completions = new ArrayList(arraylist);
+        Collections.sort(completions); // sort the final list
+        return completions;
+        // Cauldron end
     }
 
     public static MinecraftServer getServer()
@@ -1034,7 +1364,7 @@
 
     public boolean isServerInOnlineMode()
     {
-        return this.onlineMode;
+        return this.server.getOnlineMode(); // CraftBukkit
     }
 
     public void setOnlineMode(boolean p_71229_1_)
@@ -1124,7 +1454,7 @@
 
     public NetworkSystem func_147137_ag()
     {
-        return this.field_147144_o;
+        return (this.field_147144_o) == null ? this.field_147144_o = new NetworkSystem(this) : this.field_147144_o;     // Spigot
     }
 
     @SideOnly(Side.CLIENT)
@@ -1259,8 +1589,11 @@
     {
         Bootstrap.func_151354_b();
 
+        OptionSet options = loadOptions(p_main_0_);
+
         try
         {
+            /* CraftBukkit start - Replace everything
             boolean flag = true;
             String s = null;
             String s1 = ".";
@@ -1356,16 +1689,34 @@
             {
                 dedicatedserver.setGuiEnabled();
             }
+            // */
+            // CraftBukkit end
+            if (CauldronUtils.deobfuscatedEnvironment()) useJline = false; // Cauldron
+            DedicatedServer dedicatedserver = new DedicatedServer(options);
 
-            dedicatedserver.startServerThread();
-            Runtime.getRuntime().addShutdownHook(new Thread("Server Shutdown Thread")
+            if (options.has("port"))
             {
-                private static final String __OBFID = "CL_00001806";
-                public void run()
+                int port = (Integer) options.valueOf("port");
+
+                if (port > 0)
                 {
-                    dedicatedserver.stopServer();
+                    dedicatedserver.setServerPort(port);
                 }
-            });
+            }
+
+            if (options.has("universe"))
+            {
+                dedicatedserver.anvilFile = (File) options.valueOf("universe");
+            }
+
+            if (options.has("world"))
+            {
+                dedicatedserver.setFolderName((String) options.valueOf("world"));
+            }
+
+            dedicatedserver.primaryThread.start();
+            // Runtime.getRuntime().addShutdownHook(new ThreadShutdown("Server Shutdown Thread", dedicatedserver));
+            // CraftBukkit end
         }
         catch (Exception exception)
         {
@@ -1400,15 +1751,70 @@
     @SideOnly(Side.SERVER)
     public String getPlugins()
     {
-        return "";
+        // CraftBukkit start - Whole method
+        StringBuilder result = new StringBuilder();
+        org.bukkit.plugin.Plugin[] plugins = server.getPluginManager().getPlugins();
+        result.append(server.getName());
+        result.append(" on Bukkit ");
+        result.append(server.getBukkitVersion());
+
+        if (plugins.length > 0 && this.server.getQueryPlugins())
+        {
+            result.append(": ");
+
+            for (int i = 0; i < plugins.length; i++)
+            {
+                if (i > 0)
+                {
+                    result.append("; ");
+                }
+
+                result.append(plugins[i].getDescription().getName());
+                result.append(" ");
+                result.append(plugins[i].getDescription().getVersion().replaceAll(";", ","));
+            }
+        }
+
+        return result.toString();
+        // CraftBukkit end
     }
 
     @SideOnly(Side.SERVER)
-    public String handleRConCommand(String p_71252_1_)
+    public String handleRConCommand(final String par1Str)
     {
-        RConConsoleSource.instance.resetLog();
-        this.commandManager.executeCommand(RConConsoleSource.instance, p_71252_1_);
-        return RConConsoleSource.instance.getLogContents();
+        Waitable<String> waitable = new Waitable<String>()
+        {
+            @Override
+            protected String evaluate()
+            {
+                RConConsoleSource.instance.resetLog();
+                // Event changes start
+                RemoteServerCommandEvent event = new RemoteServerCommandEvent(MinecraftServer.this.remoteConsole, par1Str);
+                MinecraftServer.this.server.getPluginManager().callEvent(event);
+                // Event changes end
+                ServerCommand servercommand = new ServerCommand(event.getCommand(), RConConsoleSource.instance);
+                MinecraftServer.this.server.dispatchServerCommand(MinecraftServer.this.remoteConsole, servercommand); // CraftBukkit
+                // this.n.a(RemoteControlCommandListener.instance, s);
+                return RConConsoleSource.instance.getLogContents();
+            }
+        };
+        processQueue.add(waitable);
+
+        try
+        {
+            return waitable.get();
+        }
+        catch (java.util.concurrent.ExecutionException e)
+        {
+            throw new RuntimeException("Exception processing rcon command " + par1Str, e.getCause());
+        }
+        catch (InterruptedException e)
+        {
+            Thread.currentThread().interrupt(); // Maintain interrupted state
+            throw new RuntimeException("Interrupted processing rcon command " + par1Str, e);
+        }
+
+        // CraftBukkit end
     }
 
     @SideOnly(Side.SERVER)
@@ -1455,9 +1861,213 @@
         return this.serverStopped;
     }
 
+    public static OptionSet loadOptions(String[] args) {
+        OptionParser parser = new OptionParser() {
+            {
+                acceptsAll(Arrays.asList("?", "help"), "Show the help");
+
+                acceptsAll(Arrays.asList("c", "config"), "Properties file to use")
+                        .withRequiredArg()
+                        .ofType(File.class)
+                        .defaultsTo(new File("server.properties"))
+                        .describedAs("Properties file");
+
+                acceptsAll(Arrays.asList("P", "plugins"), "Plugin directory to use")
+                        .withRequiredArg()
+                        .ofType(File.class)
+                        .defaultsTo(new File("plugins"))
+                        .describedAs("Plugin directory");
+
+                acceptsAll(Arrays.asList("h", "host", "server-ip"), "Host to listen on")
+                        .withRequiredArg()
+                        .ofType(String.class)
+                        .describedAs("Hostname or IP");
+
+                acceptsAll(Arrays.asList("W", "world-dir", "universe", "world-container"), "World container")
+                        .withRequiredArg()
+                        .ofType(File.class)
+                        .describedAs("Directory containing worlds");
+
+                acceptsAll(Arrays.asList("w", "world", "level-name"), "World name")
+                        .withRequiredArg()
+                        .ofType(String.class)
+                        .describedAs("World name");
+
+                acceptsAll(Arrays.asList("p", "port", "server-port"), "Port to listen on")
+                        .withRequiredArg()
+                        .ofType(Integer.class)
+                        .describedAs("Port");
+
+                acceptsAll(Arrays.asList("o", "online-mode"), "Whether to use online authentication")
+                        .withRequiredArg()
+                        .ofType(Boolean.class)
+                        .describedAs("Authentication");
+
+                acceptsAll(Arrays.asList("s", "size", "max-players"), "Maximum amount of players")
+                        .withRequiredArg()
+                        .ofType(Integer.class)
+                        .describedAs("Server size");
+
+                acceptsAll(Arrays.asList("d", "date-format"), "Format of the date to display in the console (for log entries)")
+                        .withRequiredArg()
+                        .ofType(SimpleDateFormat.class)
+                        .describedAs("Log date format");
+
+                acceptsAll(Arrays.asList("log-pattern"), "Specfies the log filename pattern")
+                        .withRequiredArg()
+                        .ofType(String.class)
+                        .defaultsTo("server.log")
+                        .describedAs("Log filename");
+
+                acceptsAll(Arrays.asList("log-limit"), "Limits the maximum size of the log file (0 = unlimited)")
+                        .withRequiredArg()
+                        .ofType(Integer.class)
+                        .defaultsTo(0)
+                        .describedAs("Max log size");
+
+                acceptsAll(Arrays.asList("log-count"), "Specified how many log files to cycle through")
+                        .withRequiredArg()
+                        .ofType(Integer.class)
+                        .defaultsTo(1)
+                        .describedAs("Log count");
+
+                acceptsAll(Arrays.asList("log-append"), "Whether to append to the log file")
+                        .withRequiredArg()
+                        .ofType(Boolean.class)
+                        .defaultsTo(true)
+                        .describedAs("Log append");
+
+                acceptsAll(Arrays.asList("log-strip-color"), "Strips color codes from log file");
+
+                acceptsAll(Arrays.asList("b", "bukkit-settings"), "File for bukkit settings")
+                        .withRequiredArg()
+                        .ofType(File.class)
+                        .defaultsTo(new File("bukkit.yml"))
+                        .describedAs("Yml file");
+
+                acceptsAll(Arrays.asList("C", "commands-settings"), "File for command settings")
+                         .withRequiredArg()
+                         .ofType(File.class)
+                         .defaultsTo(new File("commands.yml"))
+                         .describedAs("Yml file");
+
+                acceptsAll(Arrays.asList("nojline"), "Disables jline and emulates the vanilla console");
+
+                acceptsAll(Arrays.asList("noconsole"), "Disables the console");
+
+                acceptsAll(Arrays.asList("v", "version"), "Show the CraftBukkit Version");
+
+                acceptsAll(Arrays.asList("demo"), "Demo mode");
+            }
+        };
+
+        OptionSet options = null;
+
+        try {
+            options = parser.parse(args);
+        } catch (joptsimple.OptionException ex) {
+            logger.log(org.apache.logging.log4j.Level.ERROR, ex.getLocalizedMessage());
+        }
+
+        if ((options == null) || (options.has("?"))) {
+            try {
+                parser.printHelpOn(System.out);
+            } catch (IOException ex) {
+                logger.log(org.apache.logging.log4j.Level.ERROR, ex);
+            }
+        } else {
+            try {
+                // This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals
+                String jline_UnsupportedTerminal = new String(new char[] {'j','l','i','n','e','.','U','n','s','u','p','p','o','r','t','e','d','T','e','r','m','i','n','a','l'});
+                String jline_terminal = new String(new char[] {'j','l','i','n','e','.','t','e','r','m','i','n','a','l'});
+
+                useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal));
+
+                if (options.has("nojline")) {
+                    System.setProperty("user.language", "en");
+                    useJline = false;
+                }
+
+                if (!useJline) {
+                    // This ensures the terminal literal will always match the jline implementation
+                    System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName());
+                }
+
+
+                if (options.has("noconsole")) {
+                    useConsole = false;
+                }
+                // Cauldron start - initialize config
+                configFile = (File) options.valueOf("bukkit-settings");
+                commandFile = (File)options.valueOf("commands-settings");
+                configuration = YamlConfiguration.loadConfiguration(configFile);
+                configuration.options().copyDefaults(true);
+                configuration.setDefaults(YamlConfiguration.loadConfiguration(MinecraftServer.class.getClassLoader().getResourceAsStream("configurations/bukkit.yml")));
+                ConfigurationSection legacyAlias = null;
+                if (!configuration.isString("aliases")) {
+                    legacyAlias = configuration.getConfigurationSection("aliases");
+                    configuration.set("aliases", "now-in-commands.yml");
+                }
+                try {
+                    configuration.save(configFile);
+                    } catch (IOException ex) {
+                        logger.log(org.apache.logging.log4j.Level.ERROR, "Could not save " + configFile, ex);
+                }
+                if (commandFile.isFile()) {
+                    legacyAlias = null;
+                }
+                commandsConfiguration = YamlConfiguration.loadConfiguration(commandFile);
+                commandsConfiguration.options().copyDefaults(true);
+                commandsConfiguration.setDefaults(YamlConfiguration.loadConfiguration(MinecraftServer.class.getClassLoader().getResourceAsStream("configurations/commands.yml")));
+                try {
+                    commandsConfiguration.save(commandFile);
+                    } catch (IOException ex) {
+                        logger.log(org.apache.logging.log4j.Level.ERROR, "Could not save " + commandFile, ex);
+                }
+
+                // Migrate aliases from old file and add previously implicit $1- to pass all arguments
+                if (legacyAlias != null) {
+                    ConfigurationSection aliases = commandsConfiguration.createSection("aliases");
+                    for (String key : legacyAlias.getKeys(false)) {
+                        ArrayList<String> commands = new ArrayList<String>();
+
+                        if (legacyAlias.isList(key)) {
+                            for (String command : legacyAlias.getStringList(key)) {
+                                commands.add(command + " $1-");
+                            }
+                        } else {
+                            commands.add(legacyAlias.getString(key) + " $1-");
+                        }
+
+                        aliases.set(key, commands);
+                    }
+                }
+
+                try {
+                    commandsConfiguration.save(commandFile);
+                    } catch (IOException ex) {
+                        logger.log(org.apache.logging.log4j.Level.ERROR, "Could not save " + commandFile, ex);
+                }
+
+                return options;
+                // Cauldron end
+            } catch (Throwable t) {
+                t.printStackTrace();
+            }
+        }
+        return null; // Cauldron
+    }
+
     @SideOnly(Side.SERVER)
     public void setForceGamemode(boolean p_104055_1_)
     {
         this.isGamemodeForced = p_104055_1_;
     }
+
+    // CraftBukkit start
+    public static Logger getLogger()
+    {
+        return logger;
+    }
+    // CraftBukkit end
 }
