All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.minecraft.server.MinecraftServer Maven / Gradle / Ivy

package net.minecraft.server;

import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import jline.console.ConsoleReader;
import joptsimple.OptionSet;
import net.chestmc.block.BlockControllerRegistry;
import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.craftbukkit.Main;
import org.bukkit.craftbukkit.SpigotTimings;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.Proxy;
import java.security.KeyPair;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Queue;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
// CraftBukkit end

public abstract class MinecraftServer implements Runnable, ICommandListener, IAsyncTaskHandler, IMojangStatistics {

  public static final Logger LOGGER = LogManager.getLogger();
  public static final File a = new File("usercache.json");
  // CraftBukkit end
  // Spigot start
  private static final int TPS = 20;
  private static final int TICK_TIME = 1000000000 / TPS;
  private static final int SAMPLE_INTERVAL = 100;
  public static int currentTick = (int) (System.currentTimeMillis() / 50);
  private static MinecraftServer l;
  public final MethodProfiler methodProfiler = new MethodProfiler();
  public final long[] h = new long[100];
  public final Thread primaryThread;
  public final double[] recentTps = new double[3];
  protected final ICommandHandler b;
  protected final Proxy e;
  protected final Queue> j = new java.util.concurrent.ConcurrentLinkedQueue>(); // Spigot, PAIL: Rename
  private final MojangStatisticsGenerator n = new MojangStatisticsGenerator("server", this, az());
  private final List p = Lists.newArrayList();
  private final ServerPing r = new ServerPing();
  private final Random s = new Random();
  private final YggdrasilAuthenticationService V;
  private final MinecraftSessionService W;
  private final GameProfileRepository Y;
  private final UserCache Z;
  private final Object stopLock = new Object();
  public Convertable convertable;
  public File universe;
  public WorldServer[] worldServer;
  public String f;
  public int g;
  public long[][] i;
  // CraftBukkit start
  public List worlds = new ArrayList();
  public org.bukkit.craftbukkit.CraftServer server;
  public OptionSet options;
  public org.bukkit.command.ConsoleCommandSender console;
  public org.bukkit.command.RemoteConsoleCommandSender remoteConsole;
  public ConsoleReader reader;
  public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue();
  public int autosavePeriod;
  private ServerConnection q; // Spigot
  private String serverIp;
  private int u = -1;
  private PlayerList v;
  private boolean isRunning = true;
  private boolean isStopped;
  private int ticks;
  private boolean onlineMode;
  private boolean spawnAnimals;
  private boolean spawnNPCs;
  private boolean pvpMode;
  private boolean allowFlight;
  private String motd;
  private int F;
  private int G = 0;
  private KeyPair H;
  private String I;
  private String J;
  private boolean demoMode;
  private boolean M;
  private boolean N;
  private String O = "";
  private String P = "";
  private boolean Q;
  private long R;
  private String S;
  private boolean T;
  private boolean U;
  private long X = 0L;
  private final Thread serverThread;
  // Spigot end
  private long ab = az();
  // CraftBukkit start
  private boolean hasStopped = false;
  // CraftBukkit end

  public MinecraftServer(OptionSet options, Proxy proxy, File file1) {
    io.netty.util.ResourceLeakDetector.setEnabled(false); // Spigot - disable
    this.e = proxy;
    MinecraftServer.l = this;
    // this.universe = file; // CraftBukkit
    // this.q = new ServerConnection(this); // Spigot
    this.Z = new UserCache(this, file1);
    this.b = this.h();
    // this.convertable = new WorldLoaderServer(file); // CraftBukkit - moved to DedicatedServer.init
    this.V = new YggdrasilAuthenticationService(proxy, UUID.randomUUID().toString());
    this.W = this.V.createMinecraftSessionService();
    this.Y = this.V.createProfileRepository();
    // 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.getProperty("jline.terminal") == null) {
      System.setProperty("jline.terminal", "jline.UnsupportedTerminal");
      Main.useJline = false;
    }

