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

org.bukkit.craftbukkit.CraftServer Maven / Gradle / Ivy

package org.bukkit.craftbukkit;

import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.SQLitePlatform;
import com.avaje.ebeaninternal.server.lib.sql.TransactionIsolation;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.mojang.authlib.GameProfile;
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 net.chestmc.library.Libraries;
import net.chestmc.library.Library;
import net.chestmc.library.LibraryRegistry;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.server.WorldType;
import net.minecraft.server.*;
import org.apache.commons.lang.Validate;
import org.bukkit.*;
import org.bukkit.World;
import org.bukkit.Warning.WarningState;
import org.bukkit.World.Environment;
import org.bukkit.command.CommandException;
import org.bukkit.command.*;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.conversations.Conversable;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.generator.CraftChunkData;
import org.bukkit.craftbukkit.help.SimpleHelpMap;
import org.bukkit.craftbukkit.inventory.*;
import org.bukkit.craftbukkit.map.CraftMapView;
import org.bukkit.craftbukkit.metadata.EntityMetadataStore;
import org.bukkit.craftbukkit.metadata.PlayerMetadataStore;
import org.bukkit.craftbukkit.metadata.WorldMetadataStore;
import org.bukkit.craftbukkit.potion.CraftPotionBrewer;
import org.bukkit.craftbukkit.scheduler.CraftScheduler;
import org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager;
import org.bukkit.craftbukkit.util.CraftIconCache;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.DatFileFilter;
import org.bukkit.craftbukkit.util.Versioning;
import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerChatTabCompleteEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.help.HelpMap;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.*;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.*;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.plugin.messaging.Messenger;
import org.bukkit.plugin.messaging.StandardMessenger;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitWorker;
import org.bukkit.util.StringUtil;
import org.bukkit.util.permissions.DefaultPermissions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.error.MarkedYAMLException;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

public final class CraftServer implements Server {
  private static final Player[] EMPTY_PLAYER_ARRAY = new Player[0];

  static {
    ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
    CraftItemFactory.instance();
  }

  protected final MinecraftServer console;
  protected final DedicatedPlayerList playerList;
  private final String serverName = "CraftBukkit";
  private final String serverVersion;
  private final String bukkitVersion = Versioning.getBukkitVersion();
  private final Logger logger = Logger.getLogger("Minecraft");
  private final ServicesManager servicesManager = new SimpleServicesManager();
  private final CraftScheduler scheduler = new CraftScheduler();
  private final SimpleCommandMap commandMap = new SimpleCommandMap(this);
  private final SimpleHelpMap helpMap = new SimpleHelpMap(this);
  private final StandardMessenger messenger = new StandardMessenger();
  private final PluginManager pluginManager = new SimplePluginManager(this, commandMap);
  private final Map worlds = new LinkedHashMap();
  private final Yaml yaml = new Yaml(new SafeConstructor());
  private final Map offlinePlayers = new MapMaker().softValues().makeMap();
  private final EntityMetadataStore entityMetadata = new EntityMetadataStore();
  private final PlayerMetadataStore playerMetadata = new PlayerMetadataStore();
  private final WorldMetadataStore worldMetadata = new WorldMetadataStore();
  private final BooleanWrapper online = new BooleanWrapper();
  private final Pattern validUserPattern = Pattern.compile("^[a-zA-Z0-9_]{2,16}$");
  private final UUID invalidUserUUID = UUID.nameUUIDFromBytes("InvalidUsername".getBytes(Charsets.UTF_8));
  private final List playerView;
  private final Spigot spigot = new Spigot() {

    @Override
    public YamlConfiguration getConfig() {
      return org.spigotmc.SpigotConfig.config;
    }

    @Override
    public void restart() {
      org.spigotmc.RestartCommand.restart();
    }

    @Override
    public void broadcast(BaseComponent component) {
      for (Player player : getOnlinePlayers()) {
        player.spigot().sendMessage(component);
      }
    }

    @Override
    public void broadcast(BaseComponent... components) {
      for (Player player : getOnlinePlayers()) {
        player.spigot().sendMessage(components);
      }
    }
  };
  public int chunkGCPeriod = -1;
  public int chunkGCLoadThresh = 0;
  public CraftScoreboardManager scoreboardManager;
  public boolean playerCommandState;
  public int reloadCount;
  private YamlConfiguration configuration;
  private YamlConfiguration commandsConfiguration;
  private int monsterSpawn = -1;
  private int animalSpawn = -1;
  private int waterAnimalSpawn = -1;
  private int ambientSpawn = -1;
  private File container;
  private WarningState warningState = WarningState.DEFAULT;
  private boolean printSaveWarning;
  private CraftIconCache icon;
  private boolean overrideAllCommandBlockCommands = false;

