package org.bukkit.craftbukkit; import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import com.mojang.authlib.GameProfile; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashSet; 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.Optional; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.imageio.ImageIO; // import jline.console.ConsoleReader; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.core.BlockPos; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.NbtException; import net.minecraft.nbt.ReportedNbtException; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.ConsoleInput; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ReloadableServerRegistries; import net.minecraft.server.WorldLoader; import net.minecraft.server.bossevents.CustomBossEvent; import net.minecraft.server.commands.ReloadCommand; import net.minecraft.server.dedicated.DedicatedPlayerList; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServerProperties; import net.minecraft.server.dedicated.DedicatedServerSettings; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.TicketType; import net.minecraft.server.players.IpBanListEntry; import net.minecraft.server.players.PlayerList; import net.minecraft.server.players.ServerOpListEntry; import net.minecraft.server.players.UserBanListEntry; import net.minecraft.server.players.UserWhiteListEntry; import net.minecraft.tags.TagKey; import net.minecraft.util.GsonHelper; import net.minecraft.util.datafix.DataFixers; import net.minecraft.world.Difficulty; import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.ai.village.VillageSiege; import net.minecraft.world.entity.npc.CatSpawner; import net.minecraft.world.entity.npc.WanderingTraderSpawner; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.inventory.CraftingMenu; import net.minecraft.world.inventory.ResultContainer; import net.minecraft.world.inventory.TransientCraftingContainer; import net.minecraft.world.item.Item; import net.minecraft.world.item.MapItem; import net.minecraft.world.item.crafting.CraftingRecipe; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.item.crafting.RepairItemRecipe; import net.minecraft.world.level.CustomSpawner; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameType; import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.PatrolSpawner; import net.minecraft.world.level.levelgen.PhantomSpawner; import net.minecraft.world.level.levelgen.WorldDimensions; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.saveddata.maps.MapId; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import net.minecraft.world.level.storage.LevelDataAndDimensions; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.validation.ContentValidationException; import net.minecraft.world.phys.Vec3; import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Keyed; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.OfflinePlayer; import org.bukkit.Registry; import org.bukkit.Server; import org.bukkit.ServerLinks; import org.bukkit.ServerTickManager; import org.bukkit.StructureType; import org.bukkit.UnsafeValues; import org.bukkit.Warning.WarningState; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.WorldBorder; import org.bukkit.WorldCreator; import org.bukkit.block.data.BlockData; import org.bukkit.boss.BarColor; import org.bukkit.boss.BarFlag; import org.bukkit.boss.BarStyle; import org.bukkit.boss.BossBar; import org.bukkit.boss.KeyedBossBar; 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.ban.CraftIpBanList; import org.bukkit.craftbukkit.ban.CraftProfileBanList; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.boss.CraftBossBar; import org.bukkit.craftbukkit.boss.CraftKeyedBossbar; import org.bukkit.craftbukkit.command.CraftCommandMap; import org.bukkit.craftbukkit.command.VanillaCommandWrapper; import org.bukkit.craftbukkit.entity.CraftEntityFactory; import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.craftbukkit.generator.CraftWorldInfo; import org.bukkit.craftbukkit.generator.OldCraftChunkData; import org.bukkit.craftbukkit.help.SimpleHelpMap; import org.bukkit.craftbukkit.inventory.CraftBlastingRecipe; import org.bukkit.craftbukkit.inventory.CraftCampfireRecipe; import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe; import org.bukkit.craftbukkit.inventory.CraftItemCraftResult; import org.bukkit.craftbukkit.inventory.CraftItemFactory; import org.bukkit.craftbukkit.inventory.CraftItemStack; 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.CraftSmithingTransformRecipe; import org.bukkit.craftbukkit.inventory.CraftSmithingTrimRecipe; import org.bukkit.craftbukkit.inventory.CraftSmokingRecipe; import org.bukkit.craftbukkit.inventory.CraftStonecuttingRecipe; import org.bukkit.craftbukkit.inventory.CraftTransmuteRecipe; import org.bukkit.craftbukkit.inventory.RecipeIterator; import org.bukkit.craftbukkit.inventory.util.CraftInventoryCreator; import org.bukkit.craftbukkit.map.CraftMapColorCache; import org.bukkit.craftbukkit.map.CraftMapCursor; 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.packs.CraftDataPackManager; import org.bukkit.craftbukkit.packs.CraftResourcePack; import org.bukkit.craftbukkit.profile.CraftPlayerProfile; import org.bukkit.craftbukkit.scheduler.CraftScheduler; import org.bukkit.craftbukkit.scoreboard.CraftCriteria; import org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager; import org.bukkit.craftbukkit.structure.CraftStructureManager; import org.bukkit.craftbukkit.tag.CraftBlockTag; import org.bukkit.craftbukkit.tag.CraftDamageTag; import org.bukkit.craftbukkit.tag.CraftEntityTag; import org.bukkit.craftbukkit.tag.CraftFluidTag; import org.bukkit.craftbukkit.tag.CraftItemTag; import org.bukkit.craftbukkit.util.ApiVersion; import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.craftbukkit.util.CraftIconCache; import org.bukkit.craftbukkit.util.CraftLocation; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.craftbukkit.util.CraftSpawnCategory; 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.entity.SpawnCategory; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerChatTabCompleteEvent; import org.bukkit.event.server.BroadcastMessageEvent; import org.bukkit.event.server.ServerLoadEvent; import org.bukkit.event.server.TabCompleteEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.WorldInfo; import org.bukkit.help.HelpMap; import org.bukkit.inventory.BlastingRecipe; import org.bukkit.inventory.CampfireRecipe; import org.bukkit.inventory.ComplexRecipe; import org.bukkit.inventory.FurnaceRecipe; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemCraftResult; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Merchant; import org.bukkit.inventory.Recipe; import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapelessRecipe; import org.bukkit.inventory.SmithingTransformRecipe; import org.bukkit.inventory.SmithingTrimRecipe; import org.bukkit.inventory.SmokingRecipe; import org.bukkit.inventory.StonecuttingRecipe; import org.bukkit.inventory.TransmuteRecipe; import org.bukkit.loot.LootTable; import org.bukkit.map.MapPalette; import org.bukkit.map.MapView; import org.bukkit.packs.DataPackManager; import org.bukkit.packs.ResourcePack; 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.messaging.Messenger; import org.bukkit.plugin.messaging.StandardMessenger; import org.bukkit.profile.PlayerProfile; import org.bukkit.scheduler.BukkitWorker; import org.bukkit.scoreboard.Criteria; import org.bukkit.structure.StructureManager; import org.bukkit.util.StringUtil; import org.bukkit.util.permissions.DefaultPermissions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.MarkedYAMLException; import net.md_5.bungee.api.chat.BaseComponent; // Spigot public final class CraftServer implements Server { private final String serverName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); // Paper 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 CraftCommandMap commandMap = new CraftCommandMap(this); private final SimpleHelpMap helpMap = new SimpleHelpMap(this); private final StandardMessenger messenger = new StandardMessenger(); private final SimplePluginManager pluginManager = new SimplePluginManager(this, commandMap); public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); {this.pluginManager.paperPluginManager = this.paperPluginManager;} // Paper private final StructureManager structureManager; protected final DedicatedServer console; protected final DedicatedPlayerList playerList; private final Map worlds = new LinkedHashMap(); private final Map, Registry> registries = new HashMap<>(); private YamlConfiguration configuration; private YamlConfiguration commandsConfiguration; private final Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); private final Map offlinePlayers = new MapMaker().weakValues().makeMap(); private final EntityMetadataStore entityMetadata = new EntityMetadataStore(); private final PlayerMetadataStore playerMetadata = new PlayerMetadataStore(); private final WorldMetadataStore worldMetadata = new WorldMetadataStore(); private final Object2IntOpenHashMap spawnCategoryLimit = new Object2IntOpenHashMap<>(); private File container; private WarningState warningState = WarningState.DEFAULT; public ApiVersion minimumAPI; public CraftScoreboardManager scoreboardManager; public CraftDataPackManager dataPackManager; private CraftServerTickManager serverTickManager; private CraftServerLinks serverLinks; public boolean playerCommandState; private boolean printSaveWarning; private CraftIconCache icon; private boolean overrideAllCommandBlockCommands = false; public boolean ignoreVanillaPermissions = false; private final List playerView; public int reloadCount; public Set activeCompatibilities = Collections.emptySet(); public static Exception excessiveVelEx; // Paper - Velocity warnings static { ConfigurationSerialization.registerClass(CraftOfflinePlayer.class); ConfigurationSerialization.registerClass(CraftPlayerProfile.class); CraftItemFactory.instance(); CraftEntityFactory.instance(); } public CraftServer(DedicatedServer console, PlayerList playerList) { this.console = console; this.playerList = (DedicatedPlayerList) playerList; this.playerView = Collections.unmodifiableList(Lists.transform(playerList.players, new Function() { @Override public CraftPlayer apply(ServerPlayer player) { return player.getBukkitEntity(); } })); this.serverVersion = io.papermc.paper.ServerBuildInfo.buildInfo().asString(io.papermc.paper.ServerBuildInfo.StringRepresentation.VERSION_SIMPLE); // Paper - improve version this.structureManager = new CraftStructureManager(console.getStructureManager(), console.registryAccess()); this.dataPackManager = new CraftDataPackManager(this.getServer().getPackRepository()); this.serverTickManager = new CraftServerTickManager(console.tickRateManager()); this.serverLinks = new CraftServerLinks(console); Bukkit.setServer(this); CraftRegistry.setMinecraftRegistry(console.registryAccess()); if (!Main.useConsole) { this.getLogger().info("Console input is disabled due to --noconsole command argument"); } this.configuration = YamlConfiguration.loadConfiguration(this.getConfigFile()); this.configuration.options().copyDefaults(true); this.configuration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("configurations/bukkit.yml"), Charsets.UTF_8))); ConfigurationSection legacyAlias = null; if (!this.configuration.isString("aliases")) { legacyAlias = this.configuration.getConfigurationSection("aliases"); this.configuration.set("aliases", "now-in-commands.yml"); } this.saveConfig(); if (this.getCommandsConfigFile().isFile()) { legacyAlias = null; } this.commandsConfiguration = YamlConfiguration.loadConfiguration(this.getCommandsConfigFile()); this.commandsConfiguration.options().copyDefaults(true); this.commandsConfiguration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("configurations/commands.yml"), Charsets.UTF_8))); this.saveCommandsConfig(); // Migrate aliases from old file and add previously implicit $1- to pass all arguments if (legacyAlias != null) { ConfigurationSection aliases = this.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); } } this.saveCommandsConfig(); this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); this.overrideSpawnLimits(); console.autosavePeriod = this.configuration.getInt("ticks-per.autosave"); this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose")); TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks"); this.minimumAPI = ApiVersion.getOrCreateVersion(this.configuration.getString("settings.minimum-api")); this.loadIcon(); this.loadCompatibilities(); CraftMagicNumbers.INSTANCE.getCommodore().updateReroute(activeCompatibilities::contains); // Set map color cache if (this.configuration.getBoolean("settings.use-map-color-cache")) { MapPalette.setMapColorCache(new CraftMapColorCache(this.logger)); } } public boolean getCommandBlockOverride(String command) { return this.overrideAllCommandBlockCommands || this.commandsConfiguration.getStringList("command-block-overrides").contains(command); } private File getConfigFile() { return (File) this.console.options.valueOf("bukkit-settings"); } private File getCommandsConfigFile() { return (File) this.console.options.valueOf("commands-settings"); } private void overrideSpawnLimits() { for (SpawnCategory spawnCategory : SpawnCategory.values()) { if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { this.spawnCategoryLimit.put(spawnCategory, this.configuration.getInt(CraftSpawnCategory.getConfigNameSpawnLimit(spawnCategory))); } } } private void saveConfig() { try { this.configuration.save(this.getConfigFile()); } catch (IOException ex) { Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + this.getConfigFile(), ex); } } private void saveCommandsConfig() { try { this.commandsConfiguration.save(this.getCommandsConfigFile()); } catch (IOException ex) { Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + this.getCommandsConfigFile(), ex); } } private void loadCompatibilities() { ConfigurationSection compatibilities = this.configuration.getConfigurationSection("settings.compatibility"); if (compatibilities == null) { this.activeCompatibilities = Collections.emptySet(); return; } this.activeCompatibilities = compatibilities .getKeys(false) .stream() .filter(compatibilities::getBoolean) .collect(Collectors.toSet()); if (!this.activeCompatibilities.isEmpty()) { this.logger.info("Using following compatibilities: `" + Joiner.on("`, `").join(this.activeCompatibilities) + "`, this will affect performance and other plugins behavior."); this.logger.info("Only use when necessary and prefer updating plugins if possible."); } if (this.activeCompatibilities.contains("enum-compatibility-mode")) { this.getLogger().warning("Loading plugins in enum compatibility mode. This will affect plugin performance. Use only as a transition period or when absolutely necessary."); } else if (System.getProperty("RemoveEnumBanner") == null) { // TODO 2024-06-16: Remove in newer version this.getLogger().info("*** This version of Spigot contains changes to some enums. If you notice that plugins no longer work after updating, please report this to the developers of those plugins first. ***"); this.getLogger().info("*** If you cannot update those plugins, you can try setting `settings.compatibility.enum-compatibility-mode` to `true` in `bukkit.yml`. ***"); } } public void loadPlugins() { io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler.INSTANCE.enter(io.papermc.paper.plugin.entrypoint.Entrypoint.PLUGIN); // Paper - replace implementation } // Paper start @Override public File getPluginsFolder() { return this.console.getPluginsFolder(); } private List extraPluginJars() { @SuppressWarnings("unchecked") final List jars = (List) this.console.options.valuesOf("add-plugin"); final List list = new ArrayList<>(); for (final File file : jars) { if (!file.exists()) { net.minecraft.server.MinecraftServer.LOGGER.warn("File '{}' specified through 'add-plugin' argument does not exist, cannot load a plugin from it!", file.getAbsolutePath()); continue; } if (!file.isFile()) { net.minecraft.server.MinecraftServer.LOGGER.warn("File '{}' specified through 'add-plugin' argument is not a file, cannot load a plugin from it!", file.getAbsolutePath()); continue; } if (!file.getName().endsWith(".jar")) { net.minecraft.server.MinecraftServer.LOGGER.warn("File '{}' specified through 'add-plugin' argument is not a jar file, cannot load a plugin from it!", file.getAbsolutePath()); continue; } list.add(file); } return list; } // Paper end public void enablePlugins(PluginLoadOrder type) { if (type == PluginLoadOrder.STARTUP) { this.helpMap.clear(); this.helpMap.initializeGeneralTopics(); if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.loadPermissionsYmlBeforePlugins) loadCustomPermissions(); // Paper } Plugin[] plugins = this.pluginManager.getPlugins(); for (Plugin plugin : plugins) { if ((!plugin.isEnabled()) && (plugin.getDescription().getLoad() == type)) { this.enablePlugin(plugin); } } if (type == PluginLoadOrder.POSTWORLD) { // Spigot start - Allow vanilla commands to be forced to be the main command this.setVanillaCommands(true); this.commandMap.setFallbackCommands(); this.setVanillaCommands(false); // Spigot end this.commandMap.registerServerAliases(); DefaultPermissions.registerCorePermissions(); CraftDefaultPermissions.registerCorePermissions(); if (!io.papermc.paper.configuration.GlobalConfiguration.get().misc.loadPermissionsYmlBeforePlugins) this.loadCustomPermissions(); // Paper this.helpMap.initializeCommands(); this.syncCommands(); } } public void disablePlugins() { this.pluginManager.disablePlugins(); } private void setVanillaCommands(boolean first) { // Spigot Commands dispatcher = this.console.vanillaCommandDispatcher; // Build a list of all Vanilla commands and create wrappers for (CommandNode cmd : dispatcher.getDispatcher().getRoot().getChildren()) { // Spigot start VanillaCommandWrapper wrapper = new VanillaCommandWrapper(dispatcher, cmd); if (org.spigotmc.SpigotConfig.replaceCommands.contains( wrapper.getName() ) ) { if (first) { this.commandMap.register("minecraft", wrapper); } } else if (!first) { this.commandMap.register("minecraft", wrapper); } // Spigot end } } public void syncCommands() { // Clear existing commands Commands dispatcher = this.console.resources.managers().commands = new Commands(); // Register all commands, vanilla ones will be using the old dispatcher references for (Map.Entry entry : this.commandMap.getKnownCommands().entrySet()) { String label = entry.getKey(); Command command = entry.getValue(); if (command instanceof VanillaCommandWrapper) { LiteralCommandNode node = (LiteralCommandNode) ((VanillaCommandWrapper) command).vanillaCommand; if (!node.getLiteral().equals(label)) { LiteralCommandNode clone = new LiteralCommandNode(label, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork()); for (CommandNode child : node.getChildren()) { clone.addChild(child); } node = clone; } dispatcher.getDispatcher().getRoot().addChild(node); } else { new BukkitCommandWrapper(this, entry.getValue()).register(dispatcher.getDispatcher(), label); } } // Refresh commands for (ServerPlayer player : this.getHandle().players) { dispatcher.sendCommands(player); } } private void enablePlugin(Plugin plugin) { try { List perms = plugin.getDescription().getPermissions(); List permsToLoad = new ArrayList<>(); // Paper for (Permission perm : perms) { // Paper start if (this.paperPluginManager.getPermission(perm.getName()) == null) { permsToLoad.add(perm); } else { this.getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered"); // Paper end } } this.paperPluginManager.addPermissions(permsToLoad); // Paper this.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 this.serverName; } @Override public String getVersion() { return this.serverVersion + " (MC: " + this.console.getServerVersion() + ")"; } @Override public String getBukkitVersion() { return this.bukkitVersion; } // Paper start - expose game version @Override public String getMinecraftVersion() { return console.getServerVersion(); } // Paper end @Override public List getOnlinePlayers() { return this.playerView; } @Override @Deprecated public Player getPlayer(final String name) { Preconditions.checkArgument(name != null, "name cannot be null"); Player found = this.getPlayerExact(name); // Try for an exact match first. if (found != null) { return found; } String lowerName = name.toLowerCase(Locale.ROOT); int delta = Integer.MAX_VALUE; for (Player player : this.getOnlinePlayers()) { if (player.getName().toLowerCase(Locale.ROOT).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) { Preconditions.checkArgument(name != null, "name cannot be null"); ServerPlayer player = this.playerList.getPlayerByName(name); return (player != null) ? player.getBukkitEntity() : null; } @Override public Player getPlayer(UUID id) { Preconditions.checkArgument(id != null, "UUID id cannot be null"); ServerPlayer player = this.playerList.getPlayer(id); if (player != null) { return player.getBukkitEntity(); } return null; } @Override @Deprecated // Paper start public int broadcastMessage(String message) { return this.broadcast(message, BROADCAST_CHANNEL_USERS); // Paper end } @Override @Deprecated public List matchPlayer(String partialName) { Preconditions.checkArgument(partialName != null, "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(Locale.ROOT).contains(partialName.toLowerCase(Locale.ROOT))) { // Partial match matchedPlayers.add(iterPlayer); } } return matchedPlayers; } @Override public int getMaxPlayers() { return this.playerList.getMaxPlayers(); } @Override public void setMaxPlayers(int maxPlayers) { Preconditions.checkArgument(maxPlayers >= 0, "maxPlayers must be >= 0"); this.playerList.maxPlayers = maxPlayers; } // 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.getServer().getPort(); } @Override public int getViewDistance() { return this.getProperties().viewDistance; } @Override public int getSimulationDistance() { return this.getProperties().simulationDistance; } @Override public String getIp() { return this.getServer().getLocalIp(); } @Override public String getWorldType() { return this.getProperties().properties.getProperty("level-type"); } @Override public boolean getGenerateStructures() { return this.getServer().getWorldData().worldGenOptions().generateStructures(); } @Override public int getMaxWorldSize() { return this.getProperties().maxWorldSize; } @Override public boolean getAllowEnd() { return this.configuration.getBoolean("settings.allow-end"); } @Override public boolean getAllowNether() { return this.getProperties().allowNether; } @Override public boolean isLoggingIPs() { return this.getServer().logIPs(); } public boolean getWarnOnOverload() { return this.configuration.getBoolean("settings.warn-on-overload"); } public boolean getQueryPlugins() { return this.configuration.getBoolean("settings.query-plugins"); } @Override public List getInitialEnabledPacks() { return Collections.unmodifiableList(this.getProperties().initialDataPackConfiguration.getEnabled()); } @Override public List getInitialDisabledPacks() { return Collections.unmodifiableList(this.getProperties().initialDataPackConfiguration.getDisabled()); } @Override public DataPackManager getDataPackManager() { return this.dataPackManager; } @Override public ServerTickManager getServerTickManager() { return this.serverTickManager; } @Override public ResourcePack getServerResourcePack() { return this.getServer().getServerResourcePack().map(CraftResourcePack::new).orElse(null); } @Override public String getResourcePack() { return this.getServer().getServerResourcePack().map(MinecraftServer.ServerResourcePackInfo::url).orElse(""); } @Override public String getResourcePackHash() { return this.getServer().getServerResourcePack().map(MinecraftServer.ServerResourcePackInfo::hash).orElse("").toUpperCase(Locale.ROOT); } @Override public String getResourcePackPrompt() { return this.getServer().getServerResourcePack().map(MinecraftServer.ServerResourcePackInfo::prompt).map(CraftChatMessage::fromComponent).orElse(""); } @Override public boolean isResourcePackRequired() { return this.getServer().isResourcePackRequired(); } @Override public boolean hasWhitelist() { return this.getProperties().whiteList.get(); } // NOTE: Temporary calls through to server.properies until its replaced private DedicatedServerProperties getProperties() { return this.console.getProperties(); } // End Temporary calls @Override public String getUpdateFolder() { return this.configuration.getString("settings.update-folder", "update"); } @Override public File getUpdateFolderFile() { return new File((File) this.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 @Deprecated public int getTicksPerAnimalSpawns() { return this.getTicksPerSpawns(SpawnCategory.ANIMAL); } @Override @Deprecated public int getTicksPerMonsterSpawns() { return this.getTicksPerSpawns(SpawnCategory.MONSTER); } @Override @Deprecated public int getTicksPerWaterSpawns() { return this.getTicksPerSpawns(SpawnCategory.WATER_ANIMAL); } @Override @Deprecated public int getTicksPerWaterAmbientSpawns() { return this.getTicksPerSpawns(SpawnCategory.WATER_AMBIENT); } @Override @Deprecated public int getTicksPerWaterUndergroundCreatureSpawns() { return this.getTicksPerSpawns(SpawnCategory.WATER_UNDERGROUND_CREATURE); } @Override @Deprecated public int getTicksPerAmbientSpawns() { return this.getTicksPerSpawns(SpawnCategory.AMBIENT); } @Override public int getTicksPerSpawns(SpawnCategory spawnCategory) { Preconditions.checkArgument(spawnCategory != null, "SpawnCategory cannot be null"); Preconditions.checkArgument(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory.%s are not supported", spawnCategory); return this.configuration.getInt(CraftSpawnCategory.getConfigNameTicksPerSpawn(spawnCategory)); } @Override public PluginManager getPluginManager() { return this.pluginManager; } @Override public CraftScheduler getScheduler() { return this.scheduler; } @Override public ServicesManager getServicesManager() { return this.servicesManager; } @Override public List getWorlds() { return new ArrayList(this.worlds.values()); } public DedicatedPlayerList getHandle() { return this.playerList; } // NOTE: Should only be called from DedicatedServer.ah() public boolean dispatchServerCommand(CommandSender sender, ConsoleInput serverCommand) { if (sender instanceof Conversable) { Conversable conversable = (Conversable) sender; if (conversable.isConversing()) { conversable.acceptConversationInput(serverCommand.msg); return true; } } try { this.playerCommandState = true; return this.dispatchCommand(sender, serverCommand.msg); } catch (Exception ex) { this.getLogger().log(Level.WARNING, "Unexpected exception while parsing console command \"" + serverCommand.msg + '"', ex); return false; } finally { this.playerCommandState = false; } } @Override public boolean dispatchCommand(CommandSender sender, String commandLine) { Preconditions.checkArgument(sender != null, "sender cannot be null"); Preconditions.checkArgument(commandLine != null, "commandLine cannot be null"); org.spigotmc.AsyncCatcher.catchOp("Command Dispatched Async: " + commandLine); // Spigot // Paper - Include command in error message if (this.commandMap.dispatch(sender, commandLine)) { return true; } // Spigot start if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) { sender.sendMessage(org.spigotmc.SpigotConfig.unknownCommandMessage); } // Spigot end return false; } @Override public void reload() { this.reloadCount++; this.configuration = YamlConfiguration.loadConfiguration(this.getConfigFile()); this.commandsConfiguration = YamlConfiguration.loadConfiguration(this.getCommandsConfigFile()); this.console.settings = new DedicatedServerSettings(this.console.options); DedicatedServerProperties config = this.console.settings.getProperties(); this.console.setPvpAllowed(config.pvp); this.console.setFlightAllowed(config.allowFlight); this.console.setMotd(config.motd); this.overrideSpawnLimits(); this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose")); TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks"); this.minimumAPI = ApiVersion.getOrCreateVersion(this.configuration.getString("settings.minimum-api")); this.printSaveWarning = false; this.console.autosavePeriod = this.configuration.getInt("ticks-per.autosave"); this.loadIcon(); this.loadCompatibilities(); CraftMagicNumbers.INSTANCE.getCommodore().updateReroute(activeCompatibilities::contains); try { this.playerList.getIpBans().load(); } catch (IOException ex) { this.logger.log(Level.WARNING, "Failed to load banned-ips.json, " + ex.getMessage()); } try { this.playerList.getBans().load(); } catch (IOException ex) { this.logger.log(Level.WARNING, "Failed to load banned-players.json, " + ex.getMessage()); } org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); for (ServerLevel world : this.console.getAllLevels()) { world.serverLevelData.setDifficulty(config.difficulty); world.setSpawnSettings(config.spawnMonsters); for (SpawnCategory spawnCategory : SpawnCategory.values()) { if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { long ticksPerCategorySpawn = this.getTicksPerSpawns(spawnCategory); if (ticksPerCategorySpawn < 0) { world.ticksPerSpawnCategory.put(spawnCategory, CraftSpawnCategory.getDefaultTicksPerSpawn(spawnCategory)); } else { world.ticksPerSpawnCategory.put(spawnCategory, ticksPerCategorySpawn); } } } world.spigotConfig.init(); // Spigot } Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper this.pluginManager.clearPlugins(); this.commandMap.clearCommands(); // Paper start for (Plugin plugin : pluginClone) { entityMetadata.removeAll(plugin); worldMetadata.removeAll(plugin); playerMetadata.removeAll(plugin); } // Paper end this.reloadData(); org.spigotmc.SpigotConfig.registerCommands(); // Spigot io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); int pollCount = 0; // Wait for at most 2.5 seconds for plugins to close their threads while (pollCount < 50 && this.getScheduler().getActiveWorkers().size() > 0) { try { Thread.sleep(50); } catch (InterruptedException e) {} pollCount++; } List overdueWorkers = this.getScheduler().getActiveWorkers(); for (BukkitWorker worker : overdueWorkers) { Plugin plugin = worker.getOwner(); this.getLogger().log(Level.SEVERE, String.format( "Nag author(s): '%s' of '%s' about the following: %s", plugin.getDescription().getAuthors(), plugin.getDescription().getFullName(), "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" )); } io.papermc.paper.plugin.PluginInitializerManager.reload(this.console); // Paper this.loadPlugins(); this.enablePlugins(PluginLoadOrder.STARTUP); this.enablePlugins(PluginLoadOrder.POSTWORLD); if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD)); } @Override public void reloadData() { ReloadCommand.reload(this.console); } private void loadIcon() { this.icon = new CraftIconCache(null); try { final File file = new File(new File("."), "server-icon.png"); if (file.isFile()) { this.icon = CraftServer.loadServerIcon0(file); } } catch (Exception ex) { this.getLogger().log(Level.WARNING, "Couldn't load server icon", ex); } } @SuppressWarnings({ "unchecked", "finally" }) private void loadCustomPermissions() { File file = new File(this.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>) this.yaml.load(stream); } catch (MarkedYAMLException ex) { this.getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML: " + ex.toString()); return; } catch (Throwable ex) { this.getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML.", ex); return; } finally { try { stream.close(); } catch (IOException ex) {} } if (perms == null) { this.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 { this.pluginManager.addPermission(perm); } catch (IllegalArgumentException ex) { this.getLogger().log(Level.SEVERE, "Permission in " + file + " was already defined", ex); } } } @Override public String toString() { return "CraftServer{" + "serverName=" + this.serverName + ",serverVersion=" + this.serverVersion + ",minecraftVersion=" + this.console.getServerVersion() + '}'; } 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) { Preconditions.checkState(this.console.getAllLevels().iterator().hasNext(), "Cannot create additional worlds on STARTUP"); Preconditions.checkArgument(creator != null, "WorldCreator cannot be null"); String name = creator.name(); ChunkGenerator generator = creator.generator(); BiomeProvider biomeProvider = creator.biomeProvider(); File folder = new File(this.getWorldContainer(), name); World world = this.getWorld(name); if (world != null) { return world; } if (folder.exists()) { Preconditions.checkArgument(folder.isDirectory(), "File (%s) exists and isn't a folder", name); } if (generator == null) { generator = this.getGenerator(name); } if (biomeProvider == null) { biomeProvider = this.getBiomeProvider(name); } ResourceKey actualDimension; switch (creator.environment()) { case NORMAL: actualDimension = LevelStem.OVERWORLD; break; case NETHER: actualDimension = LevelStem.NETHER; break; case THE_END: actualDimension = LevelStem.END; break; default: throw new IllegalArgumentException("Illegal dimension (" + creator.environment() + ")"); } LevelStorageSource.LevelStorageAccess worldSession; try { worldSession = LevelStorageSource.createDefault(this.getWorldContainer().toPath()).validateAndCreateAccess(name, actualDimension); } catch (IOException | ContentValidationException ex) { throw new RuntimeException(ex); } Dynamic dynamic; if (worldSession.hasWorldData()) { net.minecraft.world.level.storage.LevelSummary worldinfo; try { dynamic = worldSession.getDataTag(); worldinfo = worldSession.getSummary(dynamic); } catch (NbtException | ReportedNbtException | IOException ioexception) { LevelStorageSource.LevelDirectory convertable_b = worldSession.getLevelDirectory(); MinecraftServer.LOGGER.warn("Failed to load world data from {}", convertable_b.dataFile(), ioexception); MinecraftServer.LOGGER.info("Attempting to use fallback"); try { dynamic = worldSession.getDataTagFallback(); worldinfo = worldSession.getSummary(dynamic); } catch (NbtException | ReportedNbtException | IOException ioexception1) { MinecraftServer.LOGGER.error("Failed to load world data from {}", convertable_b.oldDataFile(), ioexception1); MinecraftServer.LOGGER.error("Failed to load world data from {} and {}. World files may be corrupted. Shutting down.", convertable_b.dataFile(), convertable_b.oldDataFile()); return null; } worldSession.restoreLevelDataFromOld(); } if (worldinfo.requiresManualConversion()) { MinecraftServer.LOGGER.info("This world must be opened in an older version (like 1.6.4) to be safely converted"); return null; } if (!worldinfo.isCompatible()) { MinecraftServer.LOGGER.info("This world was created by an incompatible version."); return null; } } else { dynamic = null; } boolean hardcore = creator.hardcore(); PrimaryLevelData worlddata; WorldLoader.DataLoadContext worldloader_a = this.console.worldLoader; RegistryAccess.Frozen iregistrycustom_dimension = worldloader_a.datapackDimensions(); net.minecraft.core.Registry iregistry = iregistrycustom_dimension.lookupOrThrow(Registries.LEVEL_STEM); if (dynamic != null) { LevelDataAndDimensions leveldataanddimensions = LevelStorageSource.getLevelDataAndDimensions(dynamic, worldloader_a.dataConfiguration(), iregistry, worldloader_a.datapackWorldgen()); worlddata = (PrimaryLevelData) leveldataanddimensions.worldData(); iregistrycustom_dimension = leveldataanddimensions.dimensions().dimensionsRegistryAccess(); } else { LevelSettings worldsettings; WorldOptions worldoptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); WorldDimensions worlddimensions; DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); worldsettings = new LevelSettings(name, GameType.byId(this.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(worldloader_a.dataConfiguration().enabledFeatures()), worldloader_a.dataConfiguration()); worlddimensions = properties.create(worldloader_a.datapackWorldgen()); WorldDimensions.Complete worlddimensions_b = worlddimensions.bake(iregistry); Lifecycle lifecycle = worlddimensions_b.lifecycle().add(worldloader_a.datapackWorldgen().allRegistriesLifecycle()); worlddata = new PrimaryLevelData(worldsettings, worldoptions, worlddimensions_b.specialWorldProperty(), lifecycle); iregistrycustom_dimension = worlddimensions_b.dimensionsRegistryAccess(); } iregistry = iregistrycustom_dimension.lookupOrThrow(Registries.LEVEL_STEM); worlddata.customDimensions = iregistry; worlddata.checkName(name); worlddata.setModdedInfo(this.console.getServerModName(), this.console.getModdedStatus().shouldReportAsModified()); if (this.console.options.has("forceUpgrade")) { net.minecraft.server.Main.forceUpgrade(worldSession, DataFixers.getDataFixer(), this.console.options.has("eraseCache"), () -> true, iregistrycustom_dimension, this.console.options.has("recreateRegionFiles")); } long j = BiomeManager.obfuscateSeed(creator.seed()); List list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worlddata)); LevelStem worlddimension = iregistry.getValue(actualDimension); WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), worlddimension.type().value()); if (biomeProvider == null && generator != null) { biomeProvider = generator.getDefaultBiomeProvider(worldInfo); } ResourceKey worldKey; String levelName = this.getServer().getProperties().levelName; if (name.equals(levelName + "_nether")) { worldKey = net.minecraft.world.level.Level.NETHER; } else if (name.equals(levelName + "_the_end")) { worldKey = net.minecraft.world.level.Level.END; } else { worldKey = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace(name.toLowerCase(Locale.ROOT))); } // If set to not keep spawn in memory (changed from default) then adjust rule accordingly if (!creator.keepSpawnInMemory()) { worlddata.getGameRules().getRule(GameRules.RULE_SPAWN_CHUNK_RADIUS).set(0, null); } ServerLevel internal = (ServerLevel) new ServerLevel(this.console, this.console.executor, worldSession, worlddata, worldKey, worlddimension, this.getServer().progressListenerFactory.create(worlddata.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS)), worlddata.isDebugWorld(), j, creator.environment() == Environment.NORMAL ? list : ImmutableList.of(), true, this.console.overworld().getRandomSequences(), creator.environment(), generator, biomeProvider); if (!(this.worlds.containsKey(name.toLowerCase(Locale.ROOT)))) { return null; } this.console.initWorld(internal, worlddata, worlddata, worlddata.worldGenOptions()); internal.setSpawnSettings(true); this.console.addLevel(internal); this.getServer().prepareLevels(internal.getChunkSource().chunkMap.progressListener, internal); internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API this.pluginManager.callEvent(new WorldLoadEvent(internal.getWorld())); return internal.getWorld(); } @Override public boolean unloadWorld(String name, boolean save) { return this.unloadWorld(this.getWorld(name), save); } @Override public boolean unloadWorld(World world, boolean save) { if (world == null) { return false; } ServerLevel handle = ((CraftWorld) world).getHandle(); if (this.console.getLevel(handle.dimension()) == null) { return false; } if (handle.dimension() == net.minecraft.world.level.Level.OVERWORLD) { return false; } if (handle.players().size() > 0) { return false; } WorldUnloadEvent e = new WorldUnloadEvent(handle.getWorld()); this.pluginManager.callEvent(e); if (e.isCancelled()) { return false; } try { if (save) { handle.save(null, true, true); } handle.getChunkSource().close(save); handle.entityManager.close(save); // SPIGOT-6722: close entityManager handle.convertable.close(); } catch (Exception ex) { this.getLogger().log(Level.SEVERE, null, ex); } this.worlds.remove(world.getName().toLowerCase(Locale.ROOT)); this.console.removeLevel(handle); return true; } public DedicatedServer getServer() { return this.console; } @Override public World getWorld(String name) { Preconditions.checkArgument(name != null, "name cannot be null"); return this.worlds.get(name.toLowerCase(Locale.ROOT)); } @Override public World getWorld(UUID uid) { for (World world : this.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 (this.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; } this.worlds.put(world.getName().toLowerCase(Locale.ROOT), world); } @Override public WorldBorder createWorldBorder() { return new CraftWorldBorder(new net.minecraft.world.level.border.WorldBorder()); } @Override public Logger getLogger() { return this.logger; } // Paper start - JLine update /* public ConsoleReader getReader() { return this.console.reader; } */ // Paper end @Override public PluginCommand getPluginCommand(String name) { Command command = this.commandMap.getCommand(name); if (command instanceof PluginCommand) { return (PluginCommand) command; } else { return null; } } @Override public void savePlayers() { this.checkSaveState(); this.playerList.saveAll(); } @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 if (recipe instanceof BlastingRecipe) { toAdd = CraftBlastingRecipe.fromBukkitRecipe((BlastingRecipe) recipe); } else if (recipe instanceof CampfireRecipe) { toAdd = CraftCampfireRecipe.fromBukkitRecipe((CampfireRecipe) recipe); } else if (recipe instanceof SmokingRecipe) { toAdd = CraftSmokingRecipe.fromBukkitRecipe((SmokingRecipe) recipe); } else if (recipe instanceof StonecuttingRecipe) { toAdd = CraftStonecuttingRecipe.fromBukkitRecipe((StonecuttingRecipe) recipe); } else if (recipe instanceof SmithingTransformRecipe) { toAdd = CraftSmithingTransformRecipe.fromBukkitRecipe((SmithingTransformRecipe) recipe); } else if (recipe instanceof SmithingTrimRecipe) { toAdd = CraftSmithingTrimRecipe.fromBukkitRecipe((SmithingTrimRecipe) recipe); } else if (recipe instanceof TransmuteRecipe) { toAdd = CraftTransmuteRecipe.fromBukkitRecipe((TransmuteRecipe) recipe); } else if (recipe instanceof ComplexRecipe) { throw new UnsupportedOperationException("Cannot add custom complex recipe"); } else { return false; } } toAdd.addToCraftingManager(); return true; } @Override public List getRecipesFor(ItemStack result) { Preconditions.checkArgument(result != null, "ItemStack cannot be null"); List results = new ArrayList(); Iterator iter = this.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 Recipe getRecipe(NamespacedKey recipeKey) { Preconditions.checkArgument(recipeKey != null, "NamespacedKey recipeKey cannot be null"); return this.getServer().getRecipeManager().byKey(CraftRecipe.toMinecraft(recipeKey)).map(RecipeHolder::toBukkitRecipe).orElse(null); } private CraftingContainer createInventoryCrafting() { // Create a players Crafting Inventory AbstractContainerMenu container = new AbstractContainerMenu(null, -1) { @Override public InventoryView getBukkitView() { return null; } @Override public boolean stillValid(net.minecraft.world.entity.player.Player player) { return false; } @Override public net.minecraft.world.item.ItemStack quickMoveStack(net.minecraft.world.entity.player.Player player, int slot) { return net.minecraft.world.item.ItemStack.EMPTY; } }; CraftingContainer inventoryCrafting = new TransientCraftingContainer(container, 3, 3); return inventoryCrafting; } @Override public Recipe getCraftingRecipe(ItemStack[] craftingMatrix, World world) { return this.getNMSRecipe(craftingMatrix, this.createInventoryCrafting(), (CraftWorld) world).map(RecipeHolder::toBukkitRecipe).orElse(null); } @Override public ItemStack craftItem(ItemStack[] craftingMatrix, World world, Player player) { return this.craftItemResult(craftingMatrix, world, player).getResult(); } @Override public ItemCraftResult craftItemResult(ItemStack[] craftingMatrix, World world, Player player) { Preconditions.checkArgument(world != null, "world cannot be null"); Preconditions.checkArgument(player != null, "player cannot be null"); CraftWorld craftWorld = (CraftWorld) world; CraftPlayer craftPlayer = (CraftPlayer) player; // Create a players Crafting Inventory and get the recipe CraftingMenu container = new CraftingMenu(-1, craftPlayer.getHandle().getInventory()); CraftingContainer inventoryCrafting = container.craftSlots; ResultContainer craftResult = container.resultSlots; Optional> recipe = this.getNMSRecipe(craftingMatrix, inventoryCrafting, craftWorld); // Generate the resulting ItemStack from the Crafting Matrix net.minecraft.world.item.ItemStack itemstack = net.minecraft.world.item.ItemStack.EMPTY; if (recipe.isPresent()) { RecipeHolder recipeCrafting = recipe.get(); if (craftResult.setRecipeUsed(craftPlayer.getHandle(), recipeCrafting)) { itemstack = recipeCrafting.value().assemble(inventoryCrafting.asCraftInput(), craftWorld.getHandle().registryAccess()); } } // Call Bukkit event to check for matrix/result changes. net.minecraft.world.item.ItemStack result = CraftEventFactory.callPreCraftEvent(inventoryCrafting, craftResult, itemstack, container.getBukkitView(), recipe.map(RecipeHolder::value).orElse(null) instanceof RepairItemRecipe); return this.createItemCraftResult(recipe, CraftItemStack.asBukkitCopy(result), inventoryCrafting, craftWorld.getHandle()); } @Override public ItemStack craftItem(ItemStack[] craftingMatrix, World world) { return this.craftItemResult(craftingMatrix, world).getResult(); } @Override public ItemCraftResult craftItemResult(ItemStack[] craftingMatrix, World world) { Preconditions.checkArgument(world != null, "world must not be null"); CraftWorld craftWorld = (CraftWorld) world; // Create a players Crafting Inventory and get the recipe CraftingContainer inventoryCrafting = this.createInventoryCrafting(); Optional> recipe = this.getNMSRecipe(craftingMatrix, inventoryCrafting, craftWorld); // Generate the resulting ItemStack from the Crafting Matrix net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.EMPTY; if (recipe.isPresent()) { itemStack = recipe.get().value().assemble(inventoryCrafting.asCraftInput(), craftWorld.getHandle().registryAccess()); } return this.createItemCraftResult(recipe, CraftItemStack.asBukkitCopy(itemStack), inventoryCrafting, craftWorld.getHandle()); } private CraftItemCraftResult createItemCraftResult(Optional> recipe, ItemStack itemStack, CraftingContainer inventoryCrafting, ServerLevel worldServer) { CraftItemCraftResult craftItemResult = new CraftItemCraftResult(itemStack); recipe.map((holder) -> holder.value().getRemainingItems(inventoryCrafting.asCraftInput())).ifPresent((remainingItems) -> { // Set the resulting matrix items and overflow items for (int i = 0; i < remainingItems.size(); ++i) { net.minecraft.world.item.ItemStack itemstack1 = inventoryCrafting.getItem(i); net.minecraft.world.item.ItemStack itemstack2 = (net.minecraft.world.item.ItemStack) remainingItems.get(i); if (!itemstack1.isEmpty()) { inventoryCrafting.removeItem(i, 1); itemstack1 = inventoryCrafting.getItem(i); } if (!itemstack2.isEmpty()) { if (itemstack1.isEmpty()) { inventoryCrafting.setItem(i, itemstack2); } else if (net.minecraft.world.item.ItemStack.isSameItemSameComponents(itemstack1, itemstack2)) { itemstack2.grow(itemstack1.getCount()); inventoryCrafting.setItem(i, itemstack2); } else { craftItemResult.getOverflowItems().add(CraftItemStack.asBukkitCopy(itemstack2)); } } } }); for (int i = 0; i < inventoryCrafting.getContents().size(); i++) { craftItemResult.setResultMatrix(i, CraftItemStack.asBukkitCopy(inventoryCrafting.getItem(i))); } return craftItemResult; } private Optional> getNMSRecipe(ItemStack[] craftingMatrix, CraftingContainer inventoryCrafting, CraftWorld world) { Preconditions.checkArgument(craftingMatrix != null, "craftingMatrix must not be null"); Preconditions.checkArgument(craftingMatrix.length == 9, "craftingMatrix must be an array of length 9"); Preconditions.checkArgument(world != null, "world must not be null"); for (int i = 0; i < craftingMatrix.length; i++) { inventoryCrafting.setItem(i, CraftItemStack.asNMSCopy(craftingMatrix[i])); } return this.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, inventoryCrafting.asCraftInput(), world.getHandle()); } @Override public Iterator recipeIterator() { return new RecipeIterator(); } @Override public void clearRecipes() { this.console.getRecipeManager().clearRecipes(); } @Override public void resetRecipes() { this.reloadData(); // Not ideal but hard to reload a subset of a resource pack } @Override public boolean removeRecipe(NamespacedKey recipeKey) { Preconditions.checkArgument(recipeKey != null, "recipeKey == null"); return this.getServer().getRecipeManager().removeRecipe(CraftRecipe.toMinecraft(recipeKey)); } @Override public Map getCommandAliases() { ConfigurationSection section = this.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() { this.configuration.set("settings.spawn-radius", null); this.saveConfig(); } public int getBukkitSpawnRadius() { return this.configuration.getInt("settings.spawn-radius", -1); } // Paper start @Override public net.kyori.adventure.text.Component shutdownMessage() { String msg = getShutdownMessage(); return msg != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(msg) : null; } // Paper end @Override @Deprecated // Paper public String getShutdownMessage() { return this.configuration.getString("settings.shutdown-message"); } @Override public int getSpawnRadius() { return this.getServer().getSpawnProtectionRadius(); } @Override public void setSpawnRadius(int value) { this.configuration.set("settings.spawn-radius", value); this.saveConfig(); } @Override public boolean shouldSendChatPreviews() { return false; } @Override public boolean isEnforcingSecureProfiles() { return this.getServer().enforceSecureProfile(); } @Override public boolean isAcceptingTransfers() { return this.getServer().acceptsTransfers(); } @Override public boolean getHideOnlinePlayers() { return this.console.hidesOnlinePlayers(); } @Override public boolean getOnlineMode() { return this.console.usesAuthentication(); } @Override public boolean getAllowFlight() { return this.console.isFlightAllowed(); } @Override public boolean isHardcore() { return this.console.isHardcore(); } public ChunkGenerator getGenerator(String world) { ConfigurationSection section = this.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 = this.pluginManager.getPlugin(split[0]); if (plugin == null) { this.getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + split[0] + "' does not exist"); } else if (!plugin.isEnabled()) { this.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) { this.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; } public BiomeProvider getBiomeProvider(String world) { ConfigurationSection section = this.configuration.getConfigurationSection("worlds"); BiomeProvider result = null; if (section != null) { section = section.getConfigurationSection(world); if (section != null) { String name = section.getString("biome-provider"); if ((name != null) && (!name.equals(""))) { String[] split = name.split(":", 2); String id = (split.length > 1) ? split[1] : null; Plugin plugin = this.pluginManager.getPlugin(split[0]); if (plugin == null) { this.getLogger().severe("Could not set biome provider for default world '" + world + "': Plugin '" + split[0] + "' does not exist"); } else if (!plugin.isEnabled()) { this.getLogger().severe("Could not set biome provider for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' is not enabled yet (is it load:STARTUP?)"); } else { try { result = plugin.getDefaultBiomeProvider(world, id); if (result == null) { this.getLogger().severe("Could not set biome provider for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' lacks a default world biome provider"); } } catch (Throwable t) { plugin.getLogger().log(Level.SEVERE, "Could not set biome provider for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName(), t); } } } } } return result; } @Override @Deprecated public CraftMapView getMap(int id) { MapItemSavedData worldmap = this.console.getLevel(net.minecraft.world.level.Level.OVERWORLD).getMapData(new MapId(id)); if (worldmap == null) { return null; } return worldmap.mapView; } @Override public CraftMapView createMap(World world) { Preconditions.checkArgument(world != null, "World cannot be null"); net.minecraft.world.level.Level minecraftWorld = ((CraftWorld) world).getHandle(); // creates a new map at world spawn with the scale of 3, with out tracking position and unlimited tracking BlockPos spawn = minecraftWorld.getLevelData().getSpawnPos(); MapId newId = MapItem.createNewSavedData(minecraftWorld, spawn.getX(), spawn.getZ(), 3, false, false, minecraftWorld.dimension()); return minecraftWorld.getMapData(newId).mapView; } @Override public ItemStack createExplorerMap(World world, Location location, StructureType structureType) { return this.createExplorerMap(world, location, structureType, 100, true); } @Override public ItemStack createExplorerMap(World world, Location location, StructureType structureType, int radius, boolean findUnexplored) { Preconditions.checkArgument(world != null, "World cannot be null"); Preconditions.checkArgument(structureType != null, "StructureType cannot be null"); Preconditions.checkArgument(structureType.getMapIcon() != null, "Cannot create explorer maps for StructureType %s", structureType.getName()); ServerLevel worldServer = ((CraftWorld) world).getHandle(); Location structureLocation = world.locateNearestStructure(location, structureType, radius, findUnexplored); BlockPos structurePosition = CraftLocation.toBlockPosition(structureLocation); // Create map with trackPlayer = true, unlimitedTracking = true net.minecraft.world.item.ItemStack stack = MapItem.create(worldServer, structurePosition.getX(), structurePosition.getZ(), MapView.Scale.NORMAL.getValue(), true, true); MapItem.renderBiomePreviewMap(worldServer, stack); // "+" map ID taken from EntityVillager MapItem.getSavedData(stack, worldServer).addTargetDecoration(stack, structurePosition, "+", CraftMapCursor.CraftType.bukkitToMinecraftHolder(structureType.getMapIcon())); return CraftItemStack.asBukkitCopy(stack); } @Override public void shutdown() { this.console.halt(false); } @Override @Deprecated // Paper public int broadcast(String message, String permission) { // Paper start - Adventure return this.broadcast(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message), permission); } @Override public int broadcast(net.kyori.adventure.text.Component message) { return this.broadcast(message, BROADCAST_CHANNEL_USERS); } @Override public int broadcast(net.kyori.adventure.text.Component message, String permission) { // Paper end Set recipients = new HashSet<>(); for (Permissible permissible : this.getPluginManager().getPermissionSubscriptions(permission)) { if (permissible instanceof CommandSender && permissible.hasPermission(permission)) { recipients.add((CommandSender) permissible); } } BroadcastMessageEvent broadcastMessageEvent = new BroadcastMessageEvent(!Bukkit.isPrimaryThread(), message, recipients); // Paper - Adventure this.getPluginManager().callEvent(broadcastMessageEvent); if (broadcastMessageEvent.isCancelled()) { return 0; } message = broadcastMessageEvent.message(); // Paper - Adventure for (CommandSender recipient : recipients) { recipient.sendMessage(message); } return recipients.size(); } @Override @Deprecated public OfflinePlayer getOfflinePlayer(String name) { Preconditions.checkArgument(name != null, "name cannot be null"); Preconditions.checkArgument(!name.isBlank(), "name cannot be empty"); OfflinePlayer result = this.getPlayerExact(name); if (result == null) { GameProfile profile = null; // Only fetch an online UUID in online mode if (this.getOnlineMode() || io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()) { // Paper - Add setting for proxy online mode status // This is potentially blocking :( profile = this.console.getProfileCache().get(name).orElse(null); } if (profile == null) { // Make an OfflinePlayer using an offline mode UUID since the name has no profile result = this.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 = this.getOfflinePlayer(profile); } } else { this.offlinePlayers.remove(result.getUniqueId()); } return result; } @Override public OfflinePlayer getOfflinePlayer(UUID id) { Preconditions.checkArgument(id != null, "UUID id cannot be null"); OfflinePlayer result = this.getPlayer(id); if (result == null) { result = this.offlinePlayers.get(id); if (result == null) { result = new CraftOfflinePlayer(this, new GameProfile(id, "")); this.offlinePlayers.put(id, result); } } else { this.offlinePlayers.remove(id); } return result; } @Override public PlayerProfile createPlayerProfile(UUID uniqueId, String name) { return new CraftPlayerProfile(uniqueId, name); } @Override public PlayerProfile createPlayerProfile(UUID uniqueId) { return new CraftPlayerProfile(uniqueId, null); } @Override public PlayerProfile createPlayerProfile(String name) { return new CraftPlayerProfile(null, name); } public OfflinePlayer getOfflinePlayer(GameProfile profile) { OfflinePlayer player = new CraftOfflinePlayer(this, profile); this.offlinePlayers.put(profile.getId(), player); return player; } @Override @SuppressWarnings("unchecked") public Set getIPBans() { return this.playerList.getIpBans().getEntries().stream().map(IpBanListEntry::getUser).collect(Collectors.toSet()); } @Override public void banIP(String address) { Preconditions.checkArgument(address != null && !address.isBlank(), "Address cannot be null or blank."); this.getBanList(org.bukkit.BanList.Type.IP).addBan(address, null, null, null); } @Override public void unbanIP(String address) { Preconditions.checkArgument(address != null && !address.isBlank(), "Address cannot be null or blank."); this.getBanList(org.bukkit.BanList.Type.IP).pardon(address); } @Override public void banIP(InetAddress address) { Preconditions.checkArgument(address != null, "Address cannot be null."); ((CraftIpBanList) this.getBanList(BanList.Type.IP)).addBan(address, null, (Date) null, null); } @Override public void unbanIP(InetAddress address) { Preconditions.checkArgument(address != null, "Address cannot be null."); ((CraftIpBanList) this.getBanList(BanList.Type.IP)).pardon(address); } @Override public Set getBannedPlayers() { Set result = new HashSet(); for (UserBanListEntry entry : this.playerList.getBans().getEntries()) { result.add(this.getOfflinePlayer(entry.getUser())); } return result; } @Override public > T getBanList(BanList.Type type) { Preconditions.checkArgument(type != null, "BanList.Type cannot be null"); return switch (type) { case IP -> (T) new CraftIpBanList(this.playerList.getIpBans()); case PROFILE, NAME -> (T) new CraftProfileBanList(this.playerList.getBans()); }; } @Override public void setWhitelist(boolean value) { this.playerList.setUsingWhiteList(value); this.console.storeUsingWhiteList(value); } @Override public boolean isWhitelistEnforced() { return this.console.isEnforceWhitelist(); } @Override public void setWhitelistEnforced(boolean value) { this.console.setEnforceWhitelist(value); } @Override public Set getWhitelistedPlayers() { Set result = new LinkedHashSet(); for (UserWhiteListEntry entry : this.playerList.getWhiteList().getEntries()) { result.add(this.getOfflinePlayer(entry.getUser())); } return result; } @Override public Set getOperators() { Set result = new HashSet(); for (ServerOpListEntry entry : this.playerList.getOps().getEntries()) { result.add(this.getOfflinePlayer(entry.getUser())); } return result; } @Override public void reloadWhitelist() { this.playerList.reloadWhiteList(); } @Override public GameMode getDefaultGameMode() { return GameMode.getByValue(this.console.getLevel(net.minecraft.world.level.Level.OVERWORLD).serverLevelData.getGameType().getId()); } @Override public void setDefaultGameMode(GameMode mode) { Preconditions.checkArgument(mode != null, "GameMode cannot be null"); for (World world : this.getWorlds()) { ((CraftWorld) world).getHandle().serverLevelData.setGameType(GameType.byId(mode.getValue())); } } @Override public ConsoleCommandSender getConsoleSender() { return this.console.console; } public EntityMetadataStore getEntityMetadata() { return this.entityMetadata; } public PlayerMetadataStore getPlayerMetadata() { return this.playerMetadata; } public WorldMetadataStore getWorldMetadata() { return this.worldMetadata; } @Override public File getWorldContainer() { return this.getServer().storageSource.getDimensionPath(net.minecraft.world.level.Level.OVERWORLD).getParent().toFile(); } @Override public OfflinePlayer[] getOfflinePlayers() { PlayerDataStorage storage = this.console.playerDataStorage; String[] files = storage.getPlayerDir().list(new DatFileFilter()); Set players = new HashSet(); for (String file : files) { try { players.add(this.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(this.getOnlinePlayers()); return players.toArray(new OfflinePlayer[players.size()]); } @Override public Messenger getMessenger() { return this.messenger; } @Override public void sendPluginMessage(Plugin source, String channel, byte[] message) { StandardMessenger.validatePluginMessage(this.getMessenger(), source, channel, message); for (Player player : this.getOnlinePlayers()) { player.sendPluginMessage(source, channel, message); } } @Override public Set getListeningPluginChannels() { Set result = new HashSet(); for (Player player : this.getOnlinePlayers()) { result.addAll(player.getListeningPluginChannels()); } return result; } @Override public Inventory createInventory(InventoryHolder owner, InventoryType type) { Preconditions.checkArgument(type != null, "InventoryType cannot be null"); Preconditions.checkArgument(type.isCreatable(), "InventoryType.%s cannot be used to create a inventory", type); return CraftInventoryCreator.INSTANCE.createInventory(owner, type); } // Paper start @Override public Inventory createInventory(InventoryHolder owner, InventoryType type, net.kyori.adventure.text.Component title) { Preconditions.checkArgument(type.isCreatable(), "Cannot open an inventory of type ", type); return CraftInventoryCreator.INSTANCE.createInventory(owner, type, title); } // Paper end @Override public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) { Preconditions.checkArgument(type != null, "InventoryType cannot be null"); Preconditions.checkArgument(type.isCreatable(), "InventoryType.%s cannot be used to create a inventory", type); Preconditions.checkArgument(title != null, "title cannot be null"); return CraftInventoryCreator.INSTANCE.createInventory(owner, type, title); } @Override public Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException { Preconditions.checkArgument(9 <= size && size <= 54 && size % 9 == 0, "Size for custom inventory must be a multiple of 9 between 9 and 54 slots (got %s)", size); return CraftInventoryCreator.INSTANCE.createInventory(owner, size); } // Paper start @Override public Inventory createInventory(InventoryHolder owner, int size, net.kyori.adventure.text.Component title) throws IllegalArgumentException { Preconditions.checkArgument(9 <= size && size <= 54 && size % 9 == 0, "Size for custom inventory must be a multiple of 9 between 9 and 54 slots (got " + size + ")"); return CraftInventoryCreator.INSTANCE.createInventory(owner, size, title); } // Paper end @Override public Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException { Preconditions.checkArgument(9 <= size && size <= 54 && size % 9 == 0, "Size for custom inventory must be a multiple of 9 between 9 and 54 slots (got %s)", size); return CraftInventoryCreator.INSTANCE.createInventory(owner, size, title); } // Paper start @Override public Merchant createMerchant(net.kyori.adventure.text.Component title) { return new org.bukkit.craftbukkit.inventory.CraftMerchantCustom(title == null ? InventoryType.MERCHANT.defaultTitle() : title); } // Paper end @Override @Deprecated // Paper public Merchant createMerchant(String title) { return new CraftMerchantCustom(title == null ? InventoryType.MERCHANT.getDefaultTitle() : title); } @Override public int getMaxChainedNeighborUpdates() { return this.getServer().getMaxChainedNeighborUpdates(); } @Override public HelpMap getHelpMap() { return this.helpMap; } @Override // Paper - add override public SimpleCommandMap getCommandMap() { return this.commandMap; } @Override @Deprecated public int getMonsterSpawnLimit() { return this.getSpawnLimit(SpawnCategory.MONSTER); } @Override @Deprecated public int getAnimalSpawnLimit() { return this.getSpawnLimit(SpawnCategory.ANIMAL); } @Override @Deprecated public int getWaterAnimalSpawnLimit() { return this.getSpawnLimit(SpawnCategory.WATER_ANIMAL); } @Override @Deprecated public int getWaterAmbientSpawnLimit() { return this.getSpawnLimit(SpawnCategory.WATER_AMBIENT); } @Override @Deprecated public int getWaterUndergroundCreatureSpawnLimit() { return this.getSpawnLimit(SpawnCategory.WATER_UNDERGROUND_CREATURE); } @Override @Deprecated public int getAmbientSpawnLimit() { return this.getSpawnLimit(SpawnCategory.AMBIENT); } @Override public int getSpawnLimit(SpawnCategory spawnCategory) { return this.spawnCategoryLimit.getOrDefault(spawnCategory, -1); } @Override public boolean isPrimaryThread() { return Thread.currentThread().equals(this.console.serverThread) || this.console.hasStopped() || !org.spigotmc.AsyncCatcher.enabled; // All bets are off if we have shut down (e.g. due to watchdog) } // Paper start - Adventure @Override public net.kyori.adventure.text.Component motd() { return this.console.motd(); } @Override public void motd(final net.kyori.adventure.text.Component motd) { this.console.motd(motd); } // Paper end @Override public String getMotd() { return this.console.getMotd(); } @Override public void setMotd(String motd) { this.console.setMotd(motd); } @Override public ServerLinks getServerLinks() { return this.serverLinks; } @Override public WarningState getWarningState() { return this.warningState; } public List tabComplete(CommandSender sender, String message, ServerLevel world, Vec3 pos, boolean forceCommand) { if (!(sender instanceof Player)) { return ImmutableList.of(); } List offers; Player player = (Player) sender; if (message.startsWith("/") || forceCommand) { offers = this.tabCompleteCommand(player, message, world, pos); } else { offers = this.tabCompleteChat(player, message); } TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers); this.getPluginManager().callEvent(tabEvent); return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions(); } public List tabCompleteCommand(Player player, String message, ServerLevel world, Vec3 pos) { // 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 { if (message.startsWith("/")) { // Trim leading '/' if present (won't always be present in command blocks) message = message.substring(1); } if (pos == null) { completions = this.getCommandMap().tabComplete(player, message); } else { completions = this.getCommandMap().tabComplete(player, message, CraftLocation.toBukkit(pos, world.getWorld())); } } catch (CommandException ex) { player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command"); this.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 : this.getOnlinePlayers()) { if (player.canSee(p) && StringUtil.startsWithIgnoreCase(p.getName(), token)) { completions.add(p.getName()); } } this.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 CraftEntityFactory getEntityFactory() { return CraftEntityFactory.instance(); } @Override public CraftScoreboardManager getScoreboardManager() { return this.scoreboardManager; } @Override public Criteria getScoreboardCriteria(String name) { return CraftCriteria.getFromBukkit(name); } public void checkSaveState() { if (this.playerCommandState || this.printSaveWarning || this.console.autosavePeriod <= 0) { return; } this.printSaveWarning = true; this.getLogger().log(Level.WARNING, "A manual (plugin-induced) save has been detected while server is configured to auto-save. This may affect performance.", this.warningState == WarningState.ON ? new Throwable() : null); } @Override public CraftIconCache getServerIcon() { return this.icon; } @Override public CraftIconCache loadServerIcon(File file) throws Exception { Preconditions.checkArgument(file != null, "File cannot be null"); Preconditions.checkArgument(file.isFile(), "File (%s) is not a valid file", file); return CraftServer.loadServerIcon0(file); } static CraftIconCache loadServerIcon0(File file) throws Exception { return CraftServer.loadServerIcon0(ImageIO.read(file)); } @Override public CraftIconCache loadServerIcon(BufferedImage image) throws Exception { Preconditions.checkArgument(image != null, "BufferedImage image cannot be null"); return CraftServer.loadServerIcon0(image); } static CraftIconCache loadServerIcon0(BufferedImage image) throws Exception { Preconditions.checkArgument(image.getWidth() == 64, "BufferedImage must be 64 pixels wide (%s)", image.getWidth()); Preconditions.checkArgument(image.getHeight() == 64, "BufferedImage must be 64 pixels high (%s)", image.getHeight()); ByteArrayOutputStream bytebuf = new ByteArrayOutputStream(); ImageIO.write(image, "PNG", bytebuf); return new CraftIconCache(bytebuf.toByteArray()); } @Override public void setIdleTimeout(int threshold) { this.console.setPlayerIdleTimeout(threshold); } @Override public int getIdleTimeout() { return this.console.getPlayerIdleTimeout(); } @Override public int getPauseWhenEmptyTime() { return this.getProperties().pauseWhenEmptySeconds; } @Override public void setPauseWhenEmptyTime(int seconds) { this.getProperties().pauseWhenEmptySeconds = seconds; } @Override public ChunkGenerator.ChunkData createChunkData(World world) { Preconditions.checkArgument(world != null, "World cannot be null"); ServerLevel handle = ((CraftWorld) world).getHandle(); return new OldCraftChunkData(world.getMinHeight(), world.getMaxHeight(), handle.registryAccess().lookupOrThrow(Registries.BIOME)); } @Override public BossBar createBossBar(String title, BarColor color, BarStyle style, BarFlag... flags) { return new CraftBossBar(title, color, style, flags); } @Override public KeyedBossBar createBossBar(NamespacedKey key, String title, BarColor barColor, BarStyle barStyle, BarFlag... barFlags) { Preconditions.checkArgument(key != null, "NamespacedKey key cannot be null"); Preconditions.checkArgument(barColor != null, "BarColor key cannot be null"); Preconditions.checkArgument(barStyle != null, "BarStyle key cannot be null"); CustomBossEvent bossBattleCustom = this.getServer().getCustomBossEvents().create(CraftNamespacedKey.toMinecraft(key), CraftChatMessage.fromString(title, true)[0]); CraftKeyedBossbar craftKeyedBossbar = new CraftKeyedBossbar(bossBattleCustom); craftKeyedBossbar.setColor(barColor); craftKeyedBossbar.setStyle(barStyle); for (BarFlag flag : barFlags) { if (flag == null) { continue; } craftKeyedBossbar.addFlag(flag); } return craftKeyedBossbar; } @Override public Iterator getBossBars() { return Iterators.unmodifiableIterator(Iterators.transform(this.getServer().getCustomBossEvents().getEvents().iterator(), new Function() { @Override public org.bukkit.boss.KeyedBossBar apply(CustomBossEvent bossBattleCustom) { return bossBattleCustom.getBukkitEntity(); } })); } @Override public KeyedBossBar getBossBar(NamespacedKey key) { Preconditions.checkArgument(key != null, "key"); net.minecraft.server.bossevents.CustomBossEvent bossBattleCustom = this.getServer().getCustomBossEvents().get(CraftNamespacedKey.toMinecraft(key)); return (bossBattleCustom == null) ? null : bossBattleCustom.getBukkitEntity(); } @Override public boolean removeBossBar(NamespacedKey key) { Preconditions.checkArgument(key != null, "key"); net.minecraft.server.bossevents.CustomBossEvents bossBattleCustomData = this.getServer().getCustomBossEvents(); net.minecraft.server.bossevents.CustomBossEvent bossBattleCustom = bossBattleCustomData.get(CraftNamespacedKey.toMinecraft(key)); if (bossBattleCustom != null) { bossBattleCustomData.remove(bossBattleCustom); return true; } return false; } @Override public Entity getEntity(UUID uuid) { Preconditions.checkArgument(uuid != null, "UUID id cannot be null"); for (ServerLevel world : this.getServer().getAllLevels()) { net.minecraft.world.entity.Entity entity = world.getEntity(uuid); if (entity != null) { return entity.getBukkitEntity(); } } return null; } @Override public org.bukkit.advancement.Advancement getAdvancement(NamespacedKey key) { Preconditions.checkArgument(key != null, "NamespacedKey key cannot be null"); AdvancementHolder advancement = this.console.getAdvancements().get(CraftNamespacedKey.toMinecraft(key)); return (advancement == null) ? null : advancement.toBukkit(); } @Override public Iterator advancementIterator() { return Iterators.unmodifiableIterator(Iterators.transform(this.console.getAdvancements().getAllAdvancements().iterator(), new Function() { @Override public org.bukkit.advancement.Advancement apply(AdvancementHolder advancement) { return advancement.toBukkit(); } })); } @Override public BlockData createBlockData(org.bukkit.Material material) { Preconditions.checkArgument(material != null, "Material cannot be null"); return this.createBlockData(material, (String) null); } @Override public BlockData createBlockData(org.bukkit.Material material, Consumer consumer) { BlockData data = this.createBlockData(material); if (consumer != null) { consumer.accept(data); } return data; } @Override public BlockData createBlockData(String data) throws IllegalArgumentException { Preconditions.checkArgument(data != null, "data cannot be null"); return this.createBlockData((Material) null, data); } @Override public BlockData createBlockData(org.bukkit.Material material, String data) { Preconditions.checkArgument(material != null || data != null, "Must provide one of material or data"); return CraftBlockData.newData((material != null) ? material.asBlockType() : null, data); } @Override @SuppressWarnings("unchecked") public org.bukkit.Tag getTag(String registry, NamespacedKey tag, Class clazz) { Preconditions.checkArgument(registry != null, "registry cannot be null"); Preconditions.checkArgument(tag != null, "NamespacedKey tag cannot be null"); Preconditions.checkArgument(clazz != null, "Class clazz cannot be null"); ResourceLocation key = CraftNamespacedKey.toMinecraft(tag); switch (registry) { case org.bukkit.Tag.REGISTRY_BLOCKS -> { Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Block namespace (%s) must have material type", clazz.getName()); TagKey blockTagKey = TagKey.create(Registries.BLOCK, key); if (BuiltInRegistries.BLOCK.get(blockTagKey).isPresent()) { return (org.bukkit.Tag) new CraftBlockTag(BuiltInRegistries.BLOCK, blockTagKey); } } case org.bukkit.Tag.REGISTRY_ITEMS -> { Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Item namespace (%s) must have material type", clazz.getName()); TagKey itemTagKey = TagKey.create(Registries.ITEM, key); if (BuiltInRegistries.ITEM.get(itemTagKey).isPresent()) { return (org.bukkit.Tag) new CraftItemTag(BuiltInRegistries.ITEM, itemTagKey); } } case org.bukkit.Tag.REGISTRY_FLUIDS -> { Preconditions.checkArgument(clazz == org.bukkit.Fluid.class, "Fluid namespace (%s) must have fluid type", clazz.getName()); TagKey fluidTagKey = TagKey.create(Registries.FLUID, key); if (BuiltInRegistries.FLUID.get(fluidTagKey).isPresent()) { return (org.bukkit.Tag) new CraftFluidTag(BuiltInRegistries.FLUID, fluidTagKey); } } case org.bukkit.Tag.REGISTRY_ENTITY_TYPES -> { Preconditions.checkArgument(clazz == org.bukkit.entity.EntityType.class, "Entity type namespace (%s) must have entity type", clazz.getName()); TagKey> entityTagKey = TagKey.create(Registries.ENTITY_TYPE, key); if (BuiltInRegistries.ENTITY_TYPE.get(entityTagKey).isPresent()) { return (org.bukkit.Tag) new CraftEntityTag(BuiltInRegistries.ENTITY_TYPE, entityTagKey); } } case org.bukkit.tag.DamageTypeTags.REGISTRY_DAMAGE_TYPES -> { Preconditions.checkArgument(clazz == org.bukkit.damage.DamageType.class, "Damage type namespace (%s) must have damage type", clazz.getName()); TagKey damageTagKey = TagKey.create(Registries.DAMAGE_TYPE, key); net.minecraft.core.Registry damageRegistry = CraftRegistry.getMinecraftRegistry(Registries.DAMAGE_TYPE); if (damageRegistry.get(damageTagKey).isPresent()) { return (org.bukkit.Tag) new CraftDamageTag(damageRegistry, damageTagKey); } } default -> throw new IllegalArgumentException(); } return null; } @Override @SuppressWarnings("unchecked") public Iterable> getTags(String registry, Class clazz) { Preconditions.checkArgument(registry != null, "registry cannot be null"); Preconditions.checkArgument(clazz != null, "Class clazz cannot be null"); switch (registry) { case org.bukkit.Tag.REGISTRY_BLOCKS -> { Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Block namespace (%s) must have material type", clazz.getName()); net.minecraft.core.Registry blockTags = BuiltInRegistries.BLOCK; return blockTags.getTags().map(pair -> (org.bukkit.Tag) new CraftBlockTag(blockTags, pair.key())).collect(ImmutableList.toImmutableList()); } case org.bukkit.Tag.REGISTRY_ITEMS -> { Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Item namespace (%s) must have material type", clazz.getName()); net.minecraft.core.Registry itemTags = BuiltInRegistries.ITEM; return itemTags.getTags().map(pair -> (org.bukkit.Tag) new CraftItemTag(itemTags, pair.key())).collect(ImmutableList.toImmutableList()); } case org.bukkit.Tag.REGISTRY_FLUIDS -> { Preconditions.checkArgument(clazz == org.bukkit.Fluid.class, "Fluid namespace (%s) must have fluid type", clazz.getName()); net.minecraft.core.Registry fluidTags = BuiltInRegistries.FLUID; return fluidTags.getTags().map(pair -> (org.bukkit.Tag) new CraftFluidTag(fluidTags, pair.key())).collect(ImmutableList.toImmutableList()); } case org.bukkit.Tag.REGISTRY_ENTITY_TYPES -> { Preconditions.checkArgument(clazz == org.bukkit.entity.EntityType.class, "Entity type namespace (%s) must have entity type", clazz.getName()); net.minecraft.core.Registry> entityTags = BuiltInRegistries.ENTITY_TYPE; return entityTags.getTags().map(pair -> (org.bukkit.Tag) new CraftEntityTag(entityTags, pair.key())).collect(ImmutableList.toImmutableList()); } case org.bukkit.tag.DamageTypeTags.REGISTRY_DAMAGE_TYPES -> { Preconditions.checkArgument(clazz == org.bukkit.damage.DamageType.class, "Damage type namespace (%s) must have damage type", clazz.getName()); net.minecraft.core.Registry damageTags = CraftRegistry.getMinecraftRegistry(Registries.DAMAGE_TYPE); return damageTags.getTags().map(pair -> (org.bukkit.Tag) new CraftDamageTag(damageTags, pair.key())).collect(ImmutableList.toImmutableList()); } default -> throw new IllegalArgumentException(); } } @Override public LootTable getLootTable(NamespacedKey key) { Preconditions.checkArgument(key != null, "NamespacedKey key cannot be null"); ReloadableServerRegistries.Holder registry = this.getServer().reloadableRegistries(); return registry.lookup().lookup(Registries.LOOT_TABLE) .flatMap((lookup) -> lookup.get(CraftLootTable.bukkitKeyToMinecraft(key))) .map((holder) -> new CraftLootTable(key, holder.value())) .orElse(null); } @Override public List selectEntities(CommandSender sender, String selector) { Preconditions.checkArgument(selector != null, "selector cannot be null"); Preconditions.checkArgument(sender != null, "CommandSender sender cannot be null"); EntityArgument arg = EntityArgument.entities(); List nms; try { StringReader reader = new StringReader(selector); nms = arg.parse(reader, true, true).findEntities(VanillaCommandWrapper.getListener(sender)); Preconditions.checkArgument(!reader.canRead(), "Spurious trailing data in selector: %s", selector); } catch (CommandSyntaxException ex) { throw new IllegalArgumentException("Could not parse selector: " + selector, ex); } return new ArrayList<>(Lists.transform(nms, (entity) -> entity.getBukkitEntity())); } @Override public StructureManager getStructureManager() { return this.structureManager; } @Override public Registry getRegistry(Class aClass) { return (Registry) this.registries.computeIfAbsent(aClass, key -> CraftRegistry.createRegistry(aClass, this.console.registryAccess())); } @Deprecated @Override public UnsafeValues getUnsafe() { return CraftMagicNumbers.INSTANCE; } // Spigot start private final org.bukkit.Server.Spigot spigot = new org.bukkit.Server.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 CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); } @Override public void restart() { org.spigotmc.RestartCommand.restart(); } @Override public void broadcast(BaseComponent component) { for (Player player : CraftServer.this.getOnlinePlayers()) { player.spigot().sendMessage(component); } } @Override public void broadcast(BaseComponent... components) { for (Player player : CraftServer.this.getOnlinePlayers()) { player.spigot().sendMessage(components); } } }; public org.bukkit.Server.Spigot spigot() { return this.spigot; } // Spigot end @Override public double[] getTPS() { return new double[] { net.minecraft.server.MinecraftServer.getServer().tps1.getAverage(), net.minecraft.server.MinecraftServer.getServer().tps5.getAverage(), net.minecraft.server.MinecraftServer.getServer().tps15.getAverage() }; } // Paper start - adventure sounds @Override public void playSound(final net.kyori.adventure.sound.Sound sound) { if (sound.seed().isEmpty()) org.spigotmc.AsyncCatcher.catchOp("play sound; cannot generate seed with world random"); // Paper final long seed = sound.seed().orElseGet(this.console.overworld().getRandom()::nextLong); for (ServerPlayer player : this.playerList.getPlayers()) { player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player.getX(), player.getY(), player.getZ(), seed, null)); } } @Override public void playSound(final net.kyori.adventure.sound.Sound sound, final double x, final double y, final double z) { org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, x, y, z, sound.seed().orElseGet(this.console.overworld().getRandom()::nextLong), this.playSound0(x, y, z, this.console.getAllLevels())); } @Override public void playSound(final net.kyori.adventure.sound.Sound sound, final net.kyori.adventure.sound.Sound.Emitter emitter) { if (sound.seed().isEmpty()) org.spigotmc.AsyncCatcher.catchOp("play sound; cannot generate seed with world random"); // Paper final long seed = sound.seed().orElseGet(this.console.overworld().getRandom()::nextLong); if (emitter == net.kyori.adventure.sound.Sound.Emitter.self()) { for (ServerPlayer player : this.playerList.getPlayers()) { player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player, seed, null)); } } else if (emitter instanceof org.bukkit.craftbukkit.entity.CraftEntity craftEntity) { org.spigotmc.AsyncCatcher.catchOp("play sound; cannot use entity emitter"); // Paper final net.minecraft.world.entity.Entity entity = craftEntity.getHandle(); io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, entity, seed, this.playSound0(entity.getX(), entity.getY(), entity.getZ(), List.of((ServerLevel) entity.level()))); } else { throw new IllegalArgumentException("Sound emitter must be an Entity or self(), but was: " + emitter); } } private java.util.function.BiConsumer, Float> playSound0(final double x, final double y, final double z, final Iterable levels) { return (packet, distance) -> { for (final ServerLevel level : levels) { level.getServer().getPlayerList().broadcast(null, x, y, z, distance, level.dimension(), packet); } }; } // Paper end // Paper start @SuppressWarnings({"rawtypes", "unchecked"}) public static java.nio.file.Path dumpHeap(java.nio.file.Path dir, String name) { try { java.nio.file.Files.createDirectories(dir); javax.management.MBeanServer server = java.lang.management.ManagementFactory.getPlatformMBeanServer(); java.nio.file.Path file; try { Class clazz = Class.forName("openj9.lang.management.OpenJ9DiagnosticsMXBean"); Object openj9Mbean = java.lang.management.ManagementFactory.newPlatformMXBeanProxy(server, "openj9.lang.management:type=OpenJ9Diagnostics", clazz); java.lang.reflect.Method m = clazz.getMethod("triggerDumpToFile", String.class, String.class); file = dir.resolve(name + ".phd"); m.invoke(openj9Mbean, "heap", file.toString()); } catch (ClassNotFoundException e) { Class clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean"); Object hotspotMBean = java.lang.management.ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", clazz); java.lang.reflect.Method m = clazz.getMethod("dumpHeap", String.class, boolean.class); file = dir.resolve(name + ".hprof"); m.invoke(hotspotMBean, file.toString(), true); } return file; } catch (Throwable t) { Bukkit.getLogger().log(Level.SEVERE, "Could not write heap", t); return null; } } private Iterable adventure$audiences; @Override public Iterable audiences() { if (this.adventure$audiences == null) { this.adventure$audiences = com.google.common.collect.Iterables.concat(java.util.Collections.singleton(this.getConsoleSender()), this.getOnlinePlayers()); } return this.adventure$audiences; } @Override public void reloadPermissions() { pluginManager.clearPermissions(); if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.loadPermissionsYmlBeforePlugins) loadCustomPermissions(); for (Plugin plugin : pluginManager.getPlugins()) { for (Permission perm : plugin.getDescription().getPermissions()) { 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); } } } if (!io.papermc.paper.configuration.GlobalConfiguration.get().misc.loadPermissionsYmlBeforePlugins) loadCustomPermissions(); DefaultPermissions.registerCorePermissions(); CraftDefaultPermissions.registerCorePermissions(); } // Paper end }