    try {
      reader = new ConsoleReader(System.in, System.out);
      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");
        Main.useJline = false;
        reader = new ConsoleReader(System.in, System.out);
        reader.setExpandEvents(false);
      } catch (IOException ex) {
        LOGGER.warn((String) null, ex);
      }
    }
    Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));

    this.serverThread = primaryThread = new Thread(this, "Server thread"); // Moved from main
  }

  // Spigot Start
  private static double calcTps(double avg, double exp, double tps) {
    return (avg * exp) + (tps * (1 - exp));
  }

  public static void main(final OptionSet options) { // CraftBukkit - replaces main(String[] astring)
    DispenserRegistry.c();

    try {
            /* CraftBukkit start - Replace everything
            boolean flag = true;
            String s = null;
            String s1 = ".";
            String s2 = null;
            boolean flag1 = false;
            boolean flag2 = false;
            int i = -1;

            for (int j = 0; j < astring.length; ++j) {
                String s3 = astring[j];
                String s4 = j == astring.length - 1 ? null : astring[j + 1];
                boolean flag3 = false;

                if (!s3.equals("nogui") && !s3.equals("--nogui")) {
                    if (s3.equals("--port") && s4 != null) {
                        flag3 = true;

                        try {
                            i = Integer.parseInt(s4);
                        } catch (NumberFormatException numberformatexception) {
                            ;
                        }
                    } else if (s3.equals("--singleplayer") && s4 != null) {
                        flag3 = true;
                        s = s4;
                    } else if (s3.equals("--universe") && s4 != null) {
                        flag3 = true;
                        s1 = s4;
                    } else if (s3.equals("--world") && s4 != null) {
                        flag3 = true;
                        s2 = s4;
                    } else if (s3.equals("--demo")) {
                        flag1 = true;
                    } else if (s3.equals("--bonusChest")) {
                        flag2 = true;
                    }
                } else {
                    flag = false;
                }

                if (flag3) {
                    ++j;
                }
            }

            final DedicatedServer dedicatedserver = new DedicatedServer(new File(s1));

            if (s != null) {
                dedicatedserver.i(s);
            }

            if (s2 != null) {
                dedicatedserver.setWorld(s2);
            }

            if (i >= 0) {
                dedicatedserver.setPort(i);
            }

            if (flag1) {
                dedicatedserver.b(true);
            }

            if (flag2) {
                dedicatedserver.c(true);
            }

            if (flag && !GraphicsEnvironment.isHeadless()) {
                dedicatedserver.aQ();
            }

            dedicatedserver.D();
            Runtime.getRuntime().addShutdownHook(new Thread("Server Shutdown Thread") {
                public void run() {
                    dedicatedserver.stop();
                }
            });
            */

      DedicatedServer dedicatedserver = new DedicatedServer(options);

      if (options.has("port")) {
        int port = (Integer) options.valueOf("port");
        if (port > 0) {
          dedicatedserver.setPort(port);
        }
      }

      if (options.has("universe")) {
        dedicatedserver.universe = (File) options.valueOf("universe");
      }

      if (options.has("world")) {
        dedicatedserver.setWorld((String) options.valueOf("world"));
      }

      dedicatedserver.primaryThread.start();
      // CraftBukkit end
    } catch (Exception exception) {
      MinecraftServer.LOGGER.fatal("Failed to start the minecraft server", exception);
    }

  }

  public static MinecraftServer getServer() {
    return MinecraftServer.l;
  }

  public static long az() {
    return System.currentTimeMillis();
  }

  public abstract PropertyManager getPropertyManager();

  protected CommandDispatcher h() {
    return new CommandDispatcher();
  }

  protected abstract boolean init() throws IOException;

  protected void a(String s) {
    if (this.getConvertable().isConvertable(s)) {
      MinecraftServer.LOGGER.info("Converting map!");
      this.b("menu.convertingLevel");
      this.getConvertable().convert(s, new IProgressUpdate() {
        private long b = System.currentTimeMillis();

        public void a(String s) {
        }

        public void a(int i) {
          if (System.currentTimeMillis() - this.b >= 1000L) {
            this.b = System.currentTimeMillis();
            MinecraftServer.LOGGER.info("Converting... " + i + "%");
          }

        }

        public void c(String s) {
        }
      });
    }

  }

  protected synchronized void b(String s) {
    this.S = s;
  }

  protected void a(String s, String s1, long i, WorldType worldtype, String s2) {
    this.a(s);
    this.b("menu.loadingLevel");
    this.worldServer = new WorldServer[3];
        /* CraftBukkit start - Remove ticktime arrays and worldsettings
        this.i = new long[this.worldServer.length][100];
        IDataManager idatamanager = this.convertable.a(s, true);

        this.a(this.U(), idatamanager);
        WorldData worlddata = idatamanager.getWorldData();
        WorldSettings worldsettings;

        if (worlddata == null) {
            if (this.X()) {
                worldsettings = DemoWorldServer.a;
            } else {
                worldsettings = new WorldSettings(i, this.getGamemode(), this.getGenerateStructures(), this.isHardcore(), worldtype);
                worldsettings.setGeneratorSettings(s2);
                if (this.M) {
                    worldsettings.a();
                }
            }

            worlddata = new WorldData(worldsettings, s1);
        } else {
            worlddata.a(s1);
            worldsettings = new WorldSettings(worlddata);
        }
        */
    int worldCount = 3;

    for (int j = 0; j < worldCount; ++j) {
      WorldServer world;
      byte dimension = 0;

      if (j == 1) {
        if (getAllowNether()) {
          dimension = -1;
        } else {
          continue;
        }
      }

      if (j == 2) {
        if (server.getAllowEnd()) {
          dimension = 1;
        } else {
          continue;
        }
      }

      String worldType = org.bukkit.World.Environment.getEnvironment(dimension).toString().toLowerCase();
      String name = (dimension == 0) ? s : s + "_" + worldType;

      org.bukkit.generator.ChunkGenerator gen = this.server.getGenerator(name);
      WorldSettings worldsettings = new WorldSettings(i, this.getGamemode(), this.getGenerateStructures(), this.isHardcore(), worldtype);
      worldsettings.setGeneratorSettings(s2);

      if (j == 0) {
        IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), s1, true);
        WorldData worlddata = idatamanager.getWorldData();
        if (worlddata == null) {
          worlddata = new WorldData(worldsettings, s1);
        }
        worlddata.checkName(s1); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
        if (this.X()) {
          world = (WorldServer) (new DemoWorldServer(this, idatamanager, worlddata, dimension, this.methodProfiler)).b();
        } else {
          world = (WorldServer) (new WorldServer(this, idatamanager, worlddata, dimension, this.methodProfiler, org.bukkit.World.Environment.getEnvironment(dimension), gen)).b();
        }

        world.a(worldsettings);
        this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard());
      } else {
        String dim = "DIM" + dimension;

        File newWorld = new File(new File(name), dim);
        File oldWorld = new File(new File(s), dim);

        if ((!newWorld.isDirectory()) && (oldWorld.isDirectory())) {
          MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder required ----");
          MinecraftServer.LOGGER.info("Unfortunately due to the way that Minecraft implemented multiworld support in 1.6, Bukkit requires that you move your " + worldType + " folder to a new location in order to operate correctly.");
          MinecraftServer.LOGGER.info("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Bukkit in the future.");
          MinecraftServer.LOGGER.info("Attempting to move " + oldWorld + " to " + newWorld + "...");

          if (newWorld.exists()) {
            MinecraftServer.LOGGER.warn("A file or folder already exists at " + newWorld + "!");
            MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----");
          } else if (newWorld.getParentFile().mkdirs()) {
            if (oldWorld.renameTo(newWorld)) {
              MinecraftServer.LOGGER.info("Success! To restore " + worldType + " in the future, simply move " + newWorld + " to " + oldWorld);
              // Migrate world data too.
              try {
                com.google.common.io.Files.copy(new File(new File(s), "level.dat"), new File(new File(name), "level.dat"));
              } catch (IOException exception) {
                MinecraftServer.LOGGER.warn("Unable to migrate world data.");
              }
              MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder complete ----");
            } else {
              MinecraftServer.LOGGER.warn("Could not move folder " + oldWorld + " to " + newWorld + "!");
              MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----");
            }
          } else {
            MinecraftServer.LOGGER.warn("Could not create path for " + newWorld + "!");
            MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----");
          }
        }

        IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), name, true);
        // world =, b0 to dimension, s1 to name, added Environment and gen
        WorldData worlddata = idatamanager.getWorldData();
        if (worlddata == null) {
          worlddata = new WorldData(worldsettings, name);
        }
        worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
        world = (WorldServer) new SecondaryWorldServer(this, idatamanager, dimension, this.worlds.get(0), this.methodProfiler, worlddata, org.bukkit.World.Environment.getEnvironment(dimension), gen).b();
      }

      this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld()));

      world.addIWorldAccess(new WorldManager(this, world));
      if (!this.T()) {
        world.getWorldData().setGameType(this.getGamemode());
      }

      worlds.add(world);
      getPlayerList().setPlayerFileData(worlds.toArray(new WorldServer[worlds.size()]));
    }

    // CraftBukkit end
    this.a(this.getDifficulty());
    this.k();
  }

  protected void k() {
    boolean flag = true;
    boolean flag1 = true;
    boolean flag2 = true;
    boolean flag3 = true;
    int i = 0;

    this.b("menu.generatingTerrain");
    byte b0 = 0;

    // CraftBukkit start - fire WorldLoadEvent and handle whether or not to keep the spawn in memory
    for (int m = 0; m < worlds.size(); m++) {
      WorldServer worldserver = this.worlds.get(m);
      LOGGER.info("Preparing start region for level " + m + " (Seed: " + worldserver.getSeed() + ")");

      if (!worldserver.getWorld().getKeepSpawnInMemory()) {
        continue;
      }

      BlockPosition blockposition = worldserver.getSpawn();
      long j = az();
      i = 0;

      for (int k = -192; k <= 192 && this.isRunning(); k += 16) {
        for (int l = -192; l <= 192 && this.isRunning(); l += 16) {
          long i1 = az();

          if (i1 - j > 1000L) {
            this.a_("Preparing spawn area", i * 100 / 625);
            j = i1;
          }

          ++i;
          worldserver.chunkProviderServer.getChunkAt(blockposition.getX() + k >> 4, blockposition.getZ() + l >> 4);
        }
      }
    }

    for (WorldServer world : this.worlds) {

      // ChestMC start - support for block handlers.
      BlockControllerRegistry.INSTANCE.load(world.getWorld());
      // ChestMC end.

      this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(world.getWorld()));
    }
    // CraftBukkit end
    this.s();
  }

  protected void a(String s, IDataManager idatamanager) {
    File file = new File(idatamanager.getDirectory(), "resources.zip");

    if (file.isFile()) {
      this.setResourcePack("level://" + s + "/" + file.getName(), "");
    }

  }

  public abstract boolean getGenerateStructures();

  public abstract WorldSettings.EnumGamemode getGamemode();

  public void setGamemode(WorldSettings.EnumGamemode worldsettings_enumgamemode) {
    // CraftBukkit start
    for (int i = 0; i < this.worlds.size(); ++i) {
      getServer().worlds.get(i).getWorldData().setGameType(worldsettings_enumgamemode);
    }

  }

  public abstract EnumDifficulty getDifficulty();

  public abstract boolean isHardcore();

  public abstract int p();
  // CraftBukkit end

  public abstract boolean q();

  public abstract boolean r();

  protected void a_(String s, int i) {
    this.f = s;
    this.g = i;
    MinecraftServer.LOGGER.info(s + ": " + i + "%");
  }

  protected void s() {
    this.f = null;
    this.g = 0;

    this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); // CraftBukkit
  }

  protected void saveChunks(boolean flag) throws ExceptionWorldConflict { // CraftBukkit - added throws
    if (!this.N) {
      WorldServer[] aworldserver = this.worldServer;
      int i = aworldserver.length;

      // CraftBukkit start
      for (int j = 0; j < worlds.size(); ++j) {
        WorldServer worldserver = worlds.get(j);
        // CraftBukkit end

        if (worldserver != null) {
          if (!flag) {
            MinecraftServer.LOGGER.info("Saving chunks for level '" + worldserver.getWorldData().getName() + "'/" + worldserver.worldProvider.getName());
          }

          try {
            worldserver.save(true, null);
            worldserver.saveLevel(); // CraftBukkit
          } catch (ExceptionWorldConflict exceptionworldconflict) {
            MinecraftServer.LOGGER.warn(exceptionworldconflict.getMessage());
          }
        }
      }

    }
  }

  public void stop() throws ExceptionWorldConflict { // CraftBukkit - added throws
    // CraftBukkit start - prevent double stopping on multiple threads
    synchronized (stopLock) {
      if (hasStopped) return;
      hasStopped = true;
    }
    // CraftBukkit end
    if (!this.N) {
      MinecraftServer.LOGGER.info("Stopping server");
      // CraftBukkit start
      if (this.server != null) {
        this.server.disablePlugins();
      }
      // CraftBukkit end
      if (this.aq() != null) {
        this.aq().b();
      }

      if (this.v != null) {
        MinecraftServer.LOGGER.info("Saving players");
        this.v.savePlayers();
        this.v.u();
        try {
          Thread.sleep(100);
        } catch (InterruptedException ex) {
        } // CraftBukkit - SPIGOT-625 - give server at least a chance to send packets
      }

      if (this.worldServer != null) {
        MinecraftServer.LOGGER.info("Saving worlds");
        this.saveChunks(false);

                /* CraftBukkit start - Handled in saveChunks
                for (int i = 0; i < this.worldServer.length; ++i) {
                    WorldServer worldserver = this.worldServer[i];

                    worldserver.saveLevel();
                }
                // CraftBukkit end */
      }

      if (this.n.d()) {
        this.n.e();
      }
      // Spigot start
      if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) {
        LOGGER.info("Saving usercache.json");
        this.Z.c();
      }
      //Spigot end
    }
  }
  // Spigot End

  public String getServerIp() {
    return this.serverIp;
  }

  public void c(String s) {
    this.serverIp = s;
  }

  public boolean isRunning() {
    return this.isRunning;
  }

  public void safeShutdown() {
    this.isRunning = false;
  }

  public void run() {
    try {
      if (this.init()) {
        this.ab = az();
        long i = 0L;

        this.r.setMOTD(new ChatComponentText(this.motd));
        this.r.setServerInfo(new ServerPing.ServerData("1.8.8", 47));
        this.a(this.r);

        // Spigot start
        Arrays.fill(recentTps, 20);
        long lastTick = System.nanoTime(), catchupTime = 0, curTime, wait, tickSection = lastTick;
        while (this.isRunning) {
          curTime = System.nanoTime();
          wait = TICK_TIME - (curTime - lastTick) - catchupTime;
          if (wait > 0) {
            Thread.sleep(wait / 1000000);
            catchupTime = 0;
            continue;
          } else {
            catchupTime = Math.min(1000000000, Math.abs(wait));
          }

          if (MinecraftServer.currentTick++ % SAMPLE_INTERVAL == 0) {
            double currentTps = 1E9 / (curTime - tickSection) * SAMPLE_INTERVAL;
            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)
            tickSection = curTime;
          }
          lastTick = curTime;

          this.A();
          this.Q = true;
        }
        // Spigot end
      } else {
        this.a((CrashReport) null);
      }
    } catch (Throwable throwable) {
      MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable);
      // Spigot Start
      if (throwable.getCause() != null) {
        MinecraftServer.LOGGER.error("\tCause of unexpected exception was", throwable.getCause());
      }
      // Spigot End
      CrashReport crashreport = null;

      if (throwable instanceof ReportedException) {
        crashreport = this.b(((ReportedException) throwable).a());
      } else {
        crashreport = this.b(new CrashReport("Exception in server tick loop", throwable));
      }

      File file = new File(new File(this.y(), "crash-reports"), "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt");

      if (crashreport.a(file)) {
        MinecraftServer.LOGGER.error("This crash report has been saved to: " + file.getAbsolutePath());
      } else {
        MinecraftServer.LOGGER.error("We were unable to save this crash report to disk.");
      }

      this.a(crashreport);
    } finally {
      try {
        org.spigotmc.WatchdogThread.doStop();
        this.isStopped = true;
        this.stop();
      } catch (Throwable throwable1) {
        MinecraftServer.LOGGER.error("Exception stopping the server", throwable1);
      } finally {
        // CraftBukkit start - Restore terminal to original settings
        try {
          reader.getTerminal().restore();
        } catch (Exception ignored) {
        }
        // CraftBukkit end
        this.z();
      }

    }

  }

  private void a(ServerPing serverping) {
    File file = this.d("server-icon.png");

    if (file.isFile()) {
      ByteBuf bytebuf = Unpooled.buffer();

      try {
        BufferedImage bufferedimage = ImageIO.read(file);

        Validate.validState(bufferedimage.getWidth() == 64, "Must be 64 pixels wide");
        Validate.validState(bufferedimage.getHeight() == 64, "Must be 64 pixels high");
        ImageIO.write(bufferedimage, "PNG", new ByteBufOutputStream(bytebuf));
        ByteBuf bytebuf1 = Base64.encode(bytebuf);

        serverping.setFavicon("data:image/png;base64," + bytebuf1.toString(Charsets.UTF_8));
      } catch (Exception exception) {
        MinecraftServer.LOGGER.error("Couldn't load server icon", exception);
      } finally {
        bytebuf.release();
      }
    }

  }

  public File y() {
    return new File(".");
  }

  protected void a(CrashReport crashreport) {
  }

  protected void z() {
  }

  protected void A() throws ExceptionWorldConflict { // CraftBukkit - added throws
    SpigotTimings.serverTickTimer.startTiming(); // Spigot
    long i = System.nanoTime();

    ++this.ticks;
    if (this.T) {
      this.T = false;
      this.methodProfiler.a = true;
      this.methodProfiler.a();
    }

    this.methodProfiler.a("root");
    this.B();
    if (i - this.X >= 5000000000L) {
      this.X = i;
      this.r.setPlayerSample(new ServerPing.ServerPingPlayerSample(this.J(), this.I()));
      GameProfile[] agameprofile = new GameProfile[Math.min(this.I(), 12)];
      int j = MathHelper.nextInt(this.s, 0, this.I() - agameprofile.length);

      for (int k = 0; k < agameprofile.length; ++k) {
        agameprofile[k] = this.v.v().get(j + k).getProfile();
      }

      Collections.shuffle(Arrays.asList(agameprofile));
      this.r.b().a(agameprofile);
    }

    if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit
      SpigotTimings.worldSaveTimer.startTiming(); // Spigot
      this.methodProfiler.a("save");
      this.v.savePlayers();
      // Spigot Start
      // We replace this with saving each individual world as this.saveChunks(...) is broken,
      // and causes the main thread to sleep for random amounts of time depending on chunk activity
      // Also pass flag to only save modified chunks
      server.playerCommandState = true;
      for (World world : worlds) {
        world.getWorld().save(false);
      }
      server.playerCommandState = false;
      // this.saveChunks(true);
      // Spigot End
      this.methodProfiler.b();
      SpigotTimings.worldSaveTimer.stopTiming(); // Spigot
    }

    this.methodProfiler.a("tallying");
    this.h[this.ticks % 100] = System.nanoTime() - i;
    this.methodProfiler.b();
    this.methodProfiler.a("snooper");
    if (getSnooperEnabled() && !this.n.d() && this.ticks > 100) {  // Spigot
      this.n.a();
    }

    if (getSnooperEnabled() && this.ticks % 6000 == 0) { // Spigot
      this.n.b();
    }

    this.methodProfiler.b();
    this.methodProfiler.b();
    org.spigotmc.WatchdogThread.tick(); // Spigot
    SpigotTimings.serverTickTimer.stopTiming(); // Spigot
    org.spigotmc.CustomTimingsHandler.tick(); // Spigot
  }

  public void B() {
    this.methodProfiler.a("jobs");
    Queue queue = this.j;

    // Spigot start
    FutureTask entry;
    int count = this.j.size();
    while (count-- > 0 && (entry = this.j.poll()) != null) {
      SystemUtils.a(entry, MinecraftServer.LOGGER);
    }
    // Spigot end

    this.methodProfiler.c("levels");

    SpigotTimings.schedulerTimer.startTiming(); // Spigot
    // CraftBukkit start
    this.server.getScheduler().mainThreadHeartbeat(this.ticks);
    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
    org.bukkit.craftbukkit.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.ticks % 20 == 0) {
      for (int i = 0; i < this.getPlayerList().players.size(); ++i) {
        EntityPlayer entityplayer = this.getPlayerList().players.get(i);
        entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(entityplayer.world.getTime(), entityplayer.getPlayerTime(), entityplayer.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time
      }
    }
    SpigotTimings.timeUpdateTimer.stopTiming(); // Spigot

    int i;

    for (i = 0; i < this.worlds.size(); ++i) {
      long j = System.nanoTime();

      // if (i == 0 || this.getAllowNether()) {
      WorldServer worldserver = this.worlds.get(i);

      this.methodProfiler.a(worldserver.getWorldData().getName());
                /* Drop global time updates
                if (this.ticks % 20 == 0) {
                    this.methodProfiler.a("timeSync");
                    this.v.a(new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")), worldserver.worldProvider.getDimension());
                    this.methodProfiler.b();
                }
                // CraftBukkit end */

      this.methodProfiler.a("tick");

      CrashReport crashreport;

      try {
        worldserver.timings.doTick.startTiming(); // Spigot
        worldserver.doTick();
        worldserver.timings.doTick.stopTiming(); // Spigot
      } catch (Throwable throwable) {
        // Spigot Start
        try {
          crashreport = CrashReport.a(throwable, "Exception ticking world");
        } catch (Throwable t) {
          throw new RuntimeException("Error generating crash report", t);
        }
        // Spigot End
        worldserver.a(crashreport);
        throw new ReportedException(crashreport);
      }

      try {
        worldserver.timings.tickEntities.startTiming(); // Spigot
        worldserver.tickEntities();
        worldserver.timings.tickEntities.stopTiming(); // Spigot
      } catch (Throwable throwable1) {
        // Spigot Start
        try {
          crashreport = CrashReport.a(throwable1, "Exception ticking world entities");
        } catch (Throwable t) {
          throw new RuntimeException("Error generating crash report", t);
        }
        // Spigot End
        worldserver.a(crashreport);
        throw new ReportedException(crashreport);
      }

      this.methodProfiler.b();
      this.methodProfiler.a("tracker");
      worldserver.timings.tracker.startTiming(); // Spigot
      worldserver.getTracker().updatePlayers();
      worldserver.timings.tracker.stopTiming(); // Spigot
      this.methodProfiler.b();
      this.methodProfiler.b();
      // } // CraftBukkit

      // this.i[i][this.ticks % 100] = System.nanoTime() - j; // CraftBukkit
    }

    this.methodProfiler.c("connection");
    SpigotTimings.connectionTimer.startTiming(); // Spigot
    this.aq().c();
    SpigotTimings.connectionTimer.stopTiming(); // Spigot
    this.methodProfiler.c("players");
    SpigotTimings.playerListTimer.startTiming(); // Spigot
    this.v.tick();
    SpigotTimings.playerListTimer.stopTiming(); // Spigot
    this.methodProfiler.c("tickables");

    SpigotTimings.tickablesTimer.startTiming(); // Spigot
    for (i = 0; i < this.p.size(); ++i) {
      this.p.get(i).c();
    }
    SpigotTimings.tickablesTimer.stopTiming(); // Spigot

    this.methodProfiler.b();
  }

  public boolean getAllowNether() {
    return true;
  }

  public void a(IUpdatePlayerListBox iupdateplayerlistbox) {
    this.p.add(iupdateplayerlistbox);
  }

  public void C() {
        /* CraftBukkit start - prevent abuse
        this.serverThread = new Thread(this, "Server thread");
        this.serverThread.start();
        // CraftBukkit end */
  }

  public File d(String s) {
    return new File(this.y(), s);
  }

  public void info(String s) {
    MinecraftServer.LOGGER.info(s);
  }

  public void warning(String s) {
    MinecraftServer.LOGGER.warn(s);
  }

  public WorldServer getWorldServer(int i) {
    // CraftBukkit start
    for (WorldServer world : worlds) {
      if (world.dimension == i) {
        return world;
      }
    }
    return worlds.get(0);
    // CraftBukkit end
  }

  public String E() {
    return this.serverIp;
  }

  public int F() {
    return this.u;
  }

  public String G() {
    return this.motd;
  }

  public String getVersion() {
    return "1.8.8";
  }

  public int I() {
    return this.v.getPlayerCount();
  }

  public int J() {
    return this.v.getMaxPlayers();
  }

  public String[] getPlayers() {
    return this.v.f();
  }

  public GameProfile[] L() {
    return this.v.g();
  }

  public boolean isDebugging() {
    return this.getPropertyManager().getBoolean("debug", false); // CraftBukkit - don't hardcode
  }

  public void g(String s) {
    MinecraftServer.LOGGER.error(s);
  }

  public void h(String s) {
    if (this.isDebugging()) {
      MinecraftServer.LOGGER.info(s);
    }

  }

  public String getServerModName() {
    return "Spigot"; // Spigot - Spigot > // CraftBukkit - cb > vanilla!
  }

  public CrashReport b(CrashReport crashreport) {
    crashreport.g().a("Profiler Position", new Callable() {
      public String a() throws Exception {
        return MinecraftServer.this.methodProfiler.a ? MinecraftServer.this.methodProfiler.c() : "N/A (disabled)";
      }

      public Object call() throws Exception {
        return this.a();
      }
    });
    if (this.v != null) {
      crashreport.g().a("Player Count", new Callable() {
        public String a() {
          return MinecraftServer.this.v.getPlayerCount() + " / " + MinecraftServer.this.v.getMaxPlayers() + "; " + MinecraftServer.this.v.v();
        }

        public Object call() throws Exception {
          return this.a();
        }
      });
    }

    return crashreport;
  }

  public List tabCompleteCommand(ICommandListener icommandlistener, String s, BlockPosition blockposition) {
        /* CraftBukkit start - Allow tab-completion of Bukkit commands
        ArrayList arraylist = Lists.newArrayList();

        if (s.startsWith("/")) {
            s = s.substring(1);
            boolean flag = !s.contains(" ");
            List list = this.b.a(icommandlistener, s, blockposition);

            if (list != null) {
                Iterator iterator = list.iterator();

                while (iterator.hasNext()) {
                    String s1 = (String) iterator.next();

                    if (flag) {
                        arraylist.add("/" + s1);
                    } else {
                        arraylist.add(s1);
                    }
                }
            }

            return arraylist;
        } else {
            String[] astring = s.split(" ", -1);
            String s2 = astring[astring.length - 1];
            String[] astring1 = this.v.f();
            int i = astring1.length;

            for (int j = 0; j < i; ++j) {
                String s3 = astring1[j];

                if (CommandAbstract.a(s2, s3)) {
                    arraylist.add(s3);
                }
            }

            return arraylist;
        }
        */
    return server.tabComplete(icommandlistener, s);
    // CraftBukkit end
  }

  public boolean O() {
    return true; // CraftBukkit
  }

  public String getName() {
    return "Server";
  }

  public void sendMessage(IChatBaseComponent ichatbasecomponent) {
    MinecraftServer.LOGGER.info(ichatbasecomponent.c());
  }

  public boolean a(int i, String s) {
    return true;
  }

  public ICommandHandler getCommandHandler() {
    return this.b;
  }

  public KeyPair Q() {
    return this.H;
  }

  public int R() {
    return this.u;
  }

  public void setPort(int i) {
    this.u = i;
  }

  public String S() {
    return this.I;
  }

  public void i(String s) {
    this.I = s;
  }

  public boolean T() {
    return this.I != null;
  }

  public String U() {
    return this.J;
  }

  public void a(KeyPair keypair) {
    this.H = keypair;
  }

  public void a(EnumDifficulty enumdifficulty) {
    // CraftBukkit start
    for (int i = 0; i < this.worlds.size(); ++i) {
      WorldServer worldserver = this.worlds.get(i);
      // CraftBukkit end

      if (worldserver != null) {
        if (worldserver.getWorldData().isHardcore()) {
          worldserver.getWorldData().setDifficulty(EnumDifficulty.HARD);
          worldserver.setSpawnFlags(true, true);
        } else if (this.T()) {
          worldserver.getWorldData().setDifficulty(enumdifficulty);
          worldserver.setSpawnFlags(worldserver.getDifficulty() != EnumDifficulty.PEACEFUL, true);
        } else {
          worldserver.getWorldData().setDifficulty(enumdifficulty);
          worldserver.setSpawnFlags(this.getSpawnMonsters(), this.spawnAnimals);
        }
      }
    }

  }

  protected boolean getSpawnMonsters() {
    return true;
  }

  public boolean X() {
    return this.demoMode;
  }

  public void b(boolean flag) {
    this.demoMode = flag;
  }

  public void c(boolean flag) {
    this.M = flag;
  }

  public Convertable getConvertable() {
    return this.convertable;
  }

  public void aa() {
    this.N = true;
    this.getConvertable().d();

    // CraftBukkit start
    for (int i = 0; i < this.worlds.size(); ++i) {
      WorldServer worldserver = this.worlds.get(i);
      // CraftBukkit end

      if (worldserver != null) {
        worldserver.saveLevel();
      }
    }

    this.getConvertable().e(this.worlds.get(0).getDataManager().g()); // CraftBukkit
    this.safeShutdown();
  }

  public String getResourcePack() {
    return this.O;
  }

  public String getResourcePackHash() {
    return this.P;
  }

  public void setResourcePack(String s, String s1) {
    this.O = s;
    this.P = s1;
  }

  public void a(MojangStatisticsGenerator mojangstatisticsgenerator) {
    mojangstatisticsgenerator.a("whitelist_enabled", Boolean.valueOf(false));
    mojangstatisticsgenerator.a("whitelist_count", Integer.valueOf(0));
    if (this.v != null) {
      mojangstatisticsgenerator.a("players_current", Integer.valueOf(this.I()));
      mojangstatisticsgenerator.a("players_max", Integer.valueOf(this.J()));
      mojangstatisticsgenerator.a("players_seen", Integer.valueOf(this.v.getSeenPlayers().length));
    }

    mojangstatisticsgenerator.a("uses_auth", Boolean.valueOf(this.onlineMode));
    mojangstatisticsgenerator.a("gui_state", this.as() ? "enabled" : "disabled");
    mojangstatisticsgenerator.a("run_time", Long.valueOf((az() - mojangstatisticsgenerator.g()) / 60L * 1000L));
    mojangstatisticsgenerator.a("avg_tick_ms", Integer.valueOf((int) (MathHelper.a(this.h) * 1.0E-6D)));
    int i = 0;

    if (this.worldServer != null) {
      // CraftBukkit start
      for (int j = 0; j < this.worlds.size(); ++j) {
        WorldServer worldserver = this.worlds.get(j);
        if (worldserver != null) {
          // CraftBukkit end
          WorldData worlddata = worldserver.getWorldData();

          mojangstatisticsgenerator.a("world[" + i + "][dimension]", Integer.valueOf(worldserver.worldProvider.getDimension()));
          mojangstatisticsgenerator.a("world[" + i + "][mode]", worlddata.getGameType());
          mojangstatisticsgenerator.a("world[" + i + "][difficulty]", worldserver.getDifficulty());
          mojangstatisticsgenerator.a("world[" + i + "][hardcore]", Boolean.valueOf(worlddata.isHardcore()));
          mojangstatisticsgenerator.a("world[" + i + "][generator_name]", worlddata.getType().name());
          mojangstatisticsgenerator.a("world[" + i + "][generator_version]", Integer.valueOf(worlddata.getType().getVersion()));
          mojangstatisticsgenerator.a("world[" + i + "][height]", Integer.valueOf(this.F));
          mojangstatisticsgenerator.a("world[" + i + "][chunks_loaded]", Integer.valueOf(worldserver.N().getLoadedChunks()));
          ++i;
        }
      }
    }

    mojangstatisticsgenerator.a("worlds", Integer.valueOf(i));
  }

  public void b(MojangStatisticsGenerator mojangstatisticsgenerator) {
    mojangstatisticsgenerator.b("singleplayer", Boolean.valueOf(this.T()));
    mojangstatisticsgenerator.b("server_brand", this.getServerModName());
    mojangstatisticsgenerator.b("gui_supported", GraphicsEnvironment.isHeadless() ? "headless" : "supported");
    mojangstatisticsgenerator.b("dedicated", Boolean.valueOf(this.ae()));
  }

  public boolean getSnooperEnabled() {
    return true;
  }

  public abstract boolean ae();

  public boolean getOnlineMode() {
    return server.getOnlineMode(); // CraftBukkit
  }

  public void setOnlineMode(boolean flag) {
    this.onlineMode = flag;
  }

  public boolean getSpawnAnimals() {
    return this.spawnAnimals;
  }

  public void setSpawnAnimals(boolean flag) {
    this.spawnAnimals = flag;
  }

  public boolean getSpawnNPCs() {
    return this.spawnNPCs;
  }

  public void setSpawnNPCs(boolean flag) {
    this.spawnNPCs = flag;
  }

  public abstract boolean ai();

  public boolean getPVP() {
    return this.pvpMode;
  }

  public void setPVP(boolean flag) {
    this.pvpMode = flag;
  }

  public boolean getAllowFlight() {
    return this.allowFlight;
  }

  public void setAllowFlight(boolean flag) {
    this.allowFlight = flag;
  }

  public abstract boolean getEnableCommandBlock();

  public String getMotd() {
    return this.motd;
  }

  public void setMotd(String s) {
    this.motd = s;
  }

  public int getMaxBuildHeight() {
    return this.F;
  }

  public void c(int i) {
    this.F = i;
  }

  public boolean isStopped() {
    return this.isStopped;
  }

  public PlayerList getPlayerList() {
    return this.v;
  }

  public void a(PlayerList playerlist) {
    this.v = playerlist;
  }

  // Spigot Start
  public ServerConnection getServerConnection() {
    return this.q;
  }

  // Spigot End
  public ServerConnection aq() {
    return this.q == null ? this.q = new ServerConnection(this) : this.q; // Spigot
  }

  public boolean as() {
    return false;
  }

  public abstract String a(WorldSettings.EnumGamemode worldsettings_enumgamemode, boolean flag);

  public int at() {
    return this.ticks;
  }

  public void au() {
    this.T = true;
  }

  public BlockPosition getChunkCoordinates() {
    return BlockPosition.ZERO;
  }

  public Vec3D d() {
    return new Vec3D(0.0D, 0.0D, 0.0D);
  }

  public World getWorld() {
    return this.worlds.get(0); // CraftBukkit
  }

  public void setWorld(String s) {
    this.J = s;
  }

  public Entity f() {
    return null;
  }

  public int getSpawnProtection() {
    return 16;
  }

  public boolean a(World world, BlockPosition blockposition, EntityHuman entityhuman) {
    return false;
  }

  public boolean getForceGamemode() {
    return this.U;
  }

  public void setForceGamemode(boolean flag) {
    this.U = flag;
  }

  public Proxy ay() {
    return this.e;
  }

  public int getIdleTimeout() {
    return this.G;
  }

  public void setIdleTimeout(int i) {
    this.G = i;
  }

  public IChatBaseComponent getScoreboardDisplayName() {
    return new ChatComponentText(this.getName());
  }

  public boolean aB() {
    return true;
  }

  public MinecraftSessionService aD() {
    return this.W;
  }

  public GameProfileRepository getGameProfileRepository() {
    return this.Y;
  }

  public UserCache getUserCache() {
    return this.Z;
  }

  public ServerPing aG() {
    return this.r;
  }

  public void aH() {
    this.X = 0L;
  }

  public Entity a(UUID uuid) {
    WorldServer[] aworldserver = this.worldServer;
    int i = aworldserver.length;

    // CraftBukkit start
    for (int j = 0; j < worlds.size(); ++j) {
      WorldServer worldserver = worlds.get(j);
      // CraftBukkit end

      if (worldserver != null) {
        Entity entity = worldserver.getEntity(uuid);

        if (entity != null) {
          return entity;
        }
      }
    }

    return null;
  }

  public boolean getSendCommandFeedback() {
    return getServer().worlds.get(0).getGameRules().getBoolean("sendCommandFeedback");
  }

  public void a(CommandObjectiveExecutor.EnumCommandResult commandobjectiveexecutor_enumcommandresult, int i) {
  }

  public int aI() {
    return 29999984;
  }

  public  ListenableFuture a(Callable callable) {
    Validate.notNull(callable);
    if (!this.isMainThread()) { // CraftBukkit && !this.isStopped()) {
      ListenableFutureTask listenablefuturetask = ListenableFutureTask.create(callable);
      Queue queue = this.j;

      // Spigot start
      this.j.add(listenablefuturetask);
      return listenablefuturetask;
      // Spigot end
    } else {
      try {
        return Futures.immediateFuture(callable.call());
      } catch (Exception exception) {
        return Futures.immediateFailedCheckedFuture(exception);
      }
    }
  }

  public ListenableFuture postToMainThread(Runnable runnable) {
    Validate.notNull(runnable);
    return this.a(Executors.callable(runnable));
  }

  public boolean isMainThread() {
    return Thread.currentThread() == this.serverThread;
  }

  public int aK() {
    return 256;
  }

  public long aL() {
    return this.ab;
  }

  public Thread aM() {
    return this.serverThread;
  }
}