diff --git a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java index df00fbe6..3861a39b 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java +++ b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java @@ -77,6 +77,8 @@ import com.onarandombox.MultiverseCore.destination.PlayerDestination; import com.onarandombox.MultiverseCore.destination.WorldDestination; import com.onarandombox.MultiverseCore.event.MVDebugModeEvent; import com.onarandombox.MultiverseCore.event.MVVersionEvent; +import com.onarandombox.MultiverseCore.generators.GeneratorManager; +import com.onarandombox.MultiverseCore.api.MVGeneratorManager; import com.onarandombox.MultiverseCore.listeners.MVAsyncPlayerChatListener; import com.onarandombox.MultiverseCore.listeners.MVChatListener; import com.onarandombox.MultiverseCore.listeners.MVEntityListener; @@ -213,6 +215,7 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core { // Configurations private FileConfiguration multiverseConfig = null; + private MVGeneratorManager generatorManager; private final MVWorldManager worldManager = new WorldManager(this); // Setup the block/player/entity listener. @@ -280,7 +283,7 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core { this.messaging = new MVMessaging(); this.economist = new MVEconomist(this); // Load the defaultWorldGenerators - this.worldManager.getDefaultWorldGenerators(); + this.generatorManager = new GeneratorManager(this); this.registerEvents(); // Setup Permissions, we'll do an initial check for the Permissions plugin then fall back on isOP(). @@ -1014,6 +1017,14 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core { this.serverFolder = newServerFolder; } + /** + * {@inheritDoc} + */ + @Override + public MVGeneratorManager getMVGeneratorManager() { + return generatorManager; + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/Core.java b/src/main/java/com/onarandombox/MultiverseCore/api/Core.java index ff8513d4..ba62372b 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/api/Core.java +++ b/src/main/java/com/onarandombox/MultiverseCore/api/Core.java @@ -94,6 +94,13 @@ public interface Core { */ DestinationFactory getDestFactory(); + /** + * Gets the class responsible for managing world generators plugins installed on the server. + * + * @return A valid {@link MVGeneratorManager}. + */ + MVGeneratorManager getMVGeneratorManager(); + /** * Gets the primary class responsible for managing Multiverse Worlds. * diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/GeneratorPlugin.java b/src/main/java/com/onarandombox/MultiverseCore/api/GeneratorPlugin.java new file mode 100644 index 00000000..07dcbb36 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/api/GeneratorPlugin.java @@ -0,0 +1,101 @@ +package com.onarandombox.MultiverseCore.api; + +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + *

A generator API for Multiverse.

+ * + *

Any generator plugin can register themselves to Multiverse. This will provide Multiverse with addition + * information about your generator plugin such as generator id suggestions, example usages and link to more + * info on your generator plugin.

+ * + *

To register your plugin, implement this class and see {@link MVGeneratorManager#register(GeneratorPlugin)}.

+ */ +public interface GeneratorPlugin { + + /** + *

Suggest possible generator ids. To be used in command tab-completion.

+ * + *

These suggestions can be static without relying on currentIdInput, or dynamically changed based + * on the currentIdInput.

+ * + * @param currentIdInput The current state user input. This may be null or empty if user has not started + * any input for generator id. + * @return Collection of suggested generator ids. + */ + @NotNull + Collection suggestIds(@Nullable String currentIdInput); + + /** + *

Gets a default or any valid {@link ChunkGenerator}.

+ * + *

This is used on {@link MVGeneratorManager#register(GeneratorPlugin)} to prove to Multiverse that + * you are a valid generator plugin. If this returns null or throw an exception, your generator plugin + * will not be registered.

+ * + * @return A {@link ChunkGenerator} if possible, else null. + * @throws Exception When an error occurs in getting {@link ChunkGenerator}. + */ + @Nullable + ChunkGenerator getDefaultChunkGenerator() throws Exception; + + /** + *

Gets a chunk generator based on a world name and id specified.

+ * + *

If this returns null or throw an exception, it will indicate to Multiverse that there is either an issue + * with your plugin, or the generator id given is invalid.

+ * + * @param id Target generator id. + * @param worldName Target world name to use generator on. + * @return A {@link ChunkGenerator} if possible, else null. + * @throws Exception When an error occurs in getting a {@link ChunkGenerator}. + */ + @Nullable + ChunkGenerator getChunkGenerator(String id, String worldName) throws Exception; + + /** + *

Gets command usages that users can try to generate a world with your generator plugin. Returning null means + * you do not wish to show any usage examples for your generator plugin.

+ * + *

An example command: '/mv create myworld normal -g CoolGen:FunWorld'

+ * + *

Notes on usage of this method:

+ * + * + * @return A collection of command usage examples. + */ + @Nullable + Collection getExampleUsages(); + + /** + *

Gets a link with more information on your generator plugin. Returning null means you do not wish to link + * users to any website related to your generator plugin.

+ * + *

An example info: 'Click on https://www.amazinggenerator.io ;)'

+ * + *

Some suggested places you can link to are: spigot resource page, github repo or your own plugin site.

+ * + * @return Link to more info on your generator plugin. + */ + @Nullable + String getInfoLink(); + + /** + * Gets the java plugin for this generator. In short, return your own generator plugin instance. + * + * @return The associated plugin for this generator. + */ + @NotNull + Plugin getPlugin(); +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MVGeneratorManager.java b/src/main/java/com/onarandombox/MultiverseCore/api/MVGeneratorManager.java new file mode 100644 index 00000000..aa94ba95 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/api/MVGeneratorManager.java @@ -0,0 +1,134 @@ +package com.onarandombox.MultiverseCore.api; + +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; + +/** + * API for handling generator plugins installed on the user's server. + */ +public interface MVGeneratorManager { + + /** + * Register a generator plugin. + * + * @param generatorPlugin The {@link GeneratorPlugin} to register. + * @return True if successfully registered, else false. + */ + boolean register(@NotNull GeneratorPlugin generatorPlugin); + + /** + * Check if a given generator string is valid. + * + * @param genString The generator string to check on. + * @param worldName The target world name to generate on. + * @return True if valid, else false. + */ + boolean isValidGenerator(@NotNull String genString, @NotNull String worldName); + + /** + * Check if a given generator string is valid with reason of failure with {@link TestResult}. + * + * @param genString The generator string to check on. + * @param worldName The target world name to generate on. + * @return Result of the validation. + */ + @NotNull + TestResult validateGenerator(@NotNull String genString, @NotNull String worldName); + + /** + * Checks if a plugin is a registered generator plugin. + * + * @param genPlugin The plugin to check on. + * @return True if is valid, else false. + */ + boolean isGeneratorPlugin(@NotNull Plugin genPlugin); + + /** + * Checks if a plugin is a registered generator plugin. + * + * @param pluginName The plugin name to check on. + * @return True if is valid, else false. + */ + boolean isGeneratorPlugin(@NotNull String pluginName); + + /** + * Gets the {@link GeneratorPlugin} for a given plugin if present. + * + * @param genPlugin The plugin that is associated to a {@link GeneratorPlugin}. + * @return The {@link GeneratorPlugin} if present, else null. + */ + @Nullable + GeneratorPlugin getGeneratorPlugin(@NotNull Plugin genPlugin); + + /** + * Gets the {@link GeneratorPlugin} for a given plugin if present. + * + * @param pluginName The plugin name that is associated to a {@link GeneratorPlugin}. + * @return The {@link GeneratorPlugin} if present, else null. + */ + @Nullable + GeneratorPlugin getGeneratorPlugin(@NotNull String pluginName); + + /** + * Gets all registered {@link GeneratorPlugin}. + * + * @return A collection of registered generator plugins. + */ + @NotNull + Collection getGeneratorPlugins(); + + /** + * Gets all registered plugin names associate with {@link GeneratorPlugin}. + * + * @return A collection of registered generator plugin names. + */ + @NotNull + Collection getGeneratorPluginNames(); + + /** + * Gets the default world generator string defined in 'bukkit.yml' for a given world. + * + * @param worldName The world to get default generator of. + * @return The generator string if present, else null. + */ + @Nullable + String getDefaultWorldGen(String worldName); + + /** + * Gets all the world generator strings defined in 'bukkit.yml'. + * + * @return Map if world names and its respective default world generator strings. + */ + @NotNull + Map getDefaultWorldGens(); + + /** + * Results from validating a generator. + */ + enum TestResult { + /** + * Successfully got {@link ChunkGenerator} from the world generator plugin. + */ + VALID, + + /** + * Exceptions thrown while trying to get {@link ChunkGenerator} from the world generator plugin. + */ + ERRORS, + + /** + * Plugin is present on the server, but doesnt look like a world generator. + */ + INVALID_GENERATOR, + + /** + * Plugin not installed on the server. + */ + PLUGIN_DOES_NOT_EXIST + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MVWorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/api/MVWorldManager.java index 3eb96e2d..98f4d729 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MVWorldManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/api/MVWorldManager.java @@ -7,6 +7,7 @@ package com.onarandombox.MultiverseCore.api; +import com.onarandombox.MultiverseCore.generators.GeneratorManager; import com.onarandombox.MultiverseCore.utils.PurgeWorlds; import com.onarandombox.MultiverseCore.utils.SimpleWorldPurger; import org.bukkit.World; @@ -154,7 +155,9 @@ public interface MVWorldManager { * @param generatorID The generator id. * @param worldName The worldName to use as the default. * @return A {@link ChunkGenerator} or null + * @deprecated Use {@link MVGeneratorManager#validateGenerator(String, String)} */ + @Deprecated ChunkGenerator getChunkGenerator(String generator, String generatorID, String worldName); /** @@ -249,7 +252,9 @@ public interface MVWorldManager { /** * This method populates an internal list and needs to be called after multiverse initialization. + * @deprecated This is now done by {@link GeneratorManager}. */ + @Deprecated void getDefaultWorldGenerators(); /** diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java index 87bdacab..c6bb7328 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java @@ -9,6 +9,7 @@ package com.onarandombox.MultiverseCore.commands; import com.onarandombox.MultiverseCore.MultiverseCore; import com.onarandombox.MultiverseCore.api.MVWorldManager; +import com.onarandombox.MultiverseCore.api.MVGeneratorManager; import com.pneumaticraft.commandhandler.CommandHandler; import org.bukkit.ChatColor; import org.bukkit.World.Environment; @@ -18,8 +19,6 @@ import org.bukkit.command.CommandSender; import org.bukkit.permissions.PermissionDefault; import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -98,14 +97,26 @@ public class CreateCommand extends MultiverseCommand { } // Determine if the generator is valid. #918 if (generator != null) { - List genarray = new ArrayList(Arrays.asList(generator.split(":"))); - if (genarray.size() < 2) { - // If there was only one arg specified, pad with another empty one. - genarray.add(""); - } - if (this.worldManager.getChunkGenerator(genarray.get(0), genarray.get(1), "test") == null) { + MVGeneratorManager.TestResult result = this.plugin.getMVGeneratorManager().validateGenerator(generator, worldName); + if (result != MVGeneratorManager.TestResult.VALID) { // We have an invalid generator. - sender.sendMessage("Invalid generator! '" + generator + "'. " + ChatColor.RED + "Aborting world creation."); + switch (result) { + case ERRORS: + sender.sendMessage("An issue occurred while validating generator string '" + generator + + "'! This may indicate an issue with your generator plugin or an invalid generator id. Check console for errors!"); + break; + case INVALID_GENERATOR: + sender.sendMessage("Invalid generator string '" + generator + + "'! The generator id you use is probably not supported by your generator plugin."); + break; + case PLUGIN_DOES_NOT_EXIST: + sender.sendMessage("That generator plugin is not installed on your server!"); + break; + default: + sender.sendMessage("Invalid generator string '" + generator + "'!"); + break; + } + sender.sendMessage(ChatColor.RED + "Aborting world creation..."); return; } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/GeneratorCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/GeneratorCommand.java index 72d4379b..05f20e39 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/GeneratorCommand.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/GeneratorCommand.java @@ -9,12 +9,19 @@ package com.onarandombox.MultiverseCore.commands; import com.dumptruckman.minecraft.util.Logging; import com.onarandombox.MultiverseCore.MultiverseCore; +import com.onarandombox.MultiverseCore.api.GeneratorPlugin; +import com.onarandombox.MultiverseCore.generators.SimpleGeneratorPlugin; +import net.milkbowl.vault.chat.Chat; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.generator.ChunkGenerator; import org.bukkit.permissions.PermissionDefault; import org.bukkit.plugin.Plugin; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -25,36 +32,70 @@ public class GeneratorCommand extends MultiverseCommand { public GeneratorCommand(MultiverseCore plugin) { super(plugin); this.setName("World Information"); - this.setCommandUsage("/mv generators"); - this.setArgRange(0, 0); + this.setCommandUsage("/mv generators [plugin]"); + this.setArgRange(0, 1); this.addKey("mv generators"); this.addKey("mvgenerators"); this.addKey("mv gens"); this.addKey("mvgens"); this.addCommandExample("/mv generators"); - this.setPermission("multiverse.core.generator", "Returns a list of Loaded Generator Plugins.", PermissionDefault.OP); + this.addCommandExample("/mv generators VoidGenerator"); + this.setPermission("multiverse.core.generator", "Returns info of Loaded Generator Plugins.", PermissionDefault.OP); } @Override public void runCommand(CommandSender sender, List args) { - Logging.info("PLEASE IGNORE the 'Plugin X does not contain any generators' message below!"); - Plugin[] plugins = this.plugin.getServer().getPluginManager().getPlugins(); - List generators = new ArrayList(); - for (Plugin p : plugins) { - if (p.isEnabled() && p.getDefaultWorldGenerator("world", "") != null) { - generators.add(p.getDescription().getName()); - } + if (args.size() == 0) { + listKnownGenerators(sender); + return; } + showGeneratorDetails(sender, args.get(0)); + } + + private void listKnownGenerators(CommandSender sender) { sender.sendMessage(ChatColor.AQUA + "--- Loaded Generator Plugins ---"); - String loadedGens = ""; + StringBuilder loadedGens = new StringBuilder(); boolean altColor = false; - for (String s : generators) { - loadedGens += (altColor ? ChatColor.YELLOW : ChatColor.WHITE) + s + " "; + for (String s : this.plugin.getMVGeneratorManager().getGeneratorPluginNames()) { + loadedGens.append(altColor ? ChatColor.YELLOW : ChatColor.WHITE).append(s).append(' '); altColor = !altColor; } if (loadedGens.length() == 0) { - loadedGens = ChatColor.RED + "No Generator Plugins found."; + loadedGens.append(ChatColor.RED).append("No Generator Plugins found."); + } + sender.sendMessage(loadedGens.toString()); + } + + private void showGeneratorDetails(CommandSender sender, String pluginName) { + Plugin genPlugin = Bukkit.getPluginManager().getPlugin(pluginName); + if (genPlugin == null) { + sender.sendMessage(ChatColor.RED + "You do not have a plugin named '"+ pluginName + "' on your server."); + return; + } + + GeneratorPlugin generatorPlugin = this.plugin.getMVGeneratorManager().getGeneratorPlugin(genPlugin); + if (generatorPlugin == null) { + sender.sendMessage(ChatColor.RED + "'" + pluginName + "' does not look like a generator plugin."); + return; + } + + sender.sendMessage(ChatColor.AQUA + "--- Info on " + generatorPlugin.getPlugin().getName() + " ---"); + boolean hasAddInfo = false; + + Collection exampleUsages = generatorPlugin.getExampleUsages(); + if (exampleUsages != null && exampleUsages.size() > 0) { + hasAddInfo = true; + sender.sendMessage("Usages:"); + exampleUsages.forEach(sender::sendMessage); + } + String infoLink = generatorPlugin.getInfoLink(); + if (infoLink != null) { + hasAddInfo = true; + sender.sendMessage("More Info: " + infoLink); + } + + if (!hasAddInfo) { + sender.sendMessage(ChatColor.RED + "'" + pluginName + "' did not provide additional info to Multiverse."); } - sender.sendMessage(loadedGens); } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/generators/GeneratorManager.java b/src/main/java/com/onarandombox/MultiverseCore/generators/GeneratorManager.java new file mode 100644 index 00000000..c30b77cb --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/generators/GeneratorManager.java @@ -0,0 +1,311 @@ +package com.onarandombox.MultiverseCore.generators; + +import com.dumptruckman.minecraft.util.Logging; +import com.onarandombox.MultiverseCore.MultiverseCore; +import com.onarandombox.MultiverseCore.api.GeneratorPlugin; +import com.onarandombox.MultiverseCore.api.MVGeneratorManager; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +/** + * Default implementation of {@link MVGeneratorManager} to facilitate the detection and management of generator + * plugins installed on user's server. + */ +public class GeneratorManager implements MVGeneratorManager { + + private final MultiverseCore plugin; + private final Map defaultGens; + private Map generatorPluginMap; + + public GeneratorManager(MultiverseCore plugin) { + this.plugin = plugin; + this.defaultGens = new HashMap<>(); + loadDefaultWorldGenerators(); + findAndRegisterAllGeneratorPlugins(); + Bukkit.getPluginManager().registerEvents(new GeneratorListener(), this.plugin); + } + + /** + * Loads default generator strings defined in 'bukkit.yml'. + */ + private void loadDefaultWorldGenerators() { + this.generatorPluginMap = new HashMap<>(); + + File[] files = this.plugin.getServerFolder().listFiles((file, s) -> s.equalsIgnoreCase("bukkit.yml")); + if (files == null || files.length != 1) { + Logging.fine("Could not read 'bukkit.yml'. Any Default worldgenerators will not be loaded!"); + return; + } + + FileConfiguration bukkitConfig = YamlConfiguration.loadConfiguration(files[0]); + if (!bukkitConfig.isConfigurationSection("worlds")) { + Logging.fine("'bukkit.yml' missing worlds config section. No default generators found!"); + return; + } + + Set keys = bukkitConfig.getConfigurationSection("worlds").getKeys(false); + for (String key : keys) { + this.defaultGens.put(key, bukkitConfig.getString("worlds." + key + ".generator", "")); + } + + this.generatorPluginMap = Collections.unmodifiableMap(this.generatorPluginMap); + } + + /** + * Register all generator plugins detected. + */ + private void findAndRegisterAllGeneratorPlugins() { + Arrays.stream(Bukkit.getPluginManager().getPlugins()).forEach(this::register); + } + + /** + * Attempts to register a plugin as {@link SimpleGeneratorPlugin}. + * + * @param genPlugin The plugin to register. + * @return True if registered successfully, else false. + */ + private boolean register(@NotNull Plugin genPlugin) { + return this.register(genPlugin, SimpleGeneratorPlugin.DEFAULT_TEST_ID); + } + + /** + * Attempts to register a plugin as {@link SimpleGeneratorPlugin}. + * + * @param genPlugin The plugin to register. + * @param id Generator Id to test for valid chunk generator. + * @return True if registered successfully, else false. + */ + private boolean register(@NotNull Plugin genPlugin, String id) { + Logging.finer("Attempting to register %s as a generator...", genPlugin.getName()); + if (this.isGeneratorPlugin(genPlugin)) { + return false; + } + if (!simpleTestGen(genPlugin, id)) { + Logging.finer("%s is probably not a generator plugin!", genPlugin.getName()); + return false; + } + this.generatorPluginMap.put(genPlugin.getName(), new SimpleGeneratorPlugin(genPlugin)); + Logging.finer("Registered %s as a simple generator plugin.", genPlugin.getName()); + return true; + } + + /** + * Basic test to see if plugin is a valid generator. + * + * @param generator The potential generator plugin. + * @param id Generator Id to test for valid chunk generator. + * @return True if plugin is a valid generator, else false. + */ + private boolean simpleTestGen(@Nullable Plugin generator, String id) { + if (generator == null) { + return false; + } + + // Since we are unsure if the plugin is even suppose to be a generator, we assume any error means its + // not a generator plugin. + try { + return generator.getDefaultWorldGenerator(SimpleGeneratorPlugin.TEST_WORLDNAME, id) != null; + } catch (Exception ignore) { + return false; + } + } + + /** + * Unregisters a plugin. + * + * @param genPlugin The plugin to unregister. + * @return True if the plugin was present and now unregistered, else false. + */ + private boolean unregister(@NotNull Plugin genPlugin) { + return this.generatorPluginMap.remove(genPlugin.getName()) != null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean register(@NotNull GeneratorPlugin generatorPlugin) { + Plugin genPlugin = generatorPlugin.getPlugin(); + + // Make sure not registered before. + GeneratorPlugin registeredGeneratorPlugin = this.getGeneratorPlugin(genPlugin); + if (registeredGeneratorPlugin != null) { + if (!(registeredGeneratorPlugin instanceof SimpleGeneratorPlugin)) { + throw new IllegalStateException("You cannot register plugin for '" + genPlugin.getName() + "' twice!"); + } + // If it's auto registration by MV, we can remove in favour of the custom one. + this.unregister(genPlugin); + } + + if (this.safelyTestGenerator(genPlugin, generatorPlugin::getDefaultChunkGenerator) != TestResult.VALID) { + return false; + } + + this.generatorPluginMap.put(genPlugin.getName(), generatorPlugin); + Logging.fine("Registered %s as a simple generator plugin.", genPlugin.getName()); + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isValidGenerator(@NotNull String genString, @NotNull String worldName) { + return validateGenerator(genString, worldName) == TestResult.VALID; + } + + /** + * {@inheritDoc} + */ + @Override + @NotNull + public TestResult validateGenerator(@NotNull String genString, @NotNull String worldName) { + String[] genArray = genString.split(":", 2); + String pluginName = genArray[0]; + String id = (genArray.length == 1) ? "" : genArray[1]; + + Plugin genPlugin = Bukkit.getPluginManager().getPlugin(pluginName); + if (genPlugin == null) { + return TestResult.PLUGIN_DOES_NOT_EXIST; + } + + if (!this.isGeneratorPlugin(genPlugin)) { + // Tries to register again with specific Id defined. + if (!this.register(genPlugin, id)) { + return TestResult.INVALID_GENERATOR; + } + } + + GeneratorPlugin generatorPlugin = this.getGeneratorPlugin(genPlugin); + if (generatorPlugin == null) { + return TestResult.INVALID_GENERATOR; + } + + TestResult testResult = safelyTestGenerator(generatorPlugin.getPlugin(), () -> generatorPlugin.getChunkGenerator(id, worldName)); + if (testResult == TestResult.VALID && generatorPlugin instanceof SimpleGeneratorPlugin) { + ((SimpleGeneratorPlugin) generatorPlugin).addKnownWorkingId(id); + } + return testResult; + } + + /** + * Tries to get chunk generator with wrapper to catch any exceptions that may occur. + * + * @param genPlugin The generator plugin. + * @param genGetter Logic to get the chunk generator. + * @return VALID if successfully got a not-null chunk generator, INVALID_GENERATOR if null, + * ERRORS if exception is thrown. + */ + @NotNull + private TestResult safelyTestGenerator(Plugin genPlugin, Callable genGetter) { + TestResult result = this.plugin.getUnsafeCallWrapper().wrap( + () -> genGetter.call() == null ? TestResult.INVALID_GENERATOR : TestResult.VALID, + genPlugin.getName(), + "Failed to get the chunk generator: %s" + ); + return (result == null) ? TestResult.ERRORS : result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isGeneratorPlugin(@NotNull Plugin genPlugin) { + return this.isGeneratorPlugin(genPlugin.getName()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isGeneratorPlugin(@NotNull String pluginName) { + return this.generatorPluginMap.containsKey(pluginName); + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public GeneratorPlugin getGeneratorPlugin(@NotNull Plugin genPlugin) { + return this.getGeneratorPlugin(genPlugin.getName()); + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public GeneratorPlugin getGeneratorPlugin(@NotNull String pluginName) { + return this.generatorPluginMap.get(pluginName); + } + + /** + * {@inheritDoc} + */ + @Override + @NotNull + public Collection getGeneratorPlugins() { + return this.generatorPluginMap.values(); + } + + /** + * {@inheritDoc} + */ + @Override + @NotNull + public Collection getGeneratorPluginNames() { + return this.generatorPluginMap.keySet(); + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public String getDefaultWorldGen(String worldName) { + return this.defaultGens.get(worldName); + } + + /** + * {@inheritDoc} + */ + @Override + @NotNull + public Map getDefaultWorldGens() { + return this.defaultGens; + } + + /** + * Listen to when a plugin enables/disable to register and unregister accordingly. + */ + private class GeneratorListener implements Listener { + @EventHandler + public void onEnable(PluginEnableEvent event) { + register(event.getPlugin()); + } + + @EventHandler + public void onDisable(PluginDisableEvent event) { + unregister(event.getPlugin()); + } + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/generators/SimpleGeneratorPlugin.java b/src/main/java/com/onarandombox/MultiverseCore/generators/SimpleGeneratorPlugin.java new file mode 100644 index 00000000..74cb5c76 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/generators/SimpleGeneratorPlugin.java @@ -0,0 +1,94 @@ +package com.onarandombox.MultiverseCore.generators; + +import com.onarandombox.MultiverseCore.api.GeneratorPlugin; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * A default implementation of {@link GeneratorPlugin} for those generator plugins that do not provide their own + * custom {@link GeneratorPlugin} implementation to Multiverse. + */ +public class SimpleGeneratorPlugin implements GeneratorPlugin { + + public static String TEST_WORLDNAME = "test"; + public static String DEFAULT_TEST_ID = ""; + + private final Plugin plugin; + private final List workingIds; + + SimpleGeneratorPlugin(Plugin plugin) { + this(plugin, DEFAULT_TEST_ID); + } + + SimpleGeneratorPlugin(Plugin plugin, String testedId) { + this.plugin = plugin; + this.workingIds = new ArrayList<>(); + this.workingIds.add(testedId); + } + + /** + * Adds a known generator id that was tested to be working. + * + * @param id The known working generator id. + */ + void addKnownWorkingId(String id) { + if (!this.workingIds.contains(id)) { + this.workingIds.add(id); + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestIds(@Nullable String currentIdInput) { + return this.workingIds; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable ChunkGenerator getDefaultChunkGenerator() throws Exception { + return this.plugin.getDefaultWorldGenerator(TEST_WORLDNAME, this.workingIds.get(0)); + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable ChunkGenerator getChunkGenerator(String id, String worldName) throws Exception { + return this.plugin.getDefaultWorldGenerator(worldName, id); + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable Collection getExampleUsages() { + return null; + } + + /** + * {@inheritDoc} + * @return + */ + @Override + public @Nullable String getInfoLink() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Plugin getPlugin() { + return this.plugin; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/WorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/utils/WorldManager.java index 3979b7cb..2cf4d1b4 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/WorldManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/WorldManager.java @@ -74,6 +74,7 @@ public class WorldManager implements MVWorldManager { * {@inheritDoc} */ @Override + @Deprecated public void getDefaultWorldGenerators() { this.defaultGens = new HashMap(); File[] files = this.plugin.getServerFolder().listFiles(new FilenameFilter() { @@ -301,6 +302,7 @@ public class WorldManager implements MVWorldManager { * {@inheritDoc} */ @Override + @Deprecated public ChunkGenerator getChunkGenerator(String generator, final String generatorID, final String worldName) { if (generator == null) { return null; @@ -694,10 +696,7 @@ public class WorldManager implements MVWorldManager { for (World w : myWorlds) { String name = w.getName(); if (!worldsFromTheConfig.containsKey(name)) { - String generator = null; - if (this.defaultGens.containsKey(name)) { - generator = this.defaultGens.get(name); - } + String generator = this.plugin.getMVGeneratorManager().getDefaultWorldGen(name); this.addWorld(name, w.getEnvironment(), String.valueOf(w.getSeed()), w.getWorldType(), w.canGenerateStructures(), generator); } } diff --git a/src/test/java/com/onarandombox/MultiverseCore/TestWorldStuff.java b/src/test/java/com/onarandombox/MultiverseCore/TestWorldStuff.java index 05957c84..10eeb8d4 100644 --- a/src/test/java/com/onarandombox/MultiverseCore/TestWorldStuff.java +++ b/src/test/java/com/onarandombox/MultiverseCore/TestWorldStuff.java @@ -207,7 +207,7 @@ public class TestWorldStuff { assertEquals(0, creator.getCore().getMVWorldManager().getMVWorlds().size()); // Verify - verify(mockCommandSender).sendMessage("Invalid generator! 'BogusGen'. " + ChatColor.RED + "Aborting world creation."); + verify(mockCommandSender).sendMessage("That generator plugin is not installed on your server!"); } @Test