package org.bukkit.craftbukkit; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.imageio.ImageIO; import net.minecraft.server.*; import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.Server; import org.bukkit.UnsafeValues; import org.bukkit.Warning.WarningState; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.WorldCreator; import org.bukkit.boss.BarColor; import org.bukkit.boss.BarFlag; import org.bukkit.boss.BarStyle; import org.bukkit.boss.BossBar; import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.command.SimpleCommandMap; 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.boss.CraftBossBar; 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.CraftFurnaceRecipe; import org.bukkit.craftbukkit.inventory.CraftInventoryCustom; import org.bukkit.craftbukkit.inventory.CraftItemFactory; import org.bukkit.craftbukkit.inventory.CraftMerchantCustom; import org.bukkit.craftbukkit.inventory.CraftRecipe; import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; import org.bukkit.craftbukkit.inventory.RecipeIterator; 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.Entity; 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.FurnaceRecipe; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Merchant; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.Recipe; import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapelessRecipe; import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permission; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginLoadOrder; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.SimplePluginManager; import org.bukkit.plugin.SimpleServicesManager; import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.plugin.messaging.Messenger; import org.bukkit.potion.Potion; import org.bukkit.potion.PotionEffectType; import org.bukkit.plugin.messaging.StandardMessenger; import org.bukkit.scheduler.BukkitWorker; import org.bukkit.util.StringUtil; import org.bukkit.util.permissions.DefaultPermissions; import org.torch.server.TorchPlayerList; import org.torch.server.TorchServer; import org.torch.server.cache.Caches; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.MarkedYAMLException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; 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.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import com.koloboke.collect.set.hash.HashObjSets; 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 org.bukkit.event.server.TabCompleteEvent; import net.md_5.bungee.api.chat.BaseComponent; public final class CraftServer implements Server { private static final Player[] EMPTY_PLAYER_ARRAY = new Player[0]; private final String serverName = "Torch"; 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); protected final TorchServer console; // Torch protected final TorchPlayerList playerList; // Torch private final Map<String, World> worlds = new LinkedHashMap<String, World>(); private YamlConfiguration configuration; private YamlConfiguration commandsConfiguration; private final Yaml yaml = new Yaml(new SafeConstructor()); private final Map<UUID, OfflinePlayer> offlinePlayers = new MapMaker().softValues().makeMap(); private final EntityMetadataStore entityMetadata = new EntityMetadataStore(); private final PlayerMetadataStore playerMetadata = new PlayerMetadataStore(); private final WorldMetadataStore worldMetadata = new WorldMetadataStore(); private int monsterSpawn = -1; private int animalSpawn = -1; private int waterAnimalSpawn = -1; private int ambientSpawn = -1; public int chunkGCPeriod = -1; public int chunkGCLoadThresh = 0; private File container; private WarningState warningState = WarningState.DEFAULT; private final BooleanWrapper online = new BooleanWrapper(); public CraftScoreboardManager scoreboardManager; public boolean playerCommandState; private boolean printSaveWarning; private CraftIconCache icon; private boolean overrideAllCommandBlockCommands = false; 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<CraftPlayer> playerView; public int reloadCount; public static Exception excessiveVelEx; // Paper - Velocity warnings private final class BooleanWrapper { private boolean value = true; } static { ConfigurationSerialization.registerClass(CraftOfflinePlayer.class); CraftItemFactory.instance(); } public CraftServer(TorchServer console, TorchPlayerList playerList) { this.console = console; this.playerList = playerList; this.playerView = Collections.unmodifiableList(Lists.transform(playerList.players, player -> 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 Enchantments.DAMAGE_ALL.getClass(); org.bukkit.enchantments.Enchantment.stopAcceptingRegistrations(); Potion.setPotionBrewer(new CraftPotionBrewer()); MobEffects.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<String> commands = new ArrayList<String>(); if (legacyAlias.isList(key)) { for (String command : legacyAlias.getStringList(key)) { commands.add(command + " $1-"); } } else { commands.add(legacyAlias.getString(key) + " $1-"); } aliases.set(key, commands); } } 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.getServant().autosavePeriod = 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 } 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) { 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(); if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions(); // Paper } Plugin[] plugins = pluginManager.getPlugins(); for (Plugin plugin : plugins) { if ((!plugin.isEnabled()) && (plugin.getDescription().getLoad() == type)) { enablePlugin(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(); if (!com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions(); // Paper DefaultPermissions.registerCorePermissions(); CraftDefaultPermissions.registerCorePermissions(); helpMap.initializeCommands(); } } public void disablePlugins() { pluginManager.disablePlugins(); } private void setVanillaCommands(boolean first) { // Spigot Map<String, ICommand> commands = console.createCommandDispatcher().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 enablePlugin(Plugin plugin) { try { List<Permission> 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); } } pluginManager.enablePlugin(plugin); } 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.getMinecraftVersion() + ")"; } @Override public String getBukkitVersion() { return bukkitVersion; } @Override @Deprecated public Player[] _INVALID_getOnlinePlayers() { return getOnlinePlayers().toArray(EMPTY_PLAYER_ARRAY); } @Override public List<CraftPlayer> 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 = Caches.toLowerCase(name, Locale.ENGLISH); int delta = Integer.MAX_VALUE; for (Player player : getOnlinePlayers()) { if (Caches.toLowerCase(player.getName(), Locale.ENGLISH).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.getPlayerByUUID(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<Player> matchPlayer(String partialName) { Validate.notNull(partialName, "PartialName cannot be null"); List<Player> matchedPlayers = new ArrayList<Player>(); for (Player iterPlayer : this.getOnlinePlayers()) { String iterPlayerName = iterPlayer.getName(); if (partialName.equalsIgnoreCase(iterPlayerName)) { // Exact match matchedPlayers.clear(); matchedPlayers.add(iterPlayer); break; } if (iterPlayerName.toLowerCase(java.util.Locale.ENGLISH).contains(partialName.toLowerCase(java.util.Locale.ENGLISH))) { // Partial match matchedPlayers.add(iterPlayer); } } return matchedPlayers; } @Override public int getMaxPlayers() { return playerList.getMaxPlayers(); } // NOTE: These are dependent on the corresponding 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); } 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); } // End Temporary calls @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<World> getWorlds() { return new ArrayList<World>(worlds.values()); } public DedicatedPlayerList getHandle() { return (DedicatedPlayerList) playerList.getServant(); } // 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"); // Paper Start if (org.spigotmc.AsyncCatcher.enabled && !Bukkit.isPrimaryThread()) { final CommandSender fSender = sender; final String fCommandLine = commandLine; Bukkit.getLogger().log(Level.SEVERE, "Command Dispatched Async: " + commandLine); Bukkit.getLogger().log(Level.SEVERE, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable()); org.bukkit.craftbukkit.util.Waitable<Boolean> wait = new org.bukkit.craftbukkit.util.Waitable<Boolean>() { @Override protected Boolean evaluate() { return dispatchCommand(fSender, fCommandLine); } }; net.minecraft.server.MinecraftServer.getServer().processQueue.add(wait); try { return wait.get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! } catch (Exception e) { throw new RuntimeException("Exception processing dispatch command", e.getCause()); } } // Paper End 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); console.propertyManager = config; boolean animals = config.getBoolean("spawn-animals", console.isSpawnAnimals()); 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.isSpawnAnimals())); console.setPvpMode(config.getBoolean("pvp", console.isPvpMode())); console.setAllowFlight(config.getBoolean("allow-flight", console.isAllowFlight())); 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.getBannedIPs().load(); } catch (IOException ex) { logger.log(Level.WARNING, "Failed to load banned-ips.json, " + ex.getMessage()); } try { playerList.getBannedPlayers().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 com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper 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 world.paperConfig.init(); // Paper } // Paper start for (Plugin plugin : pluginManager.getPlugins()) { entityMetadata.removeAll(plugin); worldMetadata.removeAll(plugin); playerMetadata.removeAll(plugin); } // Paper end pluginManager.clearPlugins(); commandMap.clearCommands(); resetRecipes(); org.spigotmc.SpigotConfig.registerCommands(); // Spigot com.destroystokyo.paper.PaperConfig.registerCommands(); // Paper 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<BukkitWorker> overdueWorkers = getScheduler().getActiveWorkers(); for (BukkitWorker worker : overdueWorkers) { Plugin plugin = worker.getOwner(); String author = "<NoAuthorGiven>"; 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<String, Map<String, Object>> perms; try { perms = (Map<String, Map<String, Object>>) yaml.load(stream); } catch (MarkedYAMLException ex) { getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML: " + ex.toString()); 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<Permission> 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.getMinecraftVersion() + '}'; } 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(), getHandle().getServer().getDataConverterManager()); if (converter.isConvertable(name)) { getLogger().info("Converting world '" + name + "'"); converter.convert(name, new IProgressUpdate() { private long b = System.currentTimeMillis(); @Override public void a(String s) {} @Override public void a(int i) { if (System.currentTimeMillis() - this.b >= 1000L) { this.b = System.currentTimeMillis(); MinecraftServer.LOGGER.info("Converting... " + i + "%"); } } @Override 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, getHandle().getServer().getDataConverterManager()); WorldData worlddata = sdm.getWorldData(); WorldSettings worldSettings = null; if (worlddata == null) { worldSettings = new WorldSettings(creator.seed(), 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.getServant(), sdm, worlddata, dimension, console.methodProfiler, creator.environment(), generator).b(); if (!(worlds.containsKey(name.toLowerCase(java.util.Locale.ENGLISH)))) { return null; } if (worldSettings != null) { internal.a(worldSettings); } internal.scoreboard = getScoreboardManager().getMainScoreboard().getHandle(); internal.tracker = new EntityTracker(internal); internal.addIWorldAccess(new WorldManager(console.getServant(), internal)); internal.worldData.setDifficulty(EnumDifficulty.EASY); internal.setSpawnFlags(true, true); console.worlds.add(internal); 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 = internal.paperConfig.keepLoadedRange; // Paper 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.getChunkProviderServer().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(java.util.Locale.ENGLISH)); console.worlds.remove(console.worlds.indexOf(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<Map.Entry<File, RegionFile>> i = RegionFileCache.a.entrySet().iterator(); while(i.hasNext()) { Map.Entry<File, RegionFile> 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.getServant(); } @Override public World getWorld(String name) { Validate.notNull(name, "Name cannot be null"); return worlds.get(Caches.toLowerCase(name, Locale.ENGLISH)); } @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(java.util.Locale.ENGLISH), 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<Recipe> getRecipesFor(ItemStack result) { Validate.notNull(result, "Result cannot be null"); List<Recipe> results = new ArrayList<Recipe>(); Iterator<Recipe> 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<Recipe> recipeIterator() { return new RecipeIterator(); } @Override public void clearRecipes() { CraftingManager.getInstance().recipes.clear(); RecipesFurnace.getInstance().recipes.clear(); RecipesFurnace.getInstance().customRecipes.clear(); RecipesFurnace.getInstance().customExperience.clear(); } @Override public void resetRecipes() { CraftingManager.getInstance().recipes = new CraftingManager().recipes; RecipesFurnace.getInstance().recipes = new RecipesFurnace().recipes; RecipesFurnace.getInstance().customRecipes.clear(); RecipesFurnace.getInstance().customExperience.clear(); } @Override public Map<String, String[]> getCommandAliases() { ConfigurationSection section = commandsConfiguration.getConfigurationSection("aliases"); Map<String, String[]> result = new LinkedHashMap<String, String[]>(); if (section != null) { for (String key : section.getKeys(false)) { List<String> 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 console.getSpawnProtectionSize(); } @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.isAllowFlight(); } @Override public boolean isHardcore() { return console.isHardcore(); } 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<Permissible> 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( !StringUtils.isBlank( name ), "Name cannot be blank" ); // Spigot OfflinePlayer result = getPlayerExact(name); if (result == null) { // Spigot Start GameProfile profile = null; profile = console.getUserCache().requestProfile(name); // Torch result = getOfflinePlayer(profile); // Torch } 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 public Set<String> getIPBans() { return HashObjSets.newMutableSet(Arrays.asList(playerList.getBannedIPs().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<OfflinePlayer> getBannedPlayers() { Set<OfflinePlayer> result = HashObjSets.newMutableSet(); for (JsonListEntry entry : playerList.getBannedPlayers().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.getBannedIPs()); case NAME: default: return new CraftProfileBanList(playerList.getBannedPlayers()); } } @Override public void setWhitelist(boolean value) { playerList.setWhitelistMode(value); console.getPropertyManager().setProperty("white-list", value); } @Override public Set<OfflinePlayer> getWhitelistedPlayers() { Set<OfflinePlayer> result = new LinkedHashSet<OfflinePlayer>(); for (JsonListEntry entry : playerList.getWhitelist().getValues()) { result.add(getOfflinePlayer((GameProfile) entry.getKey())); } return result; } @Override public Set<OfflinePlayer> getOperators() { Set<OfflinePlayer> result = HashObjSets.newMutableSet(); for (JsonListEntry entry : playerList.getOperators().getValues()) { result.add(getOfflinePlayer((GameProfile) entry.getKey())); } return result; } @Override public void reloadWhitelist() { playerList.readWhiteList(); } @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(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<OfflinePlayer> players = HashObjSets.newMutableSet(); 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<String> getListeningPluginChannels() { Set<String> result = HashObjSets.newMutableSet(); 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 Merchant createMerchant(String title) { return new CraftMerchantCustom(title == null ? InventoryType.MERCHANT.getDefaultTitle() : title); } @Override public HelpMap getHelpMap() { return helpMap; } @Override // Paper - add override 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.getServerThread()); } @Override public String getMotd() { return console.getMotd(); } @Override public WarningState getWarningState() { return warningState; } public List<String> tabComplete(net.minecraft.server.ICommandListener sender, String message, BlockPosition pos, boolean forceCommand) { if (!(sender instanceof EntityPlayer)) { return ImmutableList.of(); } List<String> offers; Player player = ((EntityPlayer) sender).getBukkitEntity(); if (message.startsWith("/") || forceCommand) { offers = tabCompleteCommand(player, message, pos); } else { offers = tabCompleteChat(player, message); } TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers); getPluginManager().callEvent(tabEvent); return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions(); } public List<String> tabCompleteCommand(Player player, String message, BlockPosition pos) { // Spigot Start if ( (org.spigotmc.SpigotConfig.tabComplete < 0 || message.length() <= org.spigotmc.SpigotConfig.tabComplete) && !message.contains( " " ) ) { return ImmutableList.of(); } // Spigot End List<String> completions = null; try { if (message.startsWith("/")) { // Trim leading '/' if present (won't always be present in command blocks) message = message.substring(1); } if (pos == null) { completions = getCommandMap().tabComplete(player, message); } else { completions = getCommandMap().tabComplete(player, message, new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ())); } } 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.<String>of() : completions; } public List<String> tabCompleteChat(Player player, String message) { List<String> completions = new ArrayList<String>(); 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); } static CraftIconCache loadServerIcon0(File file) throws Exception { return loadServerIcon0(ImageIO.read(file)); } @Override public CraftIconCache loadServerIcon(BufferedImage image) throws Exception { Validate.notNull(image, "Image cannot be null"); return loadServerIcon0(image); } 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)); } @Override public void setIdleTimeout(int threshold) { console.setIdleTimeout(threshold); } @Override public int getIdleTimeout() { return console.getMaxPlayerIdleMinutes(); } @Override public ChunkGenerator.ChunkData createChunkData(World world) { return new CraftChunkData(world); } @Override public BossBar createBossBar(String title, BarColor color, BarStyle style, BarFlag... flags) { return new CraftBossBar(title, color, style, flags); } @Override public Entity getEntity(UUID uuid) { Validate.notNull(uuid, "UUID cannot be null"); net.minecraft.server.Entity entity = console.getEntityFromUUID(uuid); return entity == null ? null : entity.getBukkitEntity(); } @Deprecated @Override public UnsafeValues getUnsafe() { return CraftMagicNumbers.INSTANCE; } // Paper - Add getTPS API - Further improve tick loop @Override public double[] getTPS() { return new double[] { MinecraftServer.getServer().tps1.getAverage(), MinecraftServer.getServer().tps5.getAverage(), MinecraftServer.getServer().tps15.getAverage() }; } // Paper end private final Spigot spigot = new Spigot() { @Deprecated @Override public YamlConfiguration getConfig() { return org.spigotmc.SpigotConfig.config; } @Override public YamlConfiguration getBukkitConfig() { return configuration; } @Override public YamlConfiguration getSpigotConfig() { return org.spigotmc.SpigotConfig.config; } @Override public YamlConfiguration getPaperConfig() { return com.destroystokyo.paper.PaperConfig.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); } } }; @Override public Spigot spigot() { return spigot; } // Paper start @Override public void reloadPermissions() { ((SimplePluginManager) pluginManager).clearPermissions(); loadCustomPermissions(); for (Plugin plugin : pluginManager.getPlugins()) { plugin.getDescription().getPermissions().forEach((perm) -> { 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); } }); } } @Override public boolean reloadCommandAliases() { Set<String> removals = getCommandAliases().keySet().stream() .map(key -> key.toLowerCase(java.util.Locale.ENGLISH)) .collect(java.util.stream.Collectors.toSet()); getCommandMap().getKnownCommands().keySet().removeIf(removals::contains); File file = getCommandsConfigFile(); try { commandsConfiguration.load(file); } catch (FileNotFoundException ex) { return false; } catch (IOException | org.bukkit.configuration.InvalidConfigurationException ex) { Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file, ex); return false; } commandMap.registerServerAliases(); return true; } // Paper end }