  public CraftServer(MinecraftServer console, PlayerList playerList) {
    this.console = console;
    this.playerList = (DedicatedPlayerList) playerList;
    this.playerView = Collections.unmodifiableList(Lists.transform(playerList.players, new Function() {
      @Override
      public CraftPlayer apply(EntityPlayer player) {
        return player.getBukkitEntity();
      }
    }));
    this.serverVersion = CraftServer.class.getPackage().getImplementationVersion();
    online.value = console.getPropertyManager().getBoolean("online-mode", true);

    Bukkit.setServer(this);

    // Register all the Enchantments and PotionTypes now so we can stop new registration immediately after
    Enchantment.DAMAGE_ALL.getClass();
    org.bukkit.enchantments.Enchantment.stopAcceptingRegistrations();

    Potion.setPotionBrewer(new CraftPotionBrewer());
    MobEffectList.BLINDNESS.getClass();
    PotionEffectType.stopAcceptingRegistrations();
    // Ugly hack :(

    if (!Main.useConsole) {
      getLogger().info("Console input is disabled due to --noconsole command argument");
    }

    configuration = YamlConfiguration.loadConfiguration(getConfigFile());
    configuration.options().copyDefaults(true);
    configuration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/bukkit.yml"), Charsets.UTF_8)));
    ConfigurationSection legacyAlias = null;
    if (!configuration.isString("aliases")) {
      legacyAlias = configuration.getConfigurationSection("aliases");
      configuration.set("aliases", "now-in-commands.yml");
    }
    saveConfig();
    if (getCommandsConfigFile().isFile()) {
      legacyAlias = null;
    }
    commandsConfiguration = YamlConfiguration.loadConfiguration(getCommandsConfigFile());
    commandsConfiguration.options().copyDefaults(true);
    commandsConfiguration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/commands.yml"), Charsets.UTF_8)));
    saveCommandsConfig();

    // 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 commands = new ArrayList();

        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);
      }
    }

    saveCommandsConfig();
    overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");
    ((SimplePluginManager) pluginManager).useTimings(configuration.getBoolean("settings.plugin-profiling"));
    monsterSpawn = configuration.getInt("spawn-limits.monsters");
    animalSpawn = configuration.getInt("spawn-limits.animals");
    waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals");
    ambientSpawn = configuration.getInt("spawn-limits.ambient");
    console.autosavePeriod = configuration.getInt("ticks-per.autosave");
    warningState = WarningState.value(configuration.getString("settings.deprecated-verbose"));
    chunkGCPeriod = configuration.getInt("chunk-gc.period-in-ticks");
    chunkGCLoadThresh = configuration.getInt("chunk-gc.load-threshold");
    loadIcon();

    // Spigot Start - Moved to old location of new DedicatedPlayerList in DedicatedServer
    // loadPlugins();
    // enablePlugins(PluginLoadOrder.STARTUP);
    // Spigot End
  }

  static CraftIconCache loadServerIcon0(File file) throws Exception {
    return loadServerIcon0(ImageIO.read(file));
  }

  static CraftIconCache loadServerIcon0(BufferedImage image) throws Exception {
    ByteBuf bytebuf = Unpooled.buffer();

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

    return new CraftIconCache("data:image/png;base64," + bytebuf1.toString(Charsets.UTF_8));
  }

  public boolean getCommandBlockOverride(String command) {
    return overrideAllCommandBlockCommands || commandsConfiguration.getStringList("command-block-overrides").contains(command);
  }

  private File getConfigFile() {
    return (File) console.options.valueOf("bukkit-settings");
  }

  private File getCommandsConfigFile() {
    return (File) console.options.valueOf("commands-settings");
  }

  private void saveConfig() {
    try {
      configuration.save(getConfigFile());
    } catch (IOException ex) {
      Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + getConfigFile(), ex);
    }
  }

  private void saveCommandsConfig() {
    try {
      commandsConfiguration.save(getCommandsConfigFile());
    } catch (IOException ex) {
      Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + getCommandsConfigFile(), ex);
    }
  }

  public void loadPlugins() {
    pluginManager.registerInterface(JavaPluginLoader.class);

    File pluginFolder = (File) console.options.valueOf("plugins");

    if (pluginFolder.exists()) {
      Plugin[] plugins = pluginManager.loadPlugins(pluginFolder);
      for (Plugin plugin : plugins) {

        // ChestMC start - allow declaring libraries to download before loading.
        Class pluginClass = plugin.getClass();
        Libraries libraries = pluginClass.getAnnotation(Libraries.class);
        if (libraries != null) {
          for (Library lib : libraries.libraries()) {
            LibraryRegistry.INSTANCE.downloadFromMaven(lib.group(), lib.artifact(), lib.version());
          }
        }
        // ChestMC end.

        try {
          String message = String.format("Loading %s", plugin.getDescription().getFullName());
          plugin.getLogger().info(message);
          plugin.onLoad();
        } catch (Throwable ex) {
          Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, ex.getMessage() + " initializing " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
        }
      }
    } else {
      pluginFolder.mkdir();
    }
  }

  public void enablePlugins(PluginLoadOrder type) {
    if (type == PluginLoadOrder.STARTUP) {
      helpMap.clear();
      helpMap.initializeGeneralTopics();
    }

    Plugin[] plugins = pluginManager.getPlugins();

    for (Plugin plugin : plugins) {
      if ((!plugin.isEnabled()) && (plugin.getDescription().getLoad() == type)) {
        loadPlugin(plugin);
      }
    }

    if (type == PluginLoadOrder.POSTWORLD) {
      // Spigot start - Allow vanilla commands to be forced to be the main command
      setVanillaCommands(true);
      commandMap.setFallbackCommands();
      setVanillaCommands(false);
      // Spigot end
      commandMap.registerServerAliases();
      loadCustomPermissions();
      DefaultPermissions.registerCorePermissions();
      CraftDefaultPermissions.registerCorePermissions();
      helpMap.initializeCommands();
    }
  }

  public void disablePlugins() {
    pluginManager.disablePlugins();
  }

  private void setVanillaCommands(boolean first) { // Spigot
    Map commands = new CommandDispatcher().getCommands();
    for (ICommand cmd : commands.values()) {
      // Spigot start
      VanillaCommandWrapper wrapper = new VanillaCommandWrapper((CommandAbstract) cmd, LocaleI18n.get(cmd.getUsage(null)));
      if (org.spigotmc.SpigotConfig.replaceCommands.contains(wrapper.getName())) {
        if (first) {
          commandMap.register("minecraft", wrapper);
        }
      } else if (!first) {
        commandMap.register("minecraft", wrapper);
      }
      // Spigot end
    }
  }

  private void loadPlugin(Plugin plugin) {
    try {
      pluginManager.enablePlugin(plugin);

      List perms = plugin.getDescription().getPermissions();

      for (Permission perm : perms) {
        try {
          pluginManager.addPermission(perm);
        } catch (IllegalArgumentException ex) {
          getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex);
        }
      }
    } catch (Throwable ex) {
      Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, ex.getMessage() + " loading " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
    }
  }

  @Override
  public String getName() {
    return serverName;
  }

  @Override
  public String getVersion() {
    return serverVersion + " (MC: " + console.getVersion() + ")";
  }

  @Override
  public String getBukkitVersion() {
    return bukkitVersion;
  }

  @Override
  @Deprecated
  @SuppressWarnings("unchecked")
  public Player[] _INVALID_getOnlinePlayers() {
    return getOnlinePlayers().toArray(EMPTY_PLAYER_ARRAY);
  }

  @Override
  public List getOnlinePlayers() {
    return this.playerView;
  }

  @Override
  @Deprecated
  public Player getPlayer(final String name) {
    Validate.notNull(name, "Name cannot be null");

    Player found = getPlayerExact(name);
    // Try for an exact match first.
    if (found != null) {
      return found;
    }

    String lowerName = name.toLowerCase();
    int delta = Integer.MAX_VALUE;
    for (Player player : getOnlinePlayers()) {
      if (player.getName().toLowerCase().startsWith(lowerName)) {
        int curDelta = Math.abs(player.getName().length() - lowerName.length());
        if (curDelta < delta) {
          found = player;
          delta = curDelta;
        }
        if (curDelta == 0) break;
      }
    }
    return found;
  }

  @Override
  @Deprecated
  public Player getPlayerExact(String name) {
    Validate.notNull(name, "Name cannot be null");

    EntityPlayer player = playerList.getPlayer(name);
    return (player != null) ? player.getBukkitEntity() : null;
  }

  @Override
  public Player getPlayer(UUID id) {
    EntityPlayer player = playerList.a(id);

    if (player != null) {
      return player.getBukkitEntity();
    }

    return null;
  }

  @Override
  public int broadcastMessage(String message) {
    return broadcast(message, BROADCAST_CHANNEL_USERS);
  }

  public Player getPlayer(final EntityPlayer entity) {
    return entity.getBukkitEntity();
  }

  @Override
  @Deprecated
  public List matchPlayer(String partialName) {
    Validate.notNull(partialName, "PartialName cannot be null");

    List matchedPlayers = new ArrayList();

    for (Player iterPlayer : this.getOnlinePlayers()) {
      String iterPlayerName = iterPlayer.getName();

      if (partialName.equalsIgnoreCase(iterPlayerName)) {
        // Exact match
        matchedPlayers.clear();
        matchedPlayers.add(iterPlayer);
        break;
      }
      if (iterPlayerName.toLowerCase().contains(partialName.toLowerCase())) {
        // Partial match
        matchedPlayers.add(iterPlayer);
      }
    }

    return matchedPlayers;
  }

  @Override
  public int getMaxPlayers() {
    return playerList.getMaxPlayers();
  }

  // NOTE: These are dependent on the corrisponding call in MinecraftServer
  // so if that changes this will need to as well
  @Override
  public int getPort() {
    return this.getConfigInt("server-port", 25565);
  }

  @Override
  public int getViewDistance() {
    return this.getConfigInt("view-distance", 10);
  }

  @Override
  public String getIp() {
    return this.getConfigString("server-ip", "");
  }

  @Override
  public String getServerName() {
    return this.getConfigString("server-name", "Unknown Server");
  }

  @Override
  public String getServerId() {
    return this.getConfigString("server-id", "unnamed");
  }

  @Override
  public String getWorldType() {
    return this.getConfigString("level-type", "DEFAULT");
  }

  @Override
  public boolean getGenerateStructures() {
    return this.getConfigBoolean("generate-structures", true);
  }

  @Override
  public boolean getAllowEnd() {
    return this.configuration.getBoolean("settings.allow-end");
  }

  @Override
  public boolean getAllowNether() {
    return this.getConfigBoolean("allow-nether", true);
  }

  public boolean getWarnOnOverload() {
    return this.configuration.getBoolean("settings.warn-on-overload");
  }

  public boolean getQueryPlugins() {
    return this.configuration.getBoolean("settings.query-plugins");
  }

  @Override
  public boolean hasWhitelist() {
    return this.getConfigBoolean("white-list", false);
  }

  // NOTE: Temporary calls through to server.properies until its replaced
  private String getConfigString(String variable, String defaultValue) {
    return this.console.getPropertyManager().getString(variable, defaultValue);
  }

  // End Temporary calls

  private int getConfigInt(String variable, int defaultValue) {
    return this.console.getPropertyManager().getInt(variable, defaultValue);
  }

  private boolean getConfigBoolean(String variable, boolean defaultValue) {
    return this.console.getPropertyManager().getBoolean(variable, defaultValue);
  }

  @Override
  public String getUpdateFolder() {
    return this.configuration.getString("settings.update-folder", "update");
  }

  @Override
  public File getUpdateFolderFile() {
    return new File((File) console.options.valueOf("plugins"), this.configuration.getString("settings.update-folder", "update"));
  }

  @Override
  public long getConnectionThrottle() {
    // Spigot Start - Automatically set connection throttle for bungee configurations
    if (org.spigotmc.SpigotConfig.bungee) {
      return -1;
    } else {
      return this.configuration.getInt("settings.connection-throttle");
    }
    // Spigot End
  }

  @Override
  public int getTicksPerAnimalSpawns() {
    return this.configuration.getInt("ticks-per.animal-spawns");
  }

  @Override
  public int getTicksPerMonsterSpawns() {
    return this.configuration.getInt("ticks-per.monster-spawns");
  }

  @Override
  public PluginManager getPluginManager() {
    return pluginManager;
  }

  @Override
  public CraftScheduler getScheduler() {
    return scheduler;
  }

  @Override
  public ServicesManager getServicesManager() {
    return servicesManager;
  }

  @Override
  public List getWorlds() {
    return new ArrayList(worlds.values());
  }

  public DedicatedPlayerList getHandle() {
    return playerList;
  }

  // NOTE: Should only be called from DedicatedServer.ah()
  public boolean dispatchServerCommand(CommandSender sender, ServerCommand serverCommand) {
    if (sender instanceof Conversable) {
      Conversable conversable = (Conversable) sender;

      if (conversable.isConversing()) {
        conversable.acceptConversationInput(serverCommand.command);
        return true;
      }
    }
    try {
      this.playerCommandState = true;
      return dispatchCommand(sender, serverCommand.command);
    } catch (Exception ex) {
      getLogger().log(Level.WARNING, "Unexpected exception while parsing console command \"" + serverCommand.command + '"', ex);
      return false;
    } finally {
      this.playerCommandState = false;
    }
  }

  @Override
  public boolean dispatchCommand(CommandSender sender, String commandLine) {
    Validate.notNull(sender, "Sender cannot be null");
    Validate.notNull(commandLine, "CommandLine cannot be null");

    if (commandMap.dispatch(sender, commandLine)) {
      return true;
    }

    sender.sendMessage(org.spigotmc.SpigotConfig.unknownCommandMessage);

    return false;
  }

  @Override
  public void reload() {
    reloadCount++;
    configuration = YamlConfiguration.loadConfiguration(getConfigFile());
    commandsConfiguration = YamlConfiguration.loadConfiguration(getCommandsConfigFile());
    PropertyManager config = new PropertyManager(console.options);

    ((DedicatedServer) console).propertyManager = config;

    boolean animals = config.getBoolean("spawn-animals", console.getSpawnAnimals());
    boolean monsters = config.getBoolean("spawn-monsters", console.worlds.get(0).getDifficulty() != EnumDifficulty.PEACEFUL);
    EnumDifficulty difficulty = EnumDifficulty.getById(config.getInt("difficulty", console.worlds.get(0).getDifficulty().ordinal()));

    online.value = config.getBoolean("online-mode", console.getOnlineMode());
    console.setSpawnAnimals(config.getBoolean("spawn-animals", console.getSpawnAnimals()));
    console.setPVP(config.getBoolean("pvp", console.getPVP()));
    console.setAllowFlight(config.getBoolean("allow-flight", console.getAllowFlight()));
    console.setMotd(config.getString("motd", console.getMotd()));
    monsterSpawn = configuration.getInt("spawn-limits.monsters");
    animalSpawn = configuration.getInt("spawn-limits.animals");
    waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals");
    ambientSpawn = configuration.getInt("spawn-limits.ambient");
    warningState = WarningState.value(configuration.getString("settings.deprecated-verbose"));
    printSaveWarning = false;
    console.autosavePeriod = configuration.getInt("ticks-per.autosave");
    chunkGCPeriod = configuration.getInt("chunk-gc.period-in-ticks");
    chunkGCLoadThresh = configuration.getInt("chunk-gc.load-threshold");
    loadIcon();

    try {
      playerList.getIPBans().load();
    } catch (IOException ex) {
      logger.log(Level.WARNING, "Failed to load banned-ips.json, " + ex.getMessage());
    }
    try {
      playerList.getProfileBans().load();
    } catch (IOException ex) {
      logger.log(Level.WARNING, "Failed to load banned-players.json, " + ex.getMessage());
    }

    org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
    for (WorldServer world : console.worlds) {
      world.worldData.setDifficulty(difficulty);
      world.setSpawnFlags(monsters, animals);
      if (this.getTicksPerAnimalSpawns() < 0) {
        world.ticksPerAnimalSpawns = 400;
      } else {
        world.ticksPerAnimalSpawns = this.getTicksPerAnimalSpawns();
      }

      if (this.getTicksPerMonsterSpawns() < 0) {
        world.ticksPerMonsterSpawns = 1;
      } else {
        world.ticksPerMonsterSpawns = this.getTicksPerMonsterSpawns();
      }
      world.spigotConfig.init(); // Spigot
    }

    pluginManager.clearPlugins();
    commandMap.clearCommands();
    resetRecipes();
    org.spigotmc.SpigotConfig.registerCommands(); // Spigot

    overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");

    int pollCount = 0;

    // Wait for at most 2.5 seconds for plugins to close their threads
    while (pollCount < 50 && getScheduler().getActiveWorkers().size() > 0) {
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
      }
      pollCount++;
    }

    List overdueWorkers = getScheduler().getActiveWorkers();
    for (BukkitWorker worker : overdueWorkers) {
      Plugin plugin = worker.getOwner();
      String author = "";
      if (plugin.getDescription().getAuthors().size() > 0) {
        author = plugin.getDescription().getAuthors().get(0);
      }
      getLogger().log(Level.SEVERE, String.format(
        "Nag author: '%s' of '%s' about the following: %s",
        author,
        plugin.getDescription().getName(),
        "This plugin is not properly shutting down its async tasks when it is being reloaded.  This may cause conflicts with the newly loaded version of the plugin"
      ));
    }
    loadPlugins();
    enablePlugins(PluginLoadOrder.STARTUP);
    enablePlugins(PluginLoadOrder.POSTWORLD);
  }

  private void loadIcon() {
    icon = new CraftIconCache(null);
    try {
      final File file = new File(new File("."), "server-icon.png");
      if (file.isFile()) {
        icon = loadServerIcon0(file);
      }
    } catch (Exception ex) {
      getLogger().log(Level.WARNING, "Couldn't load server icon", ex);
    }
  }

  @SuppressWarnings({"unchecked", "finally"})
  private void loadCustomPermissions() {
    File file = new File(configuration.getString("settings.permissions-file"));
    FileInputStream stream;

    try {
      stream = new FileInputStream(file);
    } catch (FileNotFoundException ex) {
      try {
        file.createNewFile();
      } finally {
        return;
      }
    }

    Map> perms;

    try {
      perms = (Map>) yaml.load(stream);
    } catch (MarkedYAMLException ex) {
      getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML: " + ex);
      return;
    } catch (Throwable ex) {
      getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML.", ex);
      return;
    } finally {
      try {
        stream.close();
      } catch (IOException ex) {
      }
    }

    if (perms == null) {
      getLogger().log(Level.INFO, "Server permissions file " + file + " is empty, ignoring it");
      return;
    }

    List permsList = Permission.loadPermissions(perms, "Permission node '%s' in " + file + " is invalid", Permission.DEFAULT_PERMISSION);

    for (Permission perm : permsList) {
      try {
        pluginManager.addPermission(perm);
      } catch (IllegalArgumentException ex) {
        getLogger().log(Level.SEVERE, "Permission in " + file + " was already defined", ex);
      }
    }
  }

  @Override
  public String toString() {
    return "CraftServer{" + "serverName=" + serverName + ",serverVersion=" + serverVersion + ",minecraftVersion=" + console.getVersion() + '}';
  }

  public World createWorld(String name, World.Environment environment) {
    return WorldCreator.name(name).environment(environment).createWorld();
  }

  public World createWorld(String name, World.Environment environment, long seed) {
    return WorldCreator.name(name).environment(environment).seed(seed).createWorld();
  }

  public World createWorld(String name, Environment environment, ChunkGenerator generator) {
    return WorldCreator.name(name).environment(environment).generator(generator).createWorld();
  }

  public World createWorld(String name, Environment environment, long seed, ChunkGenerator generator) {
    return WorldCreator.name(name).environment(environment).seed(seed).generator(generator).createWorld();
  }

  @Override
  public World createWorld(WorldCreator creator) {
    Validate.notNull(creator, "Creator may not be null");

    String name = creator.name();
    ChunkGenerator generator = creator.generator();
    File folder = new File(getWorldContainer(), name);
    World world = getWorld(name);
    WorldType type = WorldType.getType(creator.type().getName());
    boolean generateStructures = creator.generateStructures();

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

    if ((folder.exists()) && (!folder.isDirectory())) {
      throw new IllegalArgumentException("File exists with the name '" + name + "' and isn't a folder");
    }

    if (generator == null) {
      generator = getGenerator(name);
    }

    Convertable converter = new WorldLoaderServer(getWorldContainer());
    if (converter.isConvertable(name)) {
      getLogger().info("Converting world '" + name + "'");
      converter.convert(name, 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) {
        }
      });
    }

    int dimension = CraftWorld.CUSTOM_DIMENSION_OFFSET + console.worlds.size();
    boolean used = false;
    do {
      for (WorldServer server : console.worlds) {
        used = server.dimension == dimension;
        if (used) {
          dimension++;
          break;
        }
      }
    } while (used);
    boolean hardcore = false;

    IDataManager sdm = new ServerNBTManager(getWorldContainer(), name, true);
    WorldData worlddata = sdm.getWorldData();
    if (worlddata == null) {
      WorldSettings worldSettings = new WorldSettings(creator.seed(), WorldSettings.EnumGamemode.getById(getDefaultGameMode().getValue()), generateStructures, hardcore, type);
      worldSettings.setGeneratorSettings(creator.generatorSettings());
      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)
    WorldServer internal = (WorldServer) new WorldServer(console, sdm, worlddata, dimension, console.methodProfiler, creator.environment(), generator).b();

    if (!(worlds.containsKey(name.toLowerCase()))) {
      return null;
    }

    internal.scoreboard = getScoreboardManager().getMainScoreboard().getHandle();

    internal.tracker = new EntityTracker(internal);
    internal.addIWorldAccess(new WorldManager(console, internal));
    internal.worldData.setDifficulty(EnumDifficulty.EASY);
    internal.setSpawnFlags(true, true);
    console.worlds.add(internal);

    if (generator != null) {
      internal.getWorld().getPopulators().addAll(generator.getDefaultPopulators(internal.getWorld()));
    }

    pluginManager.callEvent(new WorldInitEvent(internal.getWorld()));
    System.out.print("Preparing start region for level " + (console.worlds.size() - 1) + " (Seed: " + internal.getSeed() + ")");

    if (internal.getWorld().getKeepSpawnInMemory()) {
      short short1 = 196;
      long i = System.currentTimeMillis();
      for (int j = -short1; j <= short1; j += 16) {
        for (int k = -short1; k <= short1; k += 16) {
          long l = System.currentTimeMillis();

          if (l < i) {
            i = l;
          }

          if (l > i + 1000L) {
            int i1 = (short1 * 2 + 1) * (short1 * 2 + 1);
            int j1 = (j + short1) * (short1 * 2 + 1) + k + 1;

            System.out.println("Preparing spawn area for " + name + ", " + (j1 * 100 / i1) + "%");
            i = l;
          }

          BlockPosition chunkcoordinates = internal.getSpawn();
          internal.chunkProviderServer.getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4);
        }
      }
    }
    pluginManager.callEvent(new WorldLoadEvent(internal.getWorld()));
    return internal.getWorld();
  }

  @Override
  public boolean unloadWorld(String name, boolean save) {
    return unloadWorld(getWorld(name), save);
  }

  @Override
  public boolean unloadWorld(World world, boolean save) {
    if (world == null) {
      return false;
    }

    WorldServer handle = ((CraftWorld) world).getHandle();

    if (!(console.worlds.contains(handle))) {
      return false;
    }

    if (!(handle.dimension > 1)) {
      return false;
    }

    if (handle.players.size() > 0) {
      return false;
    }

    WorldUnloadEvent e = new WorldUnloadEvent(handle.getWorld());
    pluginManager.callEvent(e);

    if (e.isCancelled()) {
      return false;
    }

    if (save) {
      try {
        handle.save(true, null);
        handle.saveLevel();
      } catch (ExceptionWorldConflict ex) {
        getLogger().log(Level.SEVERE, null, ex);
      }
    }

    worlds.remove(world.getName().toLowerCase());
    console.worlds.remove(handle);

    File parentFolder = world.getWorldFolder().getAbsoluteFile();

    // Synchronized because access to RegionFileCache.a is guarded by this lock.
    synchronized (RegionFileCache.class) {
      // RegionFileCache.a should be RegionFileCache.cache
      Iterator> i = RegionFileCache.a.entrySet().iterator();
      while (i.hasNext()) {
        Map.Entry entry = i.next();
        File child = entry.getKey().getAbsoluteFile();
        while (child != null) {
          if (child.equals(parentFolder)) {
            i.remove();
            try {
              entry.getValue().c(); // Should be RegionFile.close();
            } catch (IOException ex) {
              getLogger().log(Level.SEVERE, null, ex);
            }
            break;
          }
          child = child.getParentFile();
        }
      }
    }

    return true;
  }

  public MinecraftServer getServer() {
    return console;
  }

  @Override
  public World getWorld(String name) {
    Validate.notNull(name, "Name cannot be null");

    return worlds.get(name.toLowerCase());
  }

  @Override
  public World getWorld(UUID uid) {
    for (World world : worlds.values()) {
      if (world.getUID().equals(uid)) {
        return world;
      }
    }
    return null;
  }

  public void addWorld(World world) {
    // Check if a World already exists with the UID.
    if (getWorld(world.getUID()) != null) {
      System.out.println("World " + world.getName() + " is a duplicate of another world and has been prevented from loading. Please delete the uid.dat file from " + world.getName() + "'s world directory if you want to be able to load the duplicate world.");
      return;
    }
    worlds.put(world.getName().toLowerCase(), world);
  }

  @Override
  public Logger getLogger() {
    return logger;
  }

  public ConsoleReader getReader() {
    return console.reader;
  }

  @Override
  public PluginCommand getPluginCommand(String name) {
    Command command = commandMap.getCommand(name);

    if (command instanceof PluginCommand) {
      return (PluginCommand) command;
    } else {
      return null;
    }
  }

  @Override
  public void savePlayers() {
    checkSaveState();
    playerList.savePlayers();
  }

  @Override
  public void configureDbConfig(ServerConfig config) {
    Validate.notNull(config, "Config cannot be null");

    DataSourceConfig ds = new DataSourceConfig();
    ds.setDriver(configuration.getString("database.driver"));
    ds.setUrl(configuration.getString("database.url"));
    ds.setUsername(configuration.getString("database.username"));
    ds.setPassword(configuration.getString("database.password"));
    ds.setIsolationLevel(TransactionIsolation.getLevel(configuration.getString("database.isolation")));

    if (ds.getDriver().contains("sqlite")) {
      config.setDatabasePlatform(new SQLitePlatform());
      config.getDatabasePlatform().getDbDdlSyntax().setIdentity("");
    }

    config.setDataSourceConfig(ds);
  }

  @Override
  public boolean addRecipe(Recipe recipe) {
    CraftRecipe toAdd;
    if (recipe instanceof CraftRecipe) {
      toAdd = (CraftRecipe) recipe;
    } else {
      if (recipe instanceof ShapedRecipe) {
        toAdd = CraftShapedRecipe.fromBukkitRecipe((ShapedRecipe) recipe);
      } else if (recipe instanceof ShapelessRecipe) {
        toAdd = CraftShapelessRecipe.fromBukkitRecipe((ShapelessRecipe) recipe);
      } else if (recipe instanceof FurnaceRecipe) {
        toAdd = CraftFurnaceRecipe.fromBukkitRecipe((FurnaceRecipe) recipe);
      } else {
        return false;
      }
    }
    toAdd.addToCraftingManager();
    CraftingManager.getInstance().sort();
    return true;
  }

  @Override
  public List getRecipesFor(ItemStack result) {
    Validate.notNull(result, "Result cannot be null");

    List results = new ArrayList();
    Iterator iter = recipeIterator();
    while (iter.hasNext()) {
      Recipe recipe = iter.next();
      ItemStack stack = recipe.getResult();
      if (stack.getType() != result.getType()) {
        continue;
      }
      if (result.getDurability() == -1 || result.getDurability() == stack.getDurability()) {
        results.add(recipe);
      }
    }
    return results;
  }

  @Override
  public Iterator recipeIterator() {
    return new RecipeIterator();
  }

  @Override
  public void clearRecipes() {
    CraftingManager.getInstance().recipes.clear();
    RecipesFurnace.getInstance().recipes.clear();
    RecipesFurnace.getInstance().customRecipes.clear();
  }

  @Override
  public void resetRecipes() {
    CraftingManager.getInstance().recipes = new CraftingManager().recipes;
    RecipesFurnace.getInstance().recipes = new RecipesFurnace().recipes;
    RecipesFurnace.getInstance().customRecipes.clear();
  }

  @Override
  public Map getCommandAliases() {
    ConfigurationSection section = commandsConfiguration.getConfigurationSection("aliases");
    Map result = new LinkedHashMap();

    if (section != null) {
      for (String key : section.getKeys(false)) {
        List commands;

        if (section.isList(key)) {
          commands = section.getStringList(key);
        } else {
          commands = ImmutableList.of(section.getString(key));
        }

        result.put(key, commands.toArray(new String[commands.size()]));
      }
    }

    return result;
  }

  public void removeBukkitSpawnRadius() {
    configuration.set("settings.spawn-radius", null);
    saveConfig();
  }

  public int getBukkitSpawnRadius() {
    return configuration.getInt("settings.spawn-radius", -1);
  }

  @Override
  public String getShutdownMessage() {
    return configuration.getString("settings.shutdown-message");
  }

  @Override
  public int getSpawnRadius() {
    return ((DedicatedServer) console).propertyManager.getInt("spawn-protection", 16);
  }

  @Override
  public void setSpawnRadius(int value) {
    configuration.set("settings.spawn-radius", value);
    saveConfig();
  }

  @Override
  public boolean getOnlineMode() {
    return online.value;
  }

  @Override
  public boolean getAllowFlight() {
    return console.getAllowFlight();
  }

  @Override
  public boolean isHardcore() {
    return console.isHardcore();
  }

  @Override
  public boolean useExactLoginLocation() {
    return configuration.getBoolean("settings.use-exact-login-location");
  }

  public ChunkGenerator getGenerator(String world) {
    ConfigurationSection section = configuration.getConfigurationSection("worlds");
    ChunkGenerator result = null;

    if (section != null) {
      section = section.getConfigurationSection(world);

      if (section != null) {
        String name = section.getString("generator");

        if ((name != null) && (!name.equals(""))) {
          String[] split = name.split(":", 2);
          String id = (split.length > 1) ? split[1] : null;
          Plugin plugin = pluginManager.getPlugin(split[0]);

          if (plugin == null) {
            getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + split[0] + "' does not exist");
          } else if (!plugin.isEnabled()) {
            getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' is not enabled yet (is it load:STARTUP?)");
          } else {
            try {
              result = plugin.getDefaultWorldGenerator(world, id);
              if (result == null) {
                getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' lacks a default world generator");
              }
            } catch (Throwable t) {
              plugin.getLogger().log(Level.SEVERE, "Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName(), t);
            }
          }
        }
      }
    }

    return result;
  }

  @Override
  @Deprecated
  public CraftMapView getMap(short id) {
    PersistentCollection collection = console.worlds.get(0).worldMaps;
    WorldMap worldmap = (WorldMap) collection.get(WorldMap.class, "map_" + id);
    if (worldmap == null) {
      return null;
    }
    return worldmap.mapView;
  }

  @Override
  public CraftMapView createMap(World world) {
    Validate.notNull(world, "World cannot be null");

    net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(Items.MAP, 1, -1);
    WorldMap worldmap = Items.FILLED_MAP.getSavedMap(stack, ((CraftWorld) world).getHandle());
    return worldmap.mapView;
  }

  @Override
  public void shutdown() {
    console.safeShutdown();
  }

  @Override
  public int broadcast(String message, String permission) {
    int count = 0;
    Set permissibles = getPluginManager().getPermissionSubscriptions(permission);

    for (Permissible permissible : permissibles) {
      if (permissible instanceof CommandSender && permissible.hasPermission(permission)) {
        CommandSender user = (CommandSender) permissible;
        user.sendMessage(message);
        count++;
      }
    }

    return count;
  }

  @Override
  @Deprecated
  public OfflinePlayer getOfflinePlayer(String name) {
    Validate.notNull(name, "Name cannot be null");
    com.google.common.base.Preconditions.checkArgument(!org.apache.commons.lang.StringUtils.isBlank(name), "Name cannot be blank"); // Spigot

    OfflinePlayer result = getPlayerExact(name);
    if (result == null) {
      // Spigot Start
      GameProfile profile = null;
      // Only fetch an online UUID in online mode
      if (MinecraftServer.getServer().getOnlineMode() || org.spigotmc.SpigotConfig.bungee) {
        profile = MinecraftServer.getServer().getUserCache().getProfile(name);
      }
      // Spigot end
      if (profile == null) {
        // Make an OfflinePlayer using an offline mode UUID since the name has no profile
        result = getOfflinePlayer(new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name));
      } else {
        // Use the GameProfile even when we get a UUID so we ensure we still have a name
        result = getOfflinePlayer(profile);
      }
    } else {
      offlinePlayers.remove(result.getUniqueId());
    }

    return result;
  }

  @Override
  public OfflinePlayer getOfflinePlayer(UUID id) {
    Validate.notNull(id, "UUID cannot be null");

    OfflinePlayer result = getPlayer(id);
    if (result == null) {
      result = offlinePlayers.get(id);
      if (result == null) {
        result = new CraftOfflinePlayer(this, new GameProfile(id, null));
        offlinePlayers.put(id, result);
      }
    } else {
      offlinePlayers.remove(id);
    }

    return result;
  }

  public OfflinePlayer getOfflinePlayer(GameProfile profile) {
    OfflinePlayer player = new CraftOfflinePlayer(this, profile);
    offlinePlayers.put(profile.getId(), player);
    return player;
  }

  @Override
  @SuppressWarnings("unchecked")
  public Set getIPBans() {
    return new HashSet(Arrays.asList(playerList.getIPBans().getEntries()));
  }

  @Override
  public void banIP(String address) {
    Validate.notNull(address, "Address cannot be null.");

    this.getBanList(org.bukkit.BanList.Type.IP).addBan(address, null, null, null);
  }

  @Override
  public void unbanIP(String address) {
    Validate.notNull(address, "Address cannot be null.");

    this.getBanList(org.bukkit.BanList.Type.IP).pardon(address);
  }

  @Override
  public Set getBannedPlayers() {
    Set result = new HashSet();

    for (JsonListEntry entry : playerList.getProfileBans().getValues()) {
      result.add(getOfflinePlayer((GameProfile) entry.getKey()));
    }

    return result;
  }

  @Override
  public BanList getBanList(BanList.Type type) {
    Validate.notNull(type, "Type cannot be null");

    switch (type) {
      case IP:
        return new CraftIpBanList(playerList.getIPBans());
      case NAME:
      default:
        return new CraftProfileBanList(playerList.getProfileBans());
    }
  }

  @Override
  public void setWhitelist(boolean value) {
    playerList.setHasWhitelist(value);
    console.getPropertyManager().setProperty("white-list", value);
  }

  @Override
  public Set getWhitelistedPlayers() {
    Set result = new LinkedHashSet();

    for (JsonListEntry entry : playerList.getWhitelist().getValues()) {
      result.add(getOfflinePlayer((GameProfile) entry.getKey()));
    }

    return result;
  }

  @Override
  public Set getOperators() {
    Set result = new HashSet();

    for (JsonListEntry entry : playerList.getOPs().getValues()) {
      result.add(getOfflinePlayer((GameProfile) entry.getKey()));
    }

    return result;
  }

  @Override
  public void reloadWhitelist() {
    playerList.reloadWhitelist();
  }

  @Override
  public GameMode getDefaultGameMode() {
    return GameMode.getByValue(console.worlds.get(0).getWorldData().getGameType().getId());
  }

  @Override
  public void setDefaultGameMode(GameMode mode) {
    Validate.notNull(mode, "Mode cannot be null");

    for (World world : getWorlds()) {
      ((CraftWorld) world).getHandle().worldData.setGameType(WorldSettings.EnumGamemode.getById(mode.getValue()));
    }
  }

  @Override
  public ConsoleCommandSender getConsoleSender() {
    return console.console;
  }

  public EntityMetadataStore getEntityMetadata() {
    return entityMetadata;
  }

  public PlayerMetadataStore getPlayerMetadata() {
    return playerMetadata;
  }

  public WorldMetadataStore getWorldMetadata() {
    return worldMetadata;
  }

  @Override
  public File getWorldContainer() {
    if (this.getServer().universe != null) {
      return this.getServer().universe;
    }

    if (container == null) {
      container = new File(configuration.getString("settings.world-container", "."));
    }

    return container;
  }

  @Override
  public OfflinePlayer[] getOfflinePlayers() {
    WorldNBTStorage storage = (WorldNBTStorage) console.worlds.get(0).getDataManager();
    String[] files = storage.getPlayerDir().list(new DatFileFilter());
    Set players = new HashSet();

    for (String file : files) {
      try {
        players.add(getOfflinePlayer(UUID.fromString(file.substring(0, file.length() - 4))));
      } catch (IllegalArgumentException ex) {
        // Who knows what is in this directory, just ignore invalid files
      }
    }

    players.addAll(getOnlinePlayers());

    return players.toArray(new OfflinePlayer[players.size()]);
  }

  @Override
  public Messenger getMessenger() {
    return messenger;
  }

  @Override
  public void sendPluginMessage(Plugin source, String channel, byte[] message) {
    StandardMessenger.validatePluginMessage(getMessenger(), source, channel, message);

    for (Player player : getOnlinePlayers()) {
      player.sendPluginMessage(source, channel, message);
    }
  }

  @Override
  public Set getListeningPluginChannels() {
    Set result = new HashSet();

    for (Player player : getOnlinePlayers()) {
      result.addAll(player.getListeningPluginChannels());
    }

    return result;
  }

  @Override
  public Inventory createInventory(InventoryHolder owner, InventoryType type) {
    // TODO: Create the appropriate type, rather than Custom?
    return new CraftInventoryCustom(owner, type);
  }

  @Override
  public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) {
    return new CraftInventoryCustom(owner, type, title);
  }

  @Override
  public Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException {
    Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!");
    return new CraftInventoryCustom(owner, size);
  }

  @Override
  public Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException {
    Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!");
    return new CraftInventoryCustom(owner, size, title);
  }

  @Override
  public HelpMap getHelpMap() {
    return helpMap;
  }

  public SimpleCommandMap getCommandMap() {
    return commandMap;
  }

  @Override
  public int getMonsterSpawnLimit() {
    return monsterSpawn;
  }

  @Override
  public int getAnimalSpawnLimit() {
    return animalSpawn;
  }

  @Override
  public int getWaterAnimalSpawnLimit() {
    return waterAnimalSpawn;
  }

  @Override
  public int getAmbientSpawnLimit() {
    return ambientSpawn;
  }

  @Override
  public boolean isPrimaryThread() {
    return Thread.currentThread().equals(console.primaryThread);
  }

  @Override
  public String getMotd() {
    return console.getMotd();
  }

  @Override
  public WarningState getWarningState() {
    return warningState;
  }

  public List tabComplete(net.minecraft.server.ICommandListener sender, String message) {
    if (!(sender instanceof EntityPlayer)) {
      return ImmutableList.of();
    }

    Player player = ((EntityPlayer) sender).getBukkitEntity();
    if (message.startsWith("/")) {
      return tabCompleteCommand(player, message);
    } else {
      return tabCompleteChat(player, message);
    }
  }

  public List tabCompleteCommand(Player player, String message) {
    // Spigot Start
    if ((org.spigotmc.SpigotConfig.tabComplete < 0 || message.length() <= org.spigotmc.SpigotConfig.tabComplete) && !message.contains(" ")) {
      return ImmutableList.of();
    }
    // Spigot End

    List completions = null;
    try {
      completions = getCommandMap().tabComplete(player, message.substring(1));
    } catch (CommandException ex) {
      player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command");
      getLogger().log(Level.SEVERE, "Exception when " + player.getName() + " attempted to tab complete " + message, ex);
    }

    return completions == null ? ImmutableList.of() : completions;
  }

  public List tabCompleteChat(Player player, String message) {
    List completions = new ArrayList();
    PlayerChatTabCompleteEvent event = new PlayerChatTabCompleteEvent(player, message, completions);
    String token = event.getLastToken();
    for (Player p : getOnlinePlayers()) {
      if (player.canSee(p) && StringUtil.startsWithIgnoreCase(p.getName(), token)) {
        completions.add(p.getName());
      }
    }
    pluginManager.callEvent(event);

    Iterator it = completions.iterator();
    while (it.hasNext()) {
      Object current = it.next();
      if (!(current instanceof String)) {
        // Sanity
        it.remove();
      }
    }
    Collections.sort(completions, String.CASE_INSENSITIVE_ORDER);
    return completions;
  }

  @Override
  public CraftItemFactory getItemFactory() {
    return CraftItemFactory.instance();
  }

  @Override
  public CraftScoreboardManager getScoreboardManager() {
    return scoreboardManager;
  }

  public void checkSaveState() {
    if (this.playerCommandState || this.printSaveWarning || this.console.autosavePeriod <= 0) {
      return;
    }
    this.printSaveWarning = true;
    getLogger().log(Level.WARNING, "A manual (plugin-induced) save has been detected while server is configured to auto-save. This may affect performance.", warningState == WarningState.ON ? new Throwable() : null);
  }

  @Override
  public CraftIconCache getServerIcon() {
    return icon;
  }

  @Override
  public CraftIconCache loadServerIcon(File file) throws Exception {
    Validate.notNull(file, "File cannot be null");
    if (!file.isFile()) {
      throw new IllegalArgumentException(file + " is not a file");
    }
    return loadServerIcon0(file);
  }

  @Override
  public CraftIconCache loadServerIcon(BufferedImage image) throws Exception {
    Validate.notNull(image, "Image cannot be null");
    return loadServerIcon0(image);
  }

  @Override
  public int getIdleTimeout() {
    return console.getIdleTimeout();
  }

  @Override
  public void setIdleTimeout(int threshold) {
    console.setIdleTimeout(threshold);
  }

  @Override
  public ChunkGenerator.ChunkData createChunkData(World world) {
    return new CraftChunkData(world);
  }

  @Deprecated
  @Override
  public UnsafeValues getUnsafe() {
    return CraftMagicNumbers.INSTANCE;
  }

  public Spigot spigot() {
    return spigot;
  }

  private final class BooleanWrapper {
    private boolean value = true;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy