From 94e4693b5ec4c0139dcd4994c9578d508f98b3fa Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Thu, 29 Jul 2021 15:08:42 +0200 Subject: [PATCH 1/6] Add initial multi plugin management --- .../serverutils/bukkit/ServerUtils.java | 7 +- .../commands/BukkitCommandServerUtils.java | 28 +- .../bukkit/entities/BukkitLoadResult.java | 20 - .../bukkit/entities/BukkitPlugin.java | 22 +- .../entities/BukkitPluginDescription.java | 59 +++ .../bukkit/managers/BukkitPluginManager.java | 337 ++++++--------- .../src/main/resources/bukkit-commands.json | 4 +- .../commands/BungeeCommandServerUtils.java | 7 +- .../bungee/entities/BungeeLoadResult.java | 20 - .../bungee/entities/BungeePlugin.java | 7 +- .../entities/BungeePluginDescription.java | 57 +++ .../bungee/managers/BungeePluginManager.java | 278 ++++++------ Common/build.gradle | 3 - .../serverutils/common/ServerUtilsApp.java | 52 ++- .../common/commands/CommandPlugins.java | 2 +- .../common/commands/CommandServerUtils.java | 113 ++--- .../common/commands/ServerUtilsCommand.java | 2 +- .../commands/arguments/JarFilesArgument.java | 72 ++++ .../commands/arguments/PluginArgument.java | 66 +++ .../commands/arguments/PluginsArgument.java | 79 ++++ .../commands/brigadier/BrigadierHandler.java | 31 ++ .../common/config/CommandsResource.java | 2 +- .../common/config/ConfigResource.java | 2 +- .../common/config/MessagesResource.java | 2 +- .../common/config/ServerUtilsResource.java | 6 +- .../common/entities/CloseableResult.java | 92 ---- .../common/entities/LoadResult.java | 61 --- .../common/entities/ServerUtilsPlugin.java | 11 +- .../ServerUtilsPluginDescription.java | 19 + .../InvalidPluginDescriptionException.java | 16 + .../{ => results}/AbstractResult.java | 4 +- .../results/CloseablePluginResult.java | 43 ++ .../results/CloseablePluginResults.java | 81 ++++ .../common/entities/results/PluginResult.java | 37 ++ .../entities/results/PluginResults.java | 80 ++++ .../common/entities/{ => results}/Result.java | 3 +- .../entities/{ => results}/WatchResult.java | 3 +- .../common/listeners/PlayerListener.java | 2 +- .../common/listeners/ServerUtilsListener.java | 2 +- .../managers/AbstractPluginManager.java | 308 +++++++++++++- .../common/providers/PluginProvider.java | 47 ++- .../common/tasks/PluginWatcherTask.java | 10 +- .../common/tasks/UpdateCheckerTask.java | 24 +- .../common/utils/DependencyUtils.java | 117 ++++++ Common/src/main/resources/commands.json | 6 +- .../common/utils/DependencyUtilsTest.java | 127 ++++++ .../commands/VelocityCommandServerUtils.java | 5 +- .../velocity/entities/VelocityLoadResult.java | 20 - .../velocity/entities/VelocityPlugin.java | 11 +- .../entities/VelocityPluginDescription.java | 69 +++ .../managers/VelocityPluginManager.java | 395 ++++++++++-------- .../reflection/RVelocityEventManager.java | 23 +- build.gradle | 29 +- config/checkstyle/checkstyle.xml | 2 +- 54 files changed, 1988 insertions(+), 937 deletions(-) delete mode 100644 Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitLoadResult.java create mode 100644 Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPluginDescription.java delete mode 100644 Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeeLoadResult.java create mode 100644 Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePluginDescription.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/JarFilesArgument.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginArgument.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java delete mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java delete mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/LoadResult.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPluginDescription.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/exceptions/InvalidPluginDescriptionException.java rename Common/src/main/java/net/frankheijden/serverutils/common/entities/{ => results}/AbstractResult.java (72%) create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResult.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResult.java create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResults.java rename Common/src/main/java/net/frankheijden/serverutils/common/entities/{ => results}/Result.java (90%) rename Common/src/main/java/net/frankheijden/serverutils/common/entities/{ => results}/WatchResult.java (84%) create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/utils/DependencyUtils.java create mode 100644 Common/src/test/java/net/frankheijden/serverutils/common/utils/DependencyUtilsTest.java delete mode 100644 Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityLoadResult.java create mode 100644 Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPluginDescription.java diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/ServerUtils.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/ServerUtils.java index 375128f..f9ec40d 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/ServerUtils.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/ServerUtils.java @@ -2,6 +2,7 @@ package net.frankheijden.serverutils.bukkit; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import net.frankheijden.serverutils.bukkit.entities.BukkitPlugin; import net.frankheijden.serverutils.bukkit.managers.BukkitPluginManager; import net.frankheijden.serverutils.bukkit.reflection.RCraftServer; @@ -65,14 +66,14 @@ public class ServerUtils extends JavaPlugin { String commandString = StringUtils.join(":", split, 1); PluginCommand pluginCommand = Bukkit.getPluginCommand(commandString); - Plugin plugin = getPlugin().getPluginManager().getPlugin(split[0]); - if (plugin == null) { + Optional pluginOptional = getPlugin().getPluginManager().getPlugin(split[0]); + if (!pluginOptional.isPresent()) { getLogger().warning("Unknown plugin '" + split[0] + "' in disabled-commands!"); continue; } else if (pluginCommand == null) { getLogger().warning("Unknown command '" + commandString + "' in disabled-commands!"); continue; - } else if (!plugin.getName().equalsIgnoreCase(pluginCommand.getPlugin().getName())) { + } else if (pluginOptional.get().getName().equalsIgnoreCase(pluginCommand.getPlugin().getName())) { // No output here, plugin didn't match! continue; } diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java index 11ca083..6d93f32 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java @@ -3,6 +3,7 @@ package net.frankheijden.serverutils.bukkit.commands; import cloud.commandframework.CommandManager; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.context.CommandContext; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,7 +19,7 @@ import net.frankheijden.serverutils.bukkit.reflection.RDedicatedServer; import net.frankheijden.serverutils.bukkit.utils.ReloadHandler; import net.frankheijden.serverutils.bukkit.utils.VersionReloadHandler; import net.frankheijden.serverutils.common.commands.CommandServerUtils; -import net.frankheijden.serverutils.common.entities.Result; +import net.frankheijden.serverutils.common.entities.results.PluginResults; import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.ForwardFilter; import net.frankheijden.serverutils.common.utils.ListBuilder; @@ -46,7 +47,7 @@ public class BukkitCommandServerUtils extends CommandServerUtils context) { BukkitCommandSender sender = context.getSender(); - String pluginName = context.get("plugin"); + List plugins = Arrays.asList(context.get("plugins")); - Result result = plugin.getPluginManager().enablePlugin(pluginName); - result.sendTo(sender, "enabl", pluginName); + PluginResults result = plugin.getPluginManager().enablePlugins(plugins); + result.sendTo(sender, "enabl"); } private void handleDisablePlugin(CommandContext context) { BukkitCommandSender sender = context.getSender(); - String pluginName = context.get("plugin"); + List plugins = Arrays.asList(context.get("plugins")); - Result result = plugin.getPluginManager().disablePlugin(pluginName); - result.sendTo(sender, "disabl", pluginName); + PluginResults result = plugin.getPluginManager().disablePlugins(plugins); + result.sendTo(sender, "disabl"); } private void handleReloadConfig(CommandContext context) { @@ -148,12 +149,11 @@ public class BukkitCommandServerUtils extends CommandServerUtils>, String> listBuilderFunction, - String pluginName + Plugin bukkitPlugin ) { - Plugin plugin = Bukkit.getPluginManager().getPlugin(pluginName); - PluginDescriptionFile description = plugin.getDescription(); + PluginDescriptionFile description = bukkitPlugin.getDescription(); - builder.add("Name", plugin.getName()) + builder.add("Name", bukkitPlugin.getName()) .add("Full Name", description.getFullName()) .add("Version", description.getVersion()) .add("Website", description.getWebsite()) diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitLoadResult.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitLoadResult.java deleted file mode 100644 index a538a9f..0000000 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitLoadResult.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.frankheijden.serverutils.bukkit.entities; - -import net.frankheijden.serverutils.common.entities.LoadResult; -import net.frankheijden.serverutils.common.entities.Result; -import org.bukkit.plugin.Plugin; - -public class BukkitLoadResult extends LoadResult { - - private BukkitLoadResult(Plugin obj, Result result) { - super(obj, result); - } - - public BukkitLoadResult(Plugin obj) { - this(obj, Result.SUCCESS); - } - - public BukkitLoadResult(Result result) { - this(null, result); - } -} diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPlugin.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPlugin.java index bcaab17..2c16695 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPlugin.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPlugin.java @@ -1,5 +1,6 @@ package net.frankheijden.serverutils.bukkit.entities; +import cloud.commandframework.bukkit.CloudBukkitCapabilities; import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.paper.PaperCommandManager; import java.io.File; @@ -16,12 +17,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitTask; -public class BukkitPlugin extends ServerUtilsPlugin< - Plugin, - BukkitTask, - BukkitCommandSender, - CommandSender - > { +public class BukkitPlugin extends ServerUtilsPlugin { private final ServerUtils plugin; private final BukkitPluginManager pluginManager; @@ -45,8 +41,9 @@ public class BukkitPlugin extends ServerUtilsPlugin< @Override protected PaperCommandManager newCommandManager() { + PaperCommandManager commandManager; try { - return new PaperCommandManager<>( + commandManager = new PaperCommandManager<>( plugin, CommandExecutionCoordinator.simpleCoordinator(), chatProvider::get, @@ -55,6 +52,17 @@ public class BukkitPlugin extends ServerUtilsPlugin< } catch (Exception ex) { throw new RuntimeException(ex); } + + if (commandManager.queryCapability(CloudBukkitCapabilities.BRIGADIER)) { + commandManager.registerBrigadier(); + handleBrigadier(commandManager.brigadierManager()); + } + + if (commandManager.queryCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { + commandManager.registerAsynchronousCompletions(); + } + + return commandManager; } @Override diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPluginDescription.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPluginDescription.java new file mode 100644 index 0000000..be05c28 --- /dev/null +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/entities/BukkitPluginDescription.java @@ -0,0 +1,59 @@ +package net.frankheijden.serverutils.bukkit.entities; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import org.bukkit.plugin.PluginDescriptionFile; + +public class BukkitPluginDescription implements ServerUtilsPluginDescription { + + private final PluginDescriptionFile descriptionFile; + private final File file; + private final String author; + private final Set dependencies; + + /** + * Constructs a new BukkitPluginDescription. + */ + public BukkitPluginDescription(PluginDescriptionFile descriptionFile, File file) { + this.descriptionFile = descriptionFile; + this.file = file; + this.author = String.join(", ", this.descriptionFile.getAuthors()); + this.dependencies = new HashSet<>(descriptionFile.getDepend()); + } + + @Override + public String getId() { + return this.descriptionFile.getName(); + } + + @Override + public String getName() { + return this.descriptionFile.getName(); + } + + @Override + public String getVersion() { + return this.descriptionFile.getVersion(); + } + + @Override + public String getAuthor() { + return this.author; + } + + @Override + public File getFile() { + return this.file; + } + + @Override + public Set getDependencies() { + return this.dependencies; + } + + public PluginDescriptionFile getDescriptionFile() { + return descriptionFile; + } +} diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java index f9f2cf9..fa47d45 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java @@ -9,10 +9,11 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.frankheijden.serverutils.bukkit.entities.BukkitLoadResult; +import net.frankheijden.serverutils.bukkit.entities.BukkitPluginDescription; import net.frankheijden.serverutils.bukkit.events.BukkitPluginDisableEvent; import net.frankheijden.serverutils.bukkit.events.BukkitPluginEnableEvent; import net.frankheijden.serverutils.bukkit.events.BukkitPluginLoadEvent; @@ -24,8 +25,10 @@ import net.frankheijden.serverutils.bukkit.reflection.RJavaPlugin; import net.frankheijden.serverutils.bukkit.reflection.RJavaPluginLoader; import net.frankheijden.serverutils.bukkit.reflection.RPluginClassLoader; import net.frankheijden.serverutils.bukkit.reflection.RSimplePluginManager; -import net.frankheijden.serverutils.common.entities.CloseableResult; -import net.frankheijden.serverutils.common.entities.Result; +import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; +import net.frankheijden.serverutils.common.entities.results.PluginResults; +import net.frankheijden.serverutils.common.entities.results.Result; +import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; import net.frankheijden.serverutils.common.events.PluginEvent; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import org.bukkit.Bukkit; @@ -35,11 +38,11 @@ import org.bukkit.command.PluginIdentifiableCommand; import org.bukkit.plugin.InvalidDescriptionException; import org.bukkit.plugin.InvalidPluginException; import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginLoader; +import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.UnknownDependencyException; -public class BukkitPluginManager implements AbstractPluginManager { +public class BukkitPluginManager extends AbstractPluginManager { private static BukkitPluginManager instance; @@ -51,122 +54,101 @@ public class BukkitPluginManager implements AbstractPluginManager { return instance; } - /** - * Loads the specified file as a plugin. - * @param jarFile The name of the file in the plugins/ folder. - * @return The result of the loading procedure. - */ @Override - public BukkitLoadResult loadPlugin(String jarFile) { - return loadPlugin(new File(getPluginsFolder(), jarFile)); - } + public PluginResults loadPluginDescriptions(List descriptions) { + PluginResults pluginResults = new PluginResults<>(); - /** - * Loads the specified file as a plugin. - * @param file The file to be loaded. - * @return The result of the loading procedure. - */ - @Override - public BukkitLoadResult loadPlugin(File file) { - if (!file.exists()) return new BukkitLoadResult(Result.NOT_EXISTS); + for (BukkitPluginDescription description : descriptions) { + String pluginId = description.getId(); - Plugin loadedPlugin = getLoadedPlugin(file); - if (loadedPlugin != null) return new BukkitLoadResult(Result.ALREADY_LOADED); - - Plugin plugin; - try { - plugin = Bukkit.getPluginManager().loadPlugin(file); - } catch (InvalidDescriptionException ex) { - return new BukkitLoadResult(Result.INVALID_DESCRIPTION); - } catch (UnknownDependencyException ex) { - return new BukkitLoadResult(Result.UNKNOWN_DEPENDENCY.arg(ex.getMessage())); - } catch (InvalidPluginException ex) { - if (ex.getCause() instanceof IllegalArgumentException) { - IllegalArgumentException e = (IllegalArgumentException) ex.getCause(); - if (e.getMessage().equalsIgnoreCase("Plugin already initialized!")) { - return new BukkitLoadResult(Result.ALREADY_ENABLED); + Plugin plugin; + try { + plugin = Bukkit.getPluginManager().loadPlugin(description.getFile()); + } catch (InvalidDescriptionException ex) { + return pluginResults.addResult(pluginId, Result.INVALID_DESCRIPTION); + } catch (UnknownDependencyException ex) { + return pluginResults.addResult(pluginId, Result.UNKNOWN_DEPENDENCY.arg(ex.getMessage())); + } catch (InvalidPluginException ex) { + if (ex.getCause() instanceof IllegalArgumentException) { + IllegalArgumentException e = (IllegalArgumentException) ex.getCause(); + if (e.getMessage().equalsIgnoreCase("Plugin already initialized!")) { + return pluginResults.addResult(pluginId, Result.ALREADY_ENABLED); + } } + ex.printStackTrace(); + return pluginResults.addResult(pluginId, Result.ERROR); } - ex.printStackTrace(); - return new BukkitLoadResult(Result.ERROR); + + if (plugin == null) return pluginResults.addResult(pluginId, Result.INVALID_PLUGIN); + Bukkit.getPluginManager().callEvent(new BukkitPluginLoadEvent(plugin, PluginEvent.Stage.PRE)); + plugin.onLoad(); + Bukkit.getPluginManager().callEvent(new BukkitPluginLoadEvent(plugin, PluginEvent.Stage.POST)); + pluginResults.addResult(pluginId, plugin); } - if (plugin == null) return new BukkitLoadResult(Result.INVALID_PLUGIN); - Bukkit.getPluginManager().callEvent(new BukkitPluginLoadEvent(plugin, PluginEvent.Stage.PRE)); - plugin.onLoad(); - Bukkit.getPluginManager().callEvent(new BukkitPluginLoadEvent(plugin, PluginEvent.Stage.POST)); - return new BukkitLoadResult(plugin); + return pluginResults; } - /** - * Disables the specified plugin by name and cleans all commands and recipes of the plugin. - * @param pluginName The plugin to disable. - * @return The result of the disable call. - */ - public Result disablePlugin(String pluginName) { - return disablePlugin(Bukkit.getPluginManager().getPlugin(pluginName)); - } - - /** - * Disables the specified plugin and cleans all commands and recipes of the plugin. - * @param plugin The plugin to disable. - * @return The result of the disable call. - */ @Override - public Result disablePlugin(Plugin plugin) { - if (plugin == null) return Result.NOT_ENABLED; - if (!plugin.isEnabled()) return Result.ALREADY_DISABLED; - Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.PRE)); - try { - Bukkit.getPluginManager().disablePlugin(plugin); - RCraftingManager.removeRecipesFor(plugin); - } catch (Exception ex) { - ex.printStackTrace(); - return Result.ERROR; + public PluginResults disableOrderedPlugins(List plugins) { + PluginResults disableResults = new PluginResults<>(); + + for (Plugin plugin : plugins) { + String pluginId = getPluginId(plugin); + if (!isPluginEnabled(pluginId)) return disableResults.addResult(pluginId, Result.ALREADY_DISABLED); + + Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.PRE)); + try { + Bukkit.getPluginManager().disablePlugin(plugin); + RCraftingManager.removeRecipesFor(plugin); + } catch (Exception ex) { + ex.printStackTrace(); + return disableResults.addResult(pluginId, Result.ERROR); + } + + unregisterCommands(plugin); + Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.POST)); + + disableResults.addResult(pluginId, plugin); } - unregisterCommands(plugin); - Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.POST)); - return Result.SUCCESS; + + return disableResults; } - /** - * Unloads the specified plugin by name and cleans all traces within bukkit. - * @param pluginName The plugin to unload. - * @return The result of the unload. - */ @Override - public CloseableResult unloadPlugin(String pluginName) { - return unloadPlugin(Bukkit.getPluginManager().getPlugin(pluginName)); - } + public CloseablePluginResults unloadOrderedPlugins(List plugins) { + CloseablePluginResults unloadResults = new CloseablePluginResults<>(); - /** - * Unloads the specified plugin and cleans all traces within bukkit. - * @param plugin The plugin to unload. - * @return The result of the unload. - */ - @Override - public CloseableResult unloadPlugin(Plugin plugin) { - if (plugin == null) return new CloseableResult(Result.NOT_EXISTS); - Bukkit.getPluginManager().callEvent(new BukkitPluginUnloadEvent(plugin, PluginEvent.Stage.PRE)); - List closeables = new ArrayList<>(); - try { - RSimplePluginManager.getPlugins(Bukkit.getPluginManager()).remove(plugin); - RSimplePluginManager.removeLookupName(Bukkit.getPluginManager(), plugin.getName()); + for (Plugin plugin : plugins) { + String pluginId = getPluginId(plugin); + Bukkit.getPluginManager().callEvent(new BukkitPluginUnloadEvent(plugin, PluginEvent.Stage.PRE)); - ClassLoader loader = RJavaPlugin.getClassLoader(plugin); - RPluginClassLoader.clearClassLoader(loader); - addIfInstance(closeables, (Closeable) () -> { - Map> classes = RPluginClassLoader.getClasses(loader); - RJavaPluginLoader.removeClasses(getPluginLoader(getPluginFile(plugin)), classes.keySet()); - }); - addIfInstance(closeables, loader); - addIfInstance(closeables, RJavaPlugin.clearJavaPlugin(plugin)); - } catch (Exception ex) { - ex.printStackTrace(); - return new CloseableResult(Result.ERROR); + List closeables = new ArrayList<>(); + try { + RSimplePluginManager.getPlugins(Bukkit.getPluginManager()).remove(plugin); + RSimplePluginManager.removeLookupName(Bukkit.getPluginManager(), pluginId); + + ClassLoader loader = RJavaPlugin.getClassLoader(plugin); + RPluginClassLoader.clearClassLoader(loader); + addIfInstance(closeables, (Closeable) () -> { + Map> classes = RPluginClassLoader.getClasses(loader); + getPluginLoader(getPluginFile(plugin)).ifPresent(pluginLoader -> { + RJavaPluginLoader.removeClasses(pluginLoader, classes.keySet()); + }); + }); + addIfInstance(closeables, loader); + addIfInstance(closeables, RJavaPlugin.clearJavaPlugin(plugin)); + } catch (Exception ex) { + ex.printStackTrace(); + return unloadResults.addResult(pluginId, Result.ERROR); + } + + Bukkit.getPluginManager().callEvent(new BukkitPluginUnloadEvent(plugin, PluginEvent.Stage.POST)); + + unloadResults.addResult(pluginId, plugin, closeables); } - Bukkit.getPluginManager().callEvent(new BukkitPluginUnloadEvent(plugin, PluginEvent.Stage.POST)); - return new CloseableResult(closeables); + + return unloadResults; } private static void addIfInstance(List list, Object obj) { @@ -175,68 +157,29 @@ public class BukkitPluginManager implements AbstractPluginManager { } } - /** - * Enables the specified plugin by name. - * @param pluginName The plugin to enable. - * @return The result of the enabling. - */ - public Result enablePlugin(String pluginName) { - return enablePlugin(Bukkit.getPluginManager().getPlugin(pluginName)); - } - - /** - * Enables the specified plugin. - * @param plugin The plugin to enable. - * @return The result of the enabling. - */ @Override - public Result enablePlugin(Plugin plugin) { - if (plugin == null) return Result.NOT_EXISTS; - if (Bukkit.getPluginManager().isPluginEnabled(plugin.getName())) return Result.ALREADY_ENABLED; + protected PluginResults enableOrderedPlugins(List plugins) { + PluginResults enableResults = new PluginResults<>(); + PluginManager bukkitPluginManager = Bukkit.getPluginManager(); + for (Plugin plugin : plugins) { + String pluginId = getPluginId(plugin); + bukkitPluginManager.callEvent(new BukkitPluginEnableEvent(plugin, PluginEvent.Stage.PRE)); + bukkitPluginManager.enablePlugin(plugin); - Bukkit.getPluginManager().callEvent(new BukkitPluginEnableEvent(plugin, PluginEvent.Stage.PRE)); - Bukkit.getPluginManager().enablePlugin(plugin); - RCraftServer.syncCommands(); - - if (!Bukkit.getPluginManager().isPluginEnabled(plugin.getName())) return Result.ERROR; - Bukkit.getPluginManager().callEvent(new BukkitPluginEnableEvent(plugin, PluginEvent.Stage.POST)); - return Result.SUCCESS; - } - - /** - * Reloads the specified plugin by name. - * @param pluginName The plugin to reload. - * @return The result of the reload. - */ - @Override - public Result reloadPlugin(String pluginName) { - Plugin plugin = Bukkit.getPluginManager().getPlugin(pluginName); - if (plugin == null) return Result.NOT_EXISTS; - return reloadPlugin(plugin); - } - - /** - * Reloads the specified plugin. - * @param plugin The plugin to reload. - * @return The result of the reload. - */ - @Override - public Result reloadPlugin(Plugin plugin) { - Result disableResult = disablePlugin(plugin); - if (disableResult != Result.SUCCESS && disableResult != Result.ALREADY_DISABLED) { - return disableResult; + if (!bukkitPluginManager.isPluginEnabled(plugin.getName())) { + return enableResults.addResult(pluginId, Result.ERROR); + } + bukkitPluginManager.callEvent(new BukkitPluginEnableEvent(plugin, PluginEvent.Stage.POST)); + enableResults.addResult(pluginId, plugin); } - CloseableResult result = unloadPlugin(plugin); - if (result.getResult() != Result.SUCCESS) return result.getResult(); - result.tryClose(); + RCraftServer.syncCommands(); + return enableResults; + } - File pluginFile = getPluginFile(plugin.getName()); - if (pluginFile == null) return Result.FILE_DELETED; - - BukkitLoadResult loadResult = loadPlugin(pluginFile); - if (!loadResult.isSuccess()) return loadResult.getResult(); - return enablePlugin(loadResult.get()); + @Override + public boolean isPluginEnabled(String pluginId) { + return Bukkit.getPluginManager().isPluginEnabled(pluginId); } /** @@ -366,45 +309,30 @@ public class BukkitPluginManager implements AbstractPluginManager { * @param file The file. * @return The appropiate PluginLoader. */ - public static PluginLoader getPluginLoader(File file) { + public static Optional getPluginLoader(File file) { Map fileAssociations = getFileAssociations(); - if (fileAssociations == null) return null; - - for (Pattern filter : fileAssociations.keySet()) { - Matcher match = filter.matcher(file.getName()); - if (match.find()) { - return fileAssociations.get(filter); + if (fileAssociations != null) { + for (Map.Entry entry : fileAssociations.entrySet()) { + Matcher match = entry.getKey().matcher(file.getName()); + if (match.find()) { + return Optional.ofNullable(entry.getValue()); + } } } - return null; + return Optional.empty(); } - /** - * Retrieves a loaded plugin associated to a jar file. - * @param file The jar file. - * @return The already loaded plugin, or null if none. - */ - public static Plugin getLoadedPlugin(File file) { - PluginDescriptionFile descriptionFile; + @Override + public Optional getPluginDescription(File file) throws InvalidPluginDescriptionException { + if (!file.exists()) return Optional.empty(); + + Optional loader = getPluginLoader(file); + if (!loader.isPresent()) throw new InvalidPluginDescriptionException("Plugin loader is not present!"); try { - descriptionFile = getPluginDescription(file); + return Optional.of(new BukkitPluginDescription(loader.get().getPluginDescription(file), file)); } catch (InvalidDescriptionException ex) { - return null; + throw new InvalidPluginDescriptionException(ex); } - if (descriptionFile == null) return null; - return Bukkit.getPluginManager().getPlugin(descriptionFile.getName()); - } - - /** - * Retrieves the PluginDescriptionFile of a jar file. - * @param file The jar file. - * @return The PluginDescriptionFile. - * @throws InvalidDescriptionException Iff the PluginDescriptionFile is invalid. - */ - public static PluginDescriptionFile getPluginDescription(File file) throws InvalidDescriptionException { - PluginLoader loader = getPluginLoader(file); - if (loader == null) return null; - return loader.getPluginDescription(file); } @Override @@ -412,29 +340,14 @@ public class BukkitPluginManager implements AbstractPluginManager { return RJavaPlugin.getFile(plugin); } - /** - * Attempts to retrieve the plugin file by plugin name. - * @param pluginName The plugin name. - * @return The file, or null if invalid or not found. - */ @Override - public File getPluginFile(String pluginName) { - for (File file : getPluginJars()) { - PluginDescriptionFile descriptionFile; - try { - descriptionFile = getPluginDescription(file); - } catch (InvalidDescriptionException ex) { - return null; - } - if (descriptionFile == null) return null; - if (descriptionFile.getName().equals(pluginName)) return file; - } - return null; + public Optional getPlugin(String pluginName) { + return Optional.ofNullable(Bukkit.getPluginManager().getPlugin(pluginName)); } @Override - public Plugin getPlugin(String pluginName) { - return Bukkit.getPluginManager().getPlugin(pluginName); + public BukkitPluginDescription getLoadedPluginDescription(Plugin plugin) { + return new BukkitPluginDescription(plugin.getDescription(), getPluginFile(plugin)); } @Override @@ -455,7 +368,7 @@ public class BukkitPluginManager implements AbstractPluginManager { } @Override - public String getPluginName(Plugin plugin) { + public String getPluginId(Plugin plugin) { return plugin.getName(); } } diff --git a/Bukkit/src/main/resources/bukkit-commands.json b/Bukkit/src/main/resources/bukkit-commands.json index 6f7500c..191af7c 100644 --- a/Bukkit/src/main/resources/bukkit-commands.json +++ b/Bukkit/src/main/resources/bukkit-commands.json @@ -8,7 +8,7 @@ "ep" ], "permission": "serverutils.enableplugin", - "description": "Enables the specified plugin.", + "description": "Enables the specified plugin(s).", "display-in-help": true }, "disableplugin": { @@ -17,7 +17,7 @@ "dp" ], "permission": "serverutils.disableplugin", - "description": "Disables the specified plugin.", + "description": "Disables the specified plugin(s).", "display-in-help": true }, "reloadconfig": { diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/BungeeCommandServerUtils.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/BungeeCommandServerUtils.java index b845eb8..bf7d1fc 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/BungeeCommandServerUtils.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/commands/BungeeCommandServerUtils.java @@ -19,17 +19,16 @@ import net.md_5.bungee.api.plugin.PluginManager; public class BungeeCommandServerUtils extends CommandServerUtils { public BungeeCommandServerUtils(BungeePlugin plugin) { - super(plugin); + super(plugin, Plugin[]::new); } @Override protected FormatBuilder createPluginInfo( FormatBuilder builder, Function>, String> listBuilderFunction, - String pluginName + Plugin bungeePlugin ) { - Plugin container = plugin.getPluginManager().getPlugin(pluginName); - PluginDescription desc = container.getDescription(); + PluginDescription desc = bungeePlugin.getDescription(); return builder .add("Name", desc.getName()) diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeeLoadResult.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeeLoadResult.java deleted file mode 100644 index 19cb542..0000000 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeeLoadResult.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.frankheijden.serverutils.bungee.entities; - -import net.frankheijden.serverutils.common.entities.LoadResult; -import net.frankheijden.serverutils.common.entities.Result; -import net.md_5.bungee.api.plugin.Plugin; - -public class BungeeLoadResult extends LoadResult { - - public BungeeLoadResult(Plugin obj, Result result) { - super(obj, result); - } - - public BungeeLoadResult(Plugin obj) { - super(obj); - } - - public BungeeLoadResult(Result result) { - super(result); - } -} diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePlugin.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePlugin.java index 1ca0487..8ca481f 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePlugin.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePlugin.java @@ -15,12 +15,7 @@ import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.scheduler.ScheduledTask; -public class BungeePlugin extends ServerUtilsPlugin< - Plugin, - ScheduledTask, - BungeeCommandSender, - CommandSender - > { +public class BungeePlugin extends ServerUtilsPlugin { private final ServerUtils plugin; private final BungeePluginManager pluginManager; diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePluginDescription.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePluginDescription.java new file mode 100644 index 0000000..051bf7a --- /dev/null +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/entities/BungeePluginDescription.java @@ -0,0 +1,57 @@ +package net.frankheijden.serverutils.bungee.entities; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import net.md_5.bungee.api.plugin.PluginDescription; + +public class BungeePluginDescription implements ServerUtilsPluginDescription { + + private final PluginDescription description; + private final File file; + private final Set dependencies; + + /** + * Constructs a new BungeePluginDescription. + */ + public BungeePluginDescription(PluginDescription description) { + this.description = description; + this.file = description.getFile(); + this.dependencies = new HashSet<>(description.getDepends()); + } + + @Override + public String getId() { + return this.description.getName(); + } + + @Override + public String getName() { + return this.description.getName(); + } + + @Override + public String getVersion() { + return this.description.getVersion(); + } + + @Override + public String getAuthor() { + return this.description.getAuthor(); + } + + @Override + public File getFile() { + return this.file; + } + + @Override + public Set getDependencies() { + return this.dependencies; + } + + public PluginDescription getDescription() { + return description; + } +} diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java index 3c4ae52..8240623 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java @@ -1,8 +1,8 @@ package net.frankheijden.serverutils.bungee.managers; -import com.google.common.base.Preconditions; import java.io.Closeable; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.net.URLClassLoader; import java.util.ArrayList; @@ -10,12 +10,13 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Level; import java.util.stream.Collectors; -import net.frankheijden.serverutils.bungee.entities.BungeeLoadResult; +import net.frankheijden.serverutils.bungee.entities.BungeePluginDescription; import net.frankheijden.serverutils.bungee.events.BungeePluginDisableEvent; import net.frankheijden.serverutils.bungee.events.BungeePluginEnableEvent; import net.frankheijden.serverutils.bungee.events.BungeePluginLoadEvent; @@ -23,8 +24,11 @@ import net.frankheijden.serverutils.bungee.events.BungeePluginUnloadEvent; import net.frankheijden.serverutils.bungee.reflection.RLibraryLoader; import net.frankheijden.serverutils.bungee.reflection.RPluginClassLoader; import net.frankheijden.serverutils.bungee.reflection.RPluginManager; -import net.frankheijden.serverutils.common.entities.CloseableResult; -import net.frankheijden.serverutils.common.entities.Result; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; +import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; +import net.frankheijden.serverutils.common.entities.results.PluginResults; +import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.events.PluginEvent; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import net.md_5.bungee.api.ProxyServer; @@ -32,7 +36,7 @@ import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.PluginDescription; import org.yaml.snakeyaml.Yaml; -public class BungeePluginManager implements AbstractPluginManager { +public class BungeePluginManager extends AbstractPluginManager { private static final ProxyServer proxy = ProxyServer.getInstance(); @@ -65,128 +69,123 @@ public class BungeePluginManager implements AbstractPluginManager { } @Override - public BungeeLoadResult loadPlugin(String pluginFile) { - File file = getPluginFileExact(pluginFile); - if (!file.exists()) return new BungeeLoadResult(Result.NOT_EXISTS); - return loadPlugin(file); - } + public PluginResults loadPluginDescriptions(List descriptions) { + PluginResults loadResults = new PluginResults<>(); - @Override - public BungeeLoadResult loadPlugin(File file) { - PluginDescription desc; - try { - desc = getPluginDescription(file); - } catch (Exception ex) { - proxy.getLogger().log(Level.WARNING, "Could not load plugin from file " + file, ex); - return new BungeeLoadResult(Result.INVALID_DESCRIPTION); - } + for (BungeePluginDescription description : descriptions) { + PluginDescription desc = description.getDescription(); + Plugin plugin; - try { - Object libraryLoader = RPluginManager.getLibraryLoader(proxy.getPluginManager()); - ClassLoader classLoader = RLibraryLoader.createLoader(libraryLoader, desc); - URLClassLoader loader = (URLClassLoader) RPluginClassLoader.newInstance( - proxy, - desc, - desc.getFile(), - classLoader - ); + try { + Object libraryLoader = RPluginManager.getLibraryLoader(proxy.getPluginManager()); + ClassLoader classLoader = RLibraryLoader.createLoader(libraryLoader, desc); + URLClassLoader loader = (URLClassLoader) RPluginClassLoader.newInstance( + proxy, + desc, + desc.getFile(), + classLoader + ); - Class main = loader.loadClass(desc.getMain()); - Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance(); + Class main = loader.loadClass(desc.getMain()); + plugin = (Plugin) main.getDeclaredConstructor().newInstance(); + + RPluginManager.getPlugins(proxy.getPluginManager()).put(description.getId(), plugin); + proxy.getPluginManager().callEvent(new BungeePluginLoadEvent(plugin, PluginEvent.Stage.PRE)); + plugin.onLoad(); + } catch (Throwable th) { + proxy.getLogger().log(Level.WARNING, "Error loading plugin " + description.getId(), th); + return loadResults.addResult(description.getId(), Result.ERROR); + } - RPluginManager.getPlugins(proxy.getPluginManager()).put(desc.getName(), plugin); - proxy.getPluginManager().callEvent(new BungeePluginLoadEvent(plugin, PluginEvent.Stage.PRE)); - plugin.onLoad(); proxy.getLogger().log(Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[] { desc.getName(), desc.getVersion(), desc.getAuthor() }); proxy.getPluginManager().callEvent(new BungeePluginLoadEvent(plugin, PluginEvent.Stage.POST)); - return new BungeeLoadResult(plugin); - } catch (Throwable th) { - proxy.getLogger().log(Level.WARNING, "Error loading plugin " + desc.getName(), th); - return new BungeeLoadResult(Result.ERROR); + loadResults.addResult(description.getId(), plugin); } + + return loadResults; } @Override - public Result enablePlugin(Plugin plugin) { - PluginDescription desc = plugin.getDescription(); - String name = desc.getName(); - proxy.getPluginManager().callEvent(new BungeePluginEnableEvent(plugin, PluginEvent.Stage.PRE)); - try { - plugin.onEnable(); - Object[] args = new Object[] { name, desc.getVersion(), desc.getAuthor() }; + public PluginResults enableOrderedPlugins(List plugins) { + PluginResults enableResults = new PluginResults<>(); + + for (Plugin plugin : plugins) { + ServerUtilsPluginDescription description = getLoadedPluginDescription(plugin); + String pluginId = description.getId(); + + proxy.getPluginManager().callEvent(new BungeePluginEnableEvent(plugin, PluginEvent.Stage.PRE)); + try { + plugin.onEnable(); + } catch (Throwable th) { + proxy.getLogger().log(Level.WARNING, "Exception encountered when loading plugin: " + pluginId, th); + return enableResults.addResult(pluginId, Result.ERROR); + } + + Object[] args = new Object[] { description.getId(), description.getVersion(), description.getAuthor() }; proxy.getLogger().log(Level.INFO, "Enabled plugin {0} version {1} by {2}", args); proxy.getPluginManager().callEvent(new BungeePluginEnableEvent(plugin, PluginEvent.Stage.POST)); - return Result.SUCCESS; - } catch (Throwable th) { - proxy.getLogger().log(Level.WARNING, "Exception encountered when loading plugin: " + name, th); - return Result.ERROR; + enableResults.addResult(pluginId, plugin); } + + return enableResults; } @Override - public Result disablePlugin(Plugin plugin) { - PluginDescription desc = plugin.getDescription(); - String name = desc.getName(); - proxy.getPluginManager().callEvent(new BungeePluginDisableEvent(plugin, PluginEvent.Stage.PRE)); - try { - plugin.onDisable(); + public boolean isPluginEnabled(String pluginId) { + return proxy.getPluginManager().getPlugin(pluginId) != null; + } + + @Override + public PluginResults disableOrderedPlugins(List plugins) { + PluginResults disableResults = new PluginResults<>(); + + for (Plugin plugin : plugins) { + String pluginId = getPluginId(plugin); + proxy.getPluginManager().callEvent(new BungeePluginDisableEvent(plugin, PluginEvent.Stage.PRE)); + try { + plugin.onDisable(); + } catch (Throwable th) { + proxy.getLogger().log(Level.WARNING, "Exception encountered when disabling plugin: " + pluginId, th); + return disableResults.addResult(pluginId, Result.ERROR); + } + proxy.getPluginManager().callEvent(new BungeePluginDisableEvent(plugin, PluginEvent.Stage.POST)); - return Result.SUCCESS; - } catch (Throwable th) { - proxy.getLogger().log(Level.WARNING, "Exception encountered when disabling plugin: " + name, th); - return Result.ERROR; + disableResults.addResult(pluginId, plugin); } + + return disableResults; } @Override - public Result reloadPlugin(String pluginName) { - Plugin plugin = proxy.getPluginManager().getPlugin(pluginName); - if (plugin == null) return Result.NOT_ENABLED; - return reloadPlugin(plugin); - } + public CloseablePluginResults unloadOrderedPlugins(List plugins) { + CloseablePluginResults unloadResults = new CloseablePluginResults<>(); - @Override - public Result reloadPlugin(Plugin plugin) { - CloseableResult result = unloadPlugin(plugin); - if (result.getResult() != Result.SUCCESS) return result.getResult(); - result.tryClose(); + for (Plugin plugin : plugins) { + String pluginId = getPluginId(plugin); - File file = getPluginFile(plugin.getDescription().getName()); - if (file == null) return Result.FILE_DELETED; + proxy.getPluginManager().callEvent(new BungeePluginUnloadEvent(plugin, PluginEvent.Stage.PRE)); + plugin.onDisable(); + proxy.getPluginManager().unregisterCommands(plugin); + proxy.getPluginManager().unregisterListeners(plugin); + proxy.getScheduler().cancel(plugin); - BungeeLoadResult loadResult = loadPlugin(file); - if (!loadResult.isSuccess()) return loadResult.getResult(); + List closeables = new ArrayList<>(); + try { + RPluginManager.clearPlugin(proxy.getPluginManager(), plugin); + addIfInstance(closeables, RPluginClassLoader.getPluginClassLoader(plugin)); + addIfInstance(closeables, plugin.getClass().getClassLoader()); + } catch (Exception ex) { + ex.printStackTrace(); + return unloadResults.addResult(pluginId, Result.ERROR); + } - return enablePlugin(loadResult.get()); - } - - @Override - public CloseableResult unloadPlugin(String pluginName) { - Plugin plugin = proxy.getPluginManager().getPlugin(pluginName); - if (plugin == null) return new CloseableResult(Result.NOT_ENABLED); - return unloadPlugin(plugin); - } - - @Override - public CloseableResult unloadPlugin(Plugin plugin) { - proxy.getPluginManager().callEvent(new BungeePluginUnloadEvent(plugin, PluginEvent.Stage.PRE)); - plugin.onDisable(); - proxy.getPluginManager().unregisterCommands(plugin); - proxy.getPluginManager().unregisterListeners(plugin); - proxy.getScheduler().cancel(plugin); - List closeables = new ArrayList<>(); - try { - RPluginManager.clearPlugin(proxy.getPluginManager(), plugin); - addIfInstance(closeables, RPluginClassLoader.getPluginClassLoader(plugin)); - addIfInstance(closeables, plugin.getClass().getClassLoader()); - } catch (Exception ex) { - ex.printStackTrace(); - return new CloseableResult(Result.ERROR); + proxy.getPluginManager().callEvent(new BungeePluginUnloadEvent(plugin, PluginEvent.Stage.POST)); + unloadResults.addResult(pluginId, plugin, closeables); } - proxy.getPluginManager().callEvent(new BungeePluginUnloadEvent(plugin, PluginEvent.Stage.POST)); - return new CloseableResult(closeables); + + return unloadResults; } private static void addIfInstance(List list, Object obj) { @@ -195,38 +194,31 @@ public class BungeePluginManager implements AbstractPluginManager { } } - public static File getPluginFileExact(String fileName) { - return new File(proxy.getPluginsFolder(), fileName); - } - - /** - * Retrieves the File of a plugin associated with a name. - * @param pluginName The plugin name to search for. - * @return The File if the plugin exists with that name. - */ @Override - public File getPluginFile(String pluginName) { + public Optional getPluginFile(String pluginId) { for (File file : getPluginJars()) { - PluginDescription desc; + BungeePluginDescription description; try { - desc = getPluginDescription(file); + Optional descriptionOptional = getPluginDescription(file); + if (!descriptionOptional.isPresent()) continue; + description = descriptionOptional.get(); } catch (Exception ex) { continue; } - if (desc.getName().equals(pluginName)) return file; + if (description.getId().equals(pluginId)) return Optional.of(file); } - return null; + return Optional.empty(); } @Override - public File getPluginFile(Plugin plugin) { - return plugin.getFile(); + public Optional getPlugin(String pluginName) { + return Optional.ofNullable(proxy.getPluginManager().getPlugin(pluginName)); } @Override - public Plugin getPlugin(String pluginName) { - return proxy.getPluginManager().getPlugin(pluginName); + public BungeePluginDescription getLoadedPluginDescription(Plugin plugin) { + return new BungeePluginDescription(plugin.getDescription()); } @Override @@ -241,40 +233,35 @@ public class BungeePluginManager implements AbstractPluginManager { .collect(Collectors.toSet()); } - /** - * Retrieves the PluginDescription of a (plugin's) File. - * @param file The file. - * @return The PluginDescription. - * @throws Exception Iff and I/O exception occurred, or notNullChecks failed. - */ - public static PluginDescription getPluginDescription(File file) throws Exception { + @Override + public Optional getPluginDescription(File file) throws InvalidPluginDescriptionException { try (JarFile jar = new JarFile(file)) { - JarEntry entry = getPluginDescriptionEntry(jar); - Preconditions.checkNotNull(entry, "Plugin must have a plugin.yml or bungee.yml"); + JarEntry entry = jar.getJarEntry("bungee.yml"); + if (entry == null) { + entry = jar.getJarEntry("plugin.yml"); + } + + if (entry == null) { + throw new InvalidPluginDescriptionException("Plugin must have a plugin.yml or bungee.yml"); + } try (InputStream in = jar.getInputStream(entry)) { Yaml yaml = RPluginManager.getYaml(proxy.getPluginManager()); - PluginDescription desc = yaml.loadAs(in, PluginDescription.class); - Preconditions.checkNotNull(desc.getName(), "Plugin from %s has no name", file); - Preconditions.checkNotNull(desc.getMain(), "Plugin from %s has no main", file); + PluginDescription description = yaml.loadAs(in, PluginDescription.class); + if (description.getName() == null) { + throw new InvalidPluginDescriptionException("Plugin from " + file + " has no name"); + } else if (description.getMain() == null) { + throw new InvalidPluginDescriptionException("Plugin from " + file + " has no main"); + } - desc.setFile(file); - return desc; + description.setFile(file); + return Optional.of(new BungeePluginDescription(description)); } + } catch (IOException ex) { + throw new InvalidPluginDescriptionException(ex); } } - /** - * Retrieves the JarEntry which contains the Description file of the JarFile. - * @param jar The JarFile. - * @return The description JarEntry. - */ - public static JarEntry getPluginDescriptionEntry(JarFile jar) { - JarEntry entry = jar.getJarEntry("bungee.yml"); - if (entry == null) return jar.getJarEntry("plugin.yml"); - return entry; - } - @Override public File getPluginsFolder() { return proxy.getPluginsFolder(); @@ -298,11 +285,6 @@ public class BungeePluginManager implements AbstractPluginManager { .collect(Collectors.toList()); } - @Override - public String getPluginName(Plugin plugin) { - return plugin.getDataFolder().getName(); - } - /** * Retrieves the plugins sorted by their names. * @param modules Whether or not to include `module` plugins @@ -310,7 +292,7 @@ public class BungeePluginManager implements AbstractPluginManager { */ public List getPluginsSorted(boolean modules) { List plugins = getPlugins(modules); - plugins.sort(Comparator.comparing(this::getPluginName)); + plugins.sort(Comparator.comparing(this::getPluginId)); return plugins; } } diff --git a/Common/build.gradle b/Common/build.gradle index a176d13..1b3c12b 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -3,7 +3,6 @@ plugins { } group = rootProject.group + '.common' -String dependencyDir = group + '.dependencies' version = rootProject.version archivesBaseName = rootProject.name + '-Common' @@ -12,7 +11,6 @@ repositories { } dependencies { - implementation 'com.google.code.gson:gson:2.8.6' compileOnly 'com.github.FrankHeijden:ServerUtilsUpdater:v1.0.0' } @@ -25,5 +23,4 @@ shadowJar { exclude 'net/frankheijden/serverutilsupdater/**' exclude 'plugin.yml' exclude 'bungee.yml' - relocate 'com.google.gson', dependencyDir + '.gson' } \ No newline at end of file diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/ServerUtilsApp.java b/Common/src/main/java/net/frankheijden/serverutils/common/ServerUtilsApp.java index 745d977..a3ae81e 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/ServerUtilsApp.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/ServerUtilsApp.java @@ -3,13 +3,15 @@ package net.frankheijden.serverutils.common; import java.io.File; import java.io.IOException; import java.nio.file.Files; -import net.frankheijden.serverutils.common.entities.CloseableResult; -import net.frankheijden.serverutils.common.entities.Result; +import java.util.Optional; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import net.frankheijden.serverutils.common.entities.results.CloseablePluginResult; +import net.frankheijden.serverutils.common.entities.results.PluginResult; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; import net.frankheijden.serverutils.common.tasks.UpdateCheckerTask; -public class ServerUtilsApp, P, T, C extends ServerCommandSender, S> { +public class ServerUtilsApp, P, T, C extends ServerCommandSender, S, D extends ServerUtilsPluginDescription> { public static final int BSTATS_METRICS_ID = 7790; public static final String VERSION = "{version}"; @@ -25,7 +27,14 @@ public class ServerUtilsApp, P, T, C ext this.plugin = plugin; } - public static , P, T, C extends ServerCommandSender, S> void init( + public static < + U extends ServerUtilsPlugin, + P, + T, + C extends ServerCommandSender, + S, + D extends ServerUtilsPluginDescription + > void init( Object platformPlugin, U plugin ) { @@ -42,30 +51,31 @@ public class ServerUtilsApp, P, T, C ext /** * Unloads the ServerUtilsUpdater and deletes the file. */ - public static , P, T, C extends ServerCommandSender, S> - void unloadServerUtilsUpdater() { - U plugin = getPlugin(); + public static

void unloadServerUtilsUpdater() { + ServerUtilsPlugin plugin = getPlugin(); plugin.getTaskManager().runTaskLater(() -> { String updaterName = plugin.getPlatform() == ServerUtilsPlugin.Platform.VELOCITY ? "serverutilsupdater" : "ServerUtilsUpdater"; - P updaterPlugin = plugin.getPluginManager().getPlugin(updaterName); - if (updaterPlugin == null) return; + Optional

updaterPluginOptional = plugin.getPluginManager().getPlugin(updaterName); + if (!updaterPluginOptional.isPresent()) return; + P updaterPlugin = updaterPluginOptional.get(); @SuppressWarnings("VariableDeclarationUsageDistance") File file = plugin.getPluginManager().getPluginFile(updaterPlugin); - Result result = plugin.getPluginManager().disablePlugin(updaterPlugin); - if (result != Result.SUCCESS) { - result.sendTo(plugin.getChatProvider().getConsoleSender(), "disabl", updaterName); + PluginResult

disableResult = plugin.getPluginManager().disablePlugin(updaterPlugin); + if (!disableResult.isSuccess()) { + disableResult.getResult().sendTo(plugin.getChatProvider().getConsoleSender(), "disabl", updaterName); return; } - CloseableResult closeableResult = plugin.getPluginManager().unloadPlugin(updaterName); - if (closeableResult.getResult() != Result.SUCCESS) { - closeableResult.getResult().sendTo(plugin.getChatProvider().getConsoleSender(), "unload", updaterName); + CloseablePluginResult

unloadResult = plugin.getPluginManager().unloadPlugin(disableResult.getPlugin()); + if (!unloadResult.isSuccess()) { + unloadResult.getResult().sendTo(plugin.getChatProvider().getConsoleSender(), "unload", updaterName); + return; } - closeableResult.tryClose(); + unloadResult.tryClose(); if (Files.exists(file.toPath())) { try { @@ -82,8 +92,14 @@ public class ServerUtilsApp, P, T, C ext } @SuppressWarnings("unchecked") - public static , P, T, C extends ServerCommandSender, S> - U getPlugin() { + public static < + U extends ServerUtilsPlugin, + P, + T, + C extends ServerCommandSender, + S, + D extends ServerUtilsPluginDescription + > U getPlugin() { return (U) instance.plugin; } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandPlugins.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandPlugins.java index a614aa4..50c099f 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandPlugins.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandPlugins.java @@ -7,7 +7,7 @@ import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; import net.frankheijden.serverutils.common.utils.ListBuilder; import net.frankheijden.serverutils.common.utils.ListFormat; -public abstract class CommandPlugins, P, C extends ServerCommandSender> +public abstract class CommandPlugins, P, C extends ServerCommandSender> extends ServerUtilsCommand { protected CommandPlugins(U plugin) { diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java index 484b899..c7c3684 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java @@ -4,14 +4,22 @@ import cloud.commandframework.Command; import cloud.commandframework.CommandManager; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.context.CommandContext; +import java.io.File; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.IntFunction; +import net.frankheijden.serverutils.common.commands.arguments.JarFilesArgument; +import net.frankheijden.serverutils.common.commands.arguments.PluginArgument; +import net.frankheijden.serverutils.common.commands.arguments.PluginsArgument; import net.frankheijden.serverutils.common.config.ServerUtilsConfig; -import net.frankheijden.serverutils.common.entities.AbstractResult; -import net.frankheijden.serverutils.common.entities.CloseableResult; -import net.frankheijden.serverutils.common.entities.LoadResult; -import net.frankheijden.serverutils.common.entities.Result; +import net.frankheijden.serverutils.common.entities.results.AbstractResult; +import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; +import net.frankheijden.serverutils.common.entities.results.PluginResult; +import net.frankheijden.serverutils.common.entities.results.PluginResults; +import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; @@ -19,23 +27,21 @@ import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.ListBuilder; import net.frankheijden.serverutils.common.utils.ListFormat; -public abstract class CommandServerUtils, P, C extends ServerCommandSender> +public abstract class CommandServerUtils, P, C extends ServerCommandSender> extends ServerUtilsCommand { - protected CommandServerUtils(U plugin) { + private final IntFunction arrayCreator; + + protected CommandServerUtils(U plugin, IntFunction arrayCreator) { super(plugin, "serverutils"); + this.arrayCreator = arrayCreator; } @Override public void register(CommandManager manager, Command.Builder builder) { - addArgument(CommandArgument.ofType(String.class, "jarFile") - .manager(manager) - .withSuggestionsProvider((context, s) -> plugin.getPluginManager().getPluginFileNames()) - .build()); - addArgument(CommandArgument.ofType(String.class, "plugin") - .manager(manager) - .withSuggestionsProvider((context, s) -> plugin.getPluginManager().getPluginNames()) - .build()); + addArgument(new JarFilesArgument<>(true, "jarFiles", plugin)); + addArgument(new PluginsArgument<>(true, "plugins", plugin, arrayCreator)); + addArgument(new PluginArgument<>(true, "plugin", plugin)); addArgument(CommandArgument.ofType(String.class, "command") .manager(manager) .withSuggestionsProvider((context, s) -> new ArrayList<>(plugin.getPluginManager().getCommands())) @@ -48,13 +54,13 @@ public abstract class CommandServerUtils manager.command(buildSubcommand(builder, "reload") .handler(this::handleReload)); manager.command(buildSubcommand(builder, "loadplugin") - .argument(getArgument("jarFile")) + .argument(getArgument("jarFiles")) .handler(this::handleLoadPlugin)); manager.command(buildSubcommand(builder, "unloadplugin") - .argument(getArgument("plugin")) + .argument(getArgument("plugins")) .handler(this::handleUnloadPlugin)); manager.command(buildSubcommand(builder, "reloadplugin") - .argument(getArgument("plugin")) + .argument(getArgument("plugins")) .handler(this::handleReloadPlugin)); manager.command(buildSubcommand(builder, "watchplugin") .argument(getArgument("plugin")) @@ -146,69 +152,78 @@ public abstract class CommandServerUtils private void handleLoadPlugin(CommandContext context) { C sender = context.getSender(); - String jarFile = context.get("jarFile"); + List jarFiles = Arrays.asList(context.get("jarFiles")); - AbstractPluginManager

pluginManager = plugin.getPluginManager(); - LoadResult

loadResult = pluginManager.loadPlugin(jarFile); - if (!loadResult.isSuccess()) { - loadResult.getResult().sendTo(sender, "load", jarFile); + AbstractPluginManager pluginManager = plugin.getPluginManager(); + PluginResults

loadResults = pluginManager.loadPlugins(jarFiles); + if (!loadResults.isSuccess()) { + PluginResult

failedResult = loadResults.last(); + failedResult.getResult().sendTo(sender, "load", failedResult.getPluginId()); return; } - P loadedPlugin = loadResult.get(); - Result result = pluginManager.enablePlugin(loadedPlugin); - result.sendTo(sender, "load", pluginManager.getPluginName(loadedPlugin)); + PluginResults

enableResults = pluginManager.enablePlugins(loadResults.getPlugins()); + enableResults.sendTo(sender, "load"); } private void handleUnloadPlugin(CommandContext context) { C sender = context.getSender(); - String pluginName = context.get("plugin"); + List

plugins = Arrays.asList(context.get("plugins")); - CloseableResult result = plugin.getPluginManager().unloadPlugin(pluginName); - result.getResult().sendTo(sender, "unload", pluginName); - result.tryClose(); + PluginResults

disableResults = plugin.getPluginManager().disablePlugins(plugins); + for (PluginResult

disableResult : disableResults.getResults()) { + if (!disableResult.isSuccess() && disableResult.getResult() != Result.ALREADY_DISABLED) { + disableResult.getResult().sendTo(sender, "disabl", disableResult.getPluginId()); + return; + } + } + + CloseablePluginResults

unloadResults = plugin.getPluginManager().unloadPlugins(plugins); + unloadResults.tryClose(); + unloadResults.sendTo(sender, "unload"); } private void handleReloadPlugin(CommandContext context) { C sender = context.getSender(); - String pluginName = context.get("plugin"); + List

plugins = Arrays.asList(context.get("plugins")); - Result result = plugin.getPluginManager().reloadPlugin(pluginName); - result.sendTo(sender, "reload", pluginName); + PluginResults

reloadResult = plugin.getPluginManager().reloadPlugins(plugins); + reloadResult.sendTo(sender, "reload"); } private void handleWatchPlugin(CommandContext context) { C sender = context.getSender(); - String pluginName = context.get("plugin"); + P pluginArg = context.get("plugin"); - AbstractResult result = plugin.getPluginManager().watchPlugin(sender, pluginName); - result.sendTo(sender, "watch", pluginName); + AbstractPluginManager pluginManager = plugin.getPluginManager(); + String pluginId = pluginManager.getPluginId(pluginArg); + + AbstractResult result = pluginManager.watchPlugin(sender, pluginId); + result.sendTo(sender, "watch", pluginId); } private void handleUnwatchPlugin(CommandContext context) { C sender = context.getSender(); - String pluginName = context.get("plugin"); + P pluginArg = context.get("plugin"); - AbstractResult result = plugin.getPluginManager().unwatchPlugin(pluginName); - result.sendTo(sender, "unwatch", pluginName); + AbstractPluginManager pluginManager = plugin.getPluginManager(); + String pluginId = pluginManager.getPluginId(pluginArg); + + AbstractResult result = pluginManager.unwatchPlugin(pluginId); + result.sendTo(sender, "unwatch", pluginId); } private void handlePluginInfo(CommandContext context) { C sender = context.getSender(); - String pluginName = context.get("plugin"); + P pluginArg = context.get("plugin"); - if (this.plugin.getPluginManager().getPlugin(pluginName) == null) { - Result.NOT_EXISTS.sendTo(sender, "fetch", pluginName); - return; - } - - createInfo(sender, "plugininfo", pluginName, this::createPluginInfo); + createInfo(sender, "plugininfo", pluginArg, this::createPluginInfo); } protected abstract FormatBuilder createPluginInfo( FormatBuilder builder, Function>, String> listBuilderFunction, - String pluginName + P pluginArg ); private void handleCommandInfo(CommandContext context) { @@ -229,7 +244,7 @@ public abstract class CommandServerUtils String commandName ); - private void createInfo(C sender, String command, String item, InfoCreator creator) { + private void createInfo(C sender, String command, T item, InfoCreator creator) { String messagePrefix = "serverutils." + command; String format = plugin.getMessagesResource().getMessage(messagePrefix + ".format"); String listFormatString = plugin.getMessagesResource().getMessage(messagePrefix + ".list_format"); @@ -256,12 +271,12 @@ public abstract class CommandServerUtils plugin.getMessagesResource().sendMessage(sender, messagePrefix + ".footer"); } - private interface InfoCreator { + private interface InfoCreator { FormatBuilder createInfo( FormatBuilder builder, Function>, String> listBuilderFunction, - String item + T item ); } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java index dc6d4fe..d6c9775 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java @@ -13,7 +13,7 @@ import net.frankheijden.serverutils.common.config.ServerUtilsConfig; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; -public abstract class ServerUtilsCommand, C extends ServerCommandSender> { +public abstract class ServerUtilsCommand, C extends ServerCommandSender> { protected final U plugin; protected final String commandName; diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/JarFilesArgument.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/JarFilesArgument.java new file mode 100644 index 0000000..252566d --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/JarFilesArgument.java @@ -0,0 +1,72 @@ +package net.frankheijden.serverutils.common.commands.arguments; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.NoInputProvidedException; +import io.leangen.geantyref.TypeToken; +import java.io.File; +import java.util.HashSet; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; +import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; + +public class JarFilesArgument> extends CommandArgument { + + /** + * Constructs a Jar Files argument. + */ + public JarFilesArgument(boolean required, String name, ServerUtilsPlugin plugin) { + super( + required, + name, + new JarFilesParser<>(plugin), + "", + new TypeToken() {}, + null + ); + } + + public static final class JarFilesParser> implements ArgumentParser { + + private final ServerUtilsPlugin plugin; + + public JarFilesParser(ServerUtilsPlugin plugin) { + this.plugin = plugin; + } + + @Override + public ArgumentParseResult parse(CommandContext context, Queue inputQueue) { + if (inputQueue.isEmpty()) { + return ArgumentParseResult.failure(new NoInputProvidedException(JarFilesParser.class, context)); + } + + Set pluginFiles = new HashSet<>(plugin.getPluginManager().getPluginFileNames()); + File[] files = new File[inputQueue.size()]; + for (int i = 0; i < files.length; i++) { + if (!pluginFiles.contains(inputQueue.peek())) { + return ArgumentParseResult.failure(new IllegalArgumentException( + "Plugin '" + inputQueue.peek() + "' does not exist!" + )); + } + + files[i] = new File(plugin.getPluginManager().getPluginsFolder(), inputQueue.remove()); + } + + return ArgumentParseResult.success(files); + } + + @Override + public List suggestions(CommandContext context, String input) { + return plugin.getPluginManager().getPluginFileNames(); + } + + @Override + public boolean isContextFree() { + return true; + } + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginArgument.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginArgument.java new file mode 100644 index 0000000..8d21ec9 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginArgument.java @@ -0,0 +1,66 @@ +package net.frankheijden.serverutils.common.commands.arguments; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.NoInputProvidedException; +import io.leangen.geantyref.TypeToken; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; +import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; + +public class PluginArgument, P> extends CommandArgument { + + /** + * Constructs a Plugin argument. + */ + public PluginArgument(boolean required, String name, ServerUtilsPlugin plugin) { + super( + required, + name, + new PluginParser<>(plugin), + "", + new TypeToken

() {}, + null + ); + } + + public static final class PluginParser, P> implements ArgumentParser { + + private final ServerUtilsPlugin plugin; + + public PluginParser(ServerUtilsPlugin plugin) { + this.plugin = plugin; + } + + @Override + public ArgumentParseResult

parse(CommandContext context, Queue inputQueue) { + if (inputQueue.isEmpty()) { + return ArgumentParseResult.failure(new NoInputProvidedException(PluginParser.class, context)); + } + + Optional

pluginOptional = plugin.getPluginManager().getPlugin(inputQueue.peek()); + if (!pluginOptional.isPresent()) { + return ArgumentParseResult.failure(new IllegalArgumentException( + "Plugin '" + inputQueue.peek() + "' does not exist!" + )); + } + + inputQueue.remove(); + return ArgumentParseResult.success(pluginOptional.get()); + } + + @Override + public List suggestions(CommandContext context, String input) { + return plugin.getPluginManager().getPluginNames(); + } + + @Override + public boolean isContextFree() { + return true; + } + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java new file mode 100644 index 0000000..bcb765c --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java @@ -0,0 +1,79 @@ +package net.frankheijden.serverutils.common.commands.arguments; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.NoInputProvidedException; +import io.leangen.geantyref.TypeToken; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import java.util.function.IntFunction; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; +import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; + +public class PluginsArgument, P> extends CommandArgument { + + /** + * Constructs a Plugins argument. + */ + public PluginsArgument( + boolean required, + String name, + ServerUtilsPlugin plugin, + IntFunction arrayCreator + ) { + super( + required, + name, + new PluginsParser<>(plugin, arrayCreator), + "", + new TypeToken() {}, + null + ); + } + + public static final class PluginsParser, P> implements ArgumentParser { + + private final ServerUtilsPlugin plugin; + private final IntFunction arrayCreator; + + public PluginsParser(ServerUtilsPlugin plugin, IntFunction arrayCreator) { + this.plugin = plugin; + this.arrayCreator = arrayCreator; + } + + @Override + public ArgumentParseResult parse(CommandContext context, Queue inputQueue) { + if (inputQueue.isEmpty()) { + return ArgumentParseResult.failure(new NoInputProvidedException(PluginsParser.class, context)); + } + + P[] plugins = arrayCreator.apply(inputQueue.size()); + for (int i = 0; i < plugins.length; i++) { + Optional

pluginOptional = plugin.getPluginManager().getPlugin(inputQueue.peek()); + if (!pluginOptional.isPresent()) { + return ArgumentParseResult.failure(new IllegalArgumentException( + "Plugin '" + inputQueue.peek() + "' does not exist!" + )); + } + + inputQueue.remove(); + plugins[i] = pluginOptional.get(); + } + + return ArgumentParseResult.success(plugins); + } + + @Override + public List suggestions(CommandContext context, String input) { + return plugin.getPluginManager().getPluginNames(); + } + + @Override + public boolean isContextFree() { + return true; + } + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java new file mode 100644 index 0000000..fec659e --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java @@ -0,0 +1,31 @@ +package net.frankheijden.serverutils.common.commands.brigadier; + +import cloud.commandframework.brigadier.CloudBrigadierManager; +import com.mojang.brigadier.arguments.StringArgumentType; +import io.leangen.geantyref.TypeToken; +import net.frankheijden.serverutils.common.commands.arguments.JarFilesArgument; +import net.frankheijden.serverutils.common.commands.arguments.PluginsArgument; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; + +public class BrigadierHandler, P> { + + private final CloudBrigadierManager brigadierManager; + + public BrigadierHandler(CloudBrigadierManager brigadierManager) { + this.brigadierManager = brigadierManager; + } + + /** + * Registers types with the cloud brigadier manager. + */ + public void registerTypes() { + brigadierManager.registerMapping( + new TypeToken>() {}, + builder -> builder.toConstant(StringArgumentType.greedyString()) + ); + brigadierManager.registerMapping( + new TypeToken>() {}, + builder -> builder.toConstant(StringArgumentType.greedyString()) + ); + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java b/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java index 5356c73..7760fe4 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java @@ -9,7 +9,7 @@ public class CommandsResource extends ServerUtilsResource { private static final String COMMANDS_RESOURCE = "commands"; - public CommandsResource(ServerUtilsPlugin plugin) { + public CommandsResource(ServerUtilsPlugin plugin) { super(plugin, COMMANDS_RESOURCE); } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/config/ConfigResource.java b/Common/src/main/java/net/frankheijden/serverutils/common/config/ConfigResource.java index 1b08bb0..a46ada5 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/config/ConfigResource.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/config/ConfigResource.java @@ -6,7 +6,7 @@ public class ConfigResource extends ServerUtilsResource { private static final String CONFIG_RESOURCE = "config"; - public ConfigResource(ServerUtilsPlugin plugin) { + public ConfigResource(ServerUtilsPlugin plugin) { super(plugin, CONFIG_RESOURCE); } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/config/MessagesResource.java b/Common/src/main/java/net/frankheijden/serverutils/common/config/MessagesResource.java index c3220c7..ecffb7f 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/config/MessagesResource.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/config/MessagesResource.java @@ -8,7 +8,7 @@ public class MessagesResource extends ServerUtilsResource { public static final String MESSAGES_RESOURCE = "messages"; - public MessagesResource(ServerUtilsPlugin plugin) { + public MessagesResource(ServerUtilsPlugin plugin) { super(plugin, MESSAGES_RESOURCE); } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/config/ServerUtilsResource.java b/Common/src/main/java/net/frankheijden/serverutils/common/config/ServerUtilsResource.java index 2b4fbec..b40295e 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/config/ServerUtilsResource.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/config/ServerUtilsResource.java @@ -5,12 +5,12 @@ import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; public abstract class ServerUtilsResource { - protected final ServerUtilsPlugin plugin; + protected final ServerUtilsPlugin plugin; protected final ServerUtilsConfig config; protected final JsonConfig defaultConfig; protected ServerUtilsResource( - ServerUtilsPlugin plugin, + ServerUtilsPlugin plugin, ServerUtilsConfig config, JsonConfig defaultConfig ) { @@ -19,7 +19,7 @@ public abstract class ServerUtilsResource { this.defaultConfig = defaultConfig; } - protected ServerUtilsResource(ServerUtilsPlugin plugin, String resourceName) { + protected ServerUtilsResource(ServerUtilsPlugin plugin, String resourceName) { this.plugin = plugin; this.defaultConfig = JsonConfig.load(plugin.getResourceProvider(), plugin.getPlatform(), resourceName); this.config = ServerUtilsConfig.init( diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java deleted file mode 100644 index 530b4ea..0000000 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/CloseableResult.java +++ /dev/null @@ -1,92 +0,0 @@ -package net.frankheijden.serverutils.common.entities; - -import java.io.Closeable; -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -/** - * A result which should be closed when done. - */ -public class CloseableResult implements Closeable { - - private Result result; - private final List closeables; - - /** - * Constructs a new closable result. - * Used for unloading / reloading a plugin. - * NB: The closable needs to be closed to fully ensure that the old plugin doesn't work anymore! - * @param result The result of the procedure - * @param closeables The list of closable's of the procedure. - */ - public CloseableResult(Result result, List closeables) { - this.result = result; - this.closeables = closeables; - } - - /** - * Constructs a new closable result with no closable instance. - * @param result The result of the procedure - */ - public CloseableResult(Result result) { - this(result, null); - } - - /** - * Constructs a new closable result with a closable instance and success result. - * @param closeable The closable of the procedure. - */ - public CloseableResult(Closeable closeable) { - this(Result.SUCCESS, Collections.singletonList(closeable)); - } - - /** - * Constructs a new closable result with a closable instance and success result. - * @param closeables The list of closable's of the procedure. - */ - public CloseableResult(List closeables) { - this(Result.SUCCESS, closeables); - } - - /** - * Retrieves the result. - * @return The result. - */ - public Result getResult() { - return result; - } - - /** - * Sets the result of this instance. - * @param result The result to set. - * @return The current instance. - */ - public CloseableResult set(Result result) { - this.result = result; - return this; - } - - /** - * Attempts to close the closable, essentially wrapping it with try-catch. - */ - public void tryClose() { - if (closeables == null) return; - try { - close(); - } catch (IOException ex) { - ex.printStackTrace(); - } - } - - /** - * Closes the closable. - * @throws IOException Iff an I/O error occurred. - */ - @Override - public void close() throws IOException { - for (Closeable closeable : closeables) { - closeable.close(); - } - } -} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/LoadResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/LoadResult.java deleted file mode 100644 index 7d4fdde..0000000 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/LoadResult.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.frankheijden.serverutils.common.entities; - -/** - * A result which contains a loaded object from a load operation. - * @param The loaded object type - */ -public class LoadResult { - - private final T obj; - private final Result result; - - /** - * Constructs a new LoadResult with an object and a result. - * @param obj The object of the load operation. - * @param result The result of the load operation. - */ - public LoadResult(T obj, Result result) { - this.obj = obj; - this.result = result; - } - - /** - * Constructs a new LoadResult with an object and a success result. - * @param obj The object of the load operation. - */ - public LoadResult(T obj) { - this(obj, Result.SUCCESS); - } - - /** - * Constructs a new LoadResult without a loaded object, just a result. - * @param result The result of the load operation. - */ - public LoadResult(Result result) { - this(null, result); - } - - /** - * Retrieves the loaded object. - * @return The loaded object. - */ - public T get() { - return obj; - } - - /** - * The result of the LoadResult. - * @return The result. - */ - public Result getResult() { - return result; - } - - /** - * Checks whether the result is a success. - * @return Whether there is success or not. - */ - public boolean isSuccess() { - return obj != null && result == Result.SUCCESS; - } -} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java index 3a20111..8965299 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java @@ -2,6 +2,7 @@ package net.frankheijden.serverutils.common.entities; import cloud.commandframework.Command; import cloud.commandframework.CommandManager; +import cloud.commandframework.brigadier.CloudBrigadierManager; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -9,6 +10,7 @@ import java.nio.file.Path; import java.util.Collection; import java.util.logging.Logger; import net.frankheijden.serverutils.common.ServerUtilsApp; +import net.frankheijden.serverutils.common.commands.brigadier.BrigadierHandler; import net.frankheijden.serverutils.common.config.CommandsResource; import net.frankheijden.serverutils.common.config.ConfigResource; import net.frankheijden.serverutils.common.config.MessagesResource; @@ -19,7 +21,7 @@ import net.frankheijden.serverutils.common.providers.ChatProvider; import net.frankheijden.serverutils.common.providers.ResourceProvider; import net.frankheijden.serverutils.common.utils.FileUtils; -public abstract class ServerUtilsPlugin, S> { +public abstract class ServerUtilsPlugin, S, D extends ServerUtilsPluginDescription> { private final UpdateManager updateManager = new UpdateManager(); private CommandsResource commandsResource; @@ -43,7 +45,7 @@ public abstract class ServerUtilsPlugin, return messagesResource; } - public abstract AbstractPluginManager

getPluginManager(); + public abstract AbstractPluginManager getPluginManager(); public abstract AbstractTaskManager getTaskManager(); @@ -91,6 +93,11 @@ public abstract class ServerUtilsPlugin, protected abstract CommandManager newCommandManager(); + protected void handleBrigadier(CloudBrigadierManager brigadierManager) { + BrigadierHandler handler = new BrigadierHandler<>(brigadierManager); + handler.registerTypes(); + } + /** * Enables the plugin. */ diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPluginDescription.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPluginDescription.java new file mode 100644 index 0000000..c35c264 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPluginDescription.java @@ -0,0 +1,19 @@ +package net.frankheijden.serverutils.common.entities; + +import java.io.File; +import java.util.Set; + +public interface ServerUtilsPluginDescription { + + String getId(); + + String getName(); + + String getVersion(); + + String getAuthor(); + + File getFile(); + + Set getDependencies(); +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/exceptions/InvalidPluginDescriptionException.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/exceptions/InvalidPluginDescriptionException.java new file mode 100644 index 0000000..2aa5989 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/exceptions/InvalidPluginDescriptionException.java @@ -0,0 +1,16 @@ +package net.frankheijden.serverutils.common.entities.exceptions; + +public class InvalidPluginDescriptionException extends RuntimeException { + + public InvalidPluginDescriptionException() { + super(); + } + + public InvalidPluginDescriptionException(String message) { + super(message); + } + + public InvalidPluginDescriptionException(Throwable cause) { + super(cause); + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/AbstractResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/AbstractResult.java similarity index 72% rename from Common/src/main/java/net/frankheijden/serverutils/common/entities/AbstractResult.java rename to Common/src/main/java/net/frankheijden/serverutils/common/entities/results/AbstractResult.java index d4b3456..ec9c35e 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/AbstractResult.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/AbstractResult.java @@ -1,4 +1,6 @@ -package net.frankheijden.serverutils.common.entities; +package net.frankheijden.serverutils.common.entities.results; + +import net.frankheijden.serverutils.common.entities.ServerCommandSender; public interface AbstractResult { diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResult.java new file mode 100644 index 0000000..572ead6 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResult.java @@ -0,0 +1,43 @@ +package net.frankheijden.serverutils.common.entities.results; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class CloseablePluginResult extends PluginResult implements Closeable { + + private final List closeables; + + public CloseablePluginResult(String pluginId, Result result) { + super(pluginId, result); + this.closeables = Collections.emptyList(); + } + + public CloseablePluginResult(String pluginId, T plugin, Result result, List closeables) { + super(pluginId, plugin, result); + this.closeables = closeables; + } + + /** + * Attempts to close the closable, essentially wrapping it with try-catch. + */ + public void tryClose() { + if (closeables == null) return; + try { + close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /** + * Closes the closable. + */ + @Override + public void close() throws IOException { + for (Closeable closeable : closeables) { + closeable.close(); + } + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java new file mode 100644 index 0000000..10bf827 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java @@ -0,0 +1,81 @@ +package net.frankheijden.serverutils.common.entities.results; + +import java.io.Closeable; +import java.io.IOException; +import java.util.List; + +public class CloseablePluginResults extends PluginResults implements Closeable { + + @Override + public CloseablePluginResults addResult(PluginResult pluginResult) { + if (!(pluginResult instanceof CloseablePluginResult)) { + throw new IllegalArgumentException("Not an instance of CloseablePluginResult: " + pluginResult); + } + results.add(pluginResult); + return this; + } + + @Override + public CloseablePluginResults addResult(String pluginId, Result result) { + super.addResult(pluginId, result); + return this; + } + + @Override + public CloseablePluginResults addResult(String pluginId, T plugin) { + super.addResult(pluginId, plugin); + return this; + } + + @Override + protected CloseablePluginResults addResult(String pluginId, T plugin, Result result) { + super.addResult(pluginId, plugin, result); + return this; + } + + public CloseablePluginResults addResult(String pluginId, T plugin, List closeables) { + return addResult(new CloseablePluginResult<>(pluginId, plugin, Result.SUCCESS, closeables)); + } + + @Override + public CloseablePluginResult first() { + PluginResult pluginResult = super.first(); + if (!(pluginResult instanceof CloseablePluginResult)) { + throw new IllegalArgumentException("Not an instance of CloseablePluginResult: " + pluginResult); + } + return (CloseablePluginResult) pluginResult; + } + + @Override + public CloseablePluginResult last() { + PluginResult pluginResult = super.last(); + if (!(pluginResult instanceof CloseablePluginResult)) { + throw new IllegalArgumentException("Not an instance of CloseablePluginResult: " + pluginResult); + } + return (CloseablePluginResult) pluginResult; + } + + /** + * Attempts to close the {@link CloseablePluginResult}'s enclosed. + */ + public void tryClose() { + try { + for (PluginResult pluginResult : this) { + if (pluginResult instanceof CloseablePluginResult) { + ((CloseablePluginResult) pluginResult).close(); + } + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Override + public void close() throws IOException { + for (PluginResult pluginResult : this) { + if (pluginResult instanceof CloseablePluginResult) { + ((CloseablePluginResult) pluginResult).close(); + } + } + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResult.java new file mode 100644 index 0000000..53eb674 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResult.java @@ -0,0 +1,37 @@ +package net.frankheijden.serverutils.common.entities.results; + +public class PluginResult { + + private final String pluginId; + private final T plugin; + private final Result result; + + public PluginResult(String pluginId, Result result) { + this(pluginId, null, result); + } + + /** + * Constructs a new PluginResult. + */ + public PluginResult(String pluginId, T plugin, Result result) { + this.pluginId = pluginId; + this.plugin = plugin; + this.result = result; + } + + public String getPluginId() { + return pluginId; + } + + public T getPlugin() { + return plugin; + } + + public Result getResult() { + return result; + } + + public boolean isSuccess() { + return plugin != null && result == Result.SUCCESS; + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResults.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResults.java new file mode 100644 index 0000000..6de0815 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/PluginResults.java @@ -0,0 +1,80 @@ +package net.frankheijden.serverutils.common.entities.results; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; + +public class PluginResults implements Iterable> { + + protected final List> results; + + public PluginResults() { + this.results = new ArrayList<>(); + } + + public PluginResults addResult(String pluginId, Result result) { + addResult(pluginId, null, result); + return this; + } + + public PluginResults addResult(String pluginId, T plugin) { + addResult(pluginId, plugin, Result.SUCCESS); + return this; + } + + protected PluginResults addResult(String pluginId, T plugin, Result result) { + addResult(new PluginResult<>(pluginId, plugin, result)); + return this; + } + + public PluginResults addResult(PluginResult pluginResult) { + this.results.add(pluginResult); + return this; + } + + public boolean isSuccess() { + return results.stream().allMatch(PluginResult::isSuccess); + } + + public List> getResults() { + return results; + } + + /** + * Creates an array of all plugins. + * @throws IllegalArgumentException Iff a result was not successful (check {@link PluginResults#isSuccess()} first!) + */ + public List getPlugins() { + List plugins = new ArrayList<>(results.size()); + for (PluginResult result : results) { + if (!result.isSuccess()) throw new IllegalArgumentException( + "Result after handling plugin '" + result.getPluginId() + "' was not successful!" + ); + plugins.add(result.getPlugin()); + } + return plugins; + } + + public PluginResult first() { + return results.get(0); + } + + public PluginResult last() { + return results.get(results.size() - 1); + } + + /** + * Sends the results to given receiver. + */ + public void sendTo(ServerCommandSender sender, String action) { + for (PluginResult result : results) { + result.getResult().sendTo(sender, action, result.getPluginId()); + } + } + + @Override + public Iterator> iterator() { + return results.iterator(); + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/Result.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/Result.java similarity index 90% rename from Common/src/main/java/net/frankheijden/serverutils/common/entities/Result.java rename to Common/src/main/java/net/frankheijden/serverutils/common/entities/results/Result.java index f242443..781c35c 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/Result.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/Result.java @@ -1,6 +1,7 @@ -package net.frankheijden.serverutils.common.entities; +package net.frankheijden.serverutils.common.entities.results; import net.frankheijden.serverutils.common.ServerUtilsApp; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; /** * An enum containing possible results. diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/WatchResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java similarity index 84% rename from Common/src/main/java/net/frankheijden/serverutils/common/entities/WatchResult.java rename to Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java index 4e64f94..787378d 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/WatchResult.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java @@ -1,6 +1,7 @@ -package net.frankheijden.serverutils.common.entities; +package net.frankheijden.serverutils.common.entities.results; import net.frankheijden.serverutils.common.ServerUtilsApp; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; public enum WatchResult implements AbstractResult { diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/listeners/PlayerListener.java b/Common/src/main/java/net/frankheijden/serverutils/common/listeners/PlayerListener.java index 8d28334..53fc3b1 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/listeners/PlayerListener.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/listeners/PlayerListener.java @@ -4,7 +4,7 @@ import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; import net.frankheijden.serverutils.common.tasks.UpdateCheckerTask; -public abstract class PlayerListener, P, C extends ServerCommandSender> +public abstract class PlayerListener, P, C extends ServerCommandSender> extends ServerUtilsListener { protected PlayerListener(U plugin) { diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/listeners/ServerUtilsListener.java b/Common/src/main/java/net/frankheijden/serverutils/common/listeners/ServerUtilsListener.java index c717964..d08336d 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/listeners/ServerUtilsListener.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/listeners/ServerUtilsListener.java @@ -3,7 +3,7 @@ package net.frankheijden.serverutils.common.listeners; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; -public abstract class ServerUtilsListener, C extends ServerCommandSender> { +public abstract class ServerUtilsListener, C extends ServerCommandSender> { protected final U plugin; diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java b/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java index 3713c30..3468956 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java @@ -1,54 +1,316 @@ package net.frankheijden.serverutils.common.managers; import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import net.frankheijden.serverutils.common.ServerUtilsApp; -import net.frankheijden.serverutils.common.entities.AbstractResult; -import net.frankheijden.serverutils.common.entities.CloseableResult; -import net.frankheijden.serverutils.common.entities.LoadResult; -import net.frankheijden.serverutils.common.entities.Result; +import net.frankheijden.serverutils.common.entities.results.AbstractResult; +import net.frankheijden.serverutils.common.entities.results.CloseablePluginResult; +import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; +import net.frankheijden.serverutils.common.entities.results.PluginResult; +import net.frankheijden.serverutils.common.entities.results.PluginResults; +import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.entities.ServerCommandSender; -import net.frankheijden.serverutils.common.entities.WatchResult; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import net.frankheijden.serverutils.common.entities.results.WatchResult; +import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; import net.frankheijden.serverutils.common.providers.PluginProvider; import net.frankheijden.serverutils.common.tasks.PluginWatcherTask; +import net.frankheijden.serverutils.common.utils.DependencyUtils; -public interface AbstractPluginManager

extends PluginProvider

{ +public abstract class AbstractPluginManager implements PluginProvider { - LoadResult

loadPlugin(String pluginFile); + /** + * Loads the given plugin by their jar file. + */ + public PluginResult

loadPlugin(String pluginFile) { + File file = new File(getPluginsFolder(), pluginFile); + if (!file.exists()) return new PluginResult<>(pluginFile, Result.NOT_EXISTS); + return loadPlugin(file); + } - LoadResult

loadPlugin(File file); + public PluginResult

loadPlugin(File file) { + return loadPlugins(Collections.singletonList(file)).first(); + } - Result enablePlugin(P plugin); + /** + * Loads a list of files as plugins. + */ + public PluginResults

loadPlugins(List files) { + List descriptions = new ArrayList<>(files.size()); - Result disablePlugin(P plugin); + for (File file : files) { + D description; + try { + Optional descriptionOptional = getPluginDescription(file); + if (!descriptionOptional.isPresent()) { + return new PluginResults

().addResult(file.getName(), Result.NOT_EXISTS); + } - Result reloadPlugin(String pluginName); + description = descriptionOptional.get(); + } catch (InvalidPluginDescriptionException ex) { + return new PluginResults

().addResult(file.getName(), Result.INVALID_DESCRIPTION); + } - Result reloadPlugin(P plugin); + if (getPlugin(description.getId()).isPresent()) { + return new PluginResults

().addResult(description.getId(), Result.ALREADY_LOADED); + } - CloseableResult unloadPlugin(String pluginName); + descriptions.add(description); + } - CloseableResult unloadPlugin(P plugin); + List orderedDescriptions; + try { + orderedDescriptions = determineLoadOrder(descriptions); + } catch (IllegalStateException ex) { + ex.printStackTrace(); + + StringBuilder sb = new StringBuilder(); + for (File file : files) { + sb.append(", ").append(file.getName()); + } + + return new PluginResults

().addResult(sb.substring(2), Result.ERROR); + } + + return loadPluginDescriptions(orderedDescriptions); + } + + protected abstract PluginResults

loadPluginDescriptions(List descriptions); + + /** + * Enables the given plugin by name. + */ + public PluginResult

enablePlugin(String pluginId) { + return getPlugin(pluginId) + .map(this::enablePlugin) + .orElse(new PluginResult<>(pluginId, Result.NOT_EXISTS)); + } + + public PluginResult

enablePlugin(P plugin) { + return enablePlugins(Collections.singletonList(plugin)).first(); + } + + /** + * Enables a list of plugins. + */ + public PluginResults

enablePlugins(List

plugins) { + for (P plugin : plugins) { + String pluginId = getPluginId(plugin); + if (isPluginEnabled(pluginId)) { + return new PluginResults

().addResult(pluginId, Result.ALREADY_ENABLED); + } + } + + return enableOrderedPlugins(determineLoadOrder(plugins)); + } + + protected abstract PluginResults

enableOrderedPlugins(List

plugins); + + public boolean isPluginEnabled(P plugin) { + return isPluginEnabled(getPluginId(plugin)); + } + + public abstract boolean isPluginEnabled(String pluginId); + + /** + * Disables the given plugin by name. + */ + public PluginResult

disablePlugin(String pluginId) { + return getPlugin(pluginId) + .map(this::disablePlugin) + .orElse(new PluginResult<>(pluginId, Result.NOT_EXISTS)); + } + + public PluginResult

disablePlugin(P plugin) { + return disablePlugins(Collections.singletonList(plugin)).first(); + } + + /** + * Disables a list of plugins. + */ + public PluginResults

disablePlugins(List

plugins) { + for (P plugin : plugins) { + if (!isPluginEnabled(plugin)) { + return new PluginResults

().addResult(getPluginId(plugin), Result.ALREADY_DISABLED); + } + } + + List

orderedPlugins; + try { + orderedPlugins = determineLoadOrder(plugins); + } catch (IllegalStateException ex) { + ex.printStackTrace(); + + StringBuilder sb = new StringBuilder(); + for (P plugin : plugins) { + sb.append(", ").append(getPluginId(plugin)); + } + + return new PluginResults

().addResult(sb.substring(2), Result.ERROR); + } + + Collections.reverse(orderedPlugins); + return disableOrderedPlugins(orderedPlugins); + } + + public abstract PluginResults

disableOrderedPlugins(List

plugins); + + /** + * Reloads the given plugin by name. + */ + public PluginResult

reloadPlugin(String pluginId) { + return getPlugin(pluginId) + .map(this::reloadPlugin) + .orElse(new PluginResult<>(pluginId, Result.NOT_EXISTS)); + } + + public PluginResult

reloadPlugin(P plugin) { + return reloadPlugins(Collections.singletonList(plugin)).first(); + } + + /** + * Reloads the given plugins. + */ + public PluginResults

reloadPlugins(List

plugins) { + PluginResults

disableResults = disablePlugins(plugins); + for (PluginResult

disableResult : disableResults.getResults()) { + if (!disableResult.isSuccess() && disableResult.getResult() != Result.ALREADY_DISABLED) { + return disableResults; + } + } + + List pluginIds = new ArrayList<>(plugins.size()); + for (P plugin : plugins) { + pluginIds.add(getPluginId(plugin)); + } + + CloseablePluginResults

unloadResults = unloadPlugins(plugins); + if (!unloadResults.isSuccess()) return unloadResults; + unloadResults.tryClose(); + + List pluginFiles = new ArrayList<>(plugins.size()); + for (String pluginId : pluginIds) { + Optional pluginFile = getPluginFile(pluginId); + if (!pluginFile.isPresent()) return new PluginResults

().addResult(pluginId, Result.FILE_DELETED); + pluginFiles.add(pluginFile.get()); + } + + PluginResults

loadResults = loadPlugins(pluginFiles); + if (!loadResults.isSuccess()) return loadResults; + + List

loadedPlugins = new ArrayList<>(pluginIds.size()); + for (PluginResult

loadResult : loadResults) { + loadedPlugins.add(loadResult.getPlugin()); + } + + return enablePlugins(loadedPlugins); + } + + /** + * Unloads the given plugin by name. + */ + public CloseablePluginResult

unloadPlugin(String pluginId) { + return getPlugin(pluginId) + .map(this::unloadPlugin) + .orElse(new CloseablePluginResult<>(pluginId, Result.NOT_EXISTS)); + } + + public CloseablePluginResult

unloadPlugin(P plugin) { + return unloadPlugins(Collections.singletonList(plugin)).first(); + } + + /** + * Unloads a list of plugins. + */ + public CloseablePluginResults

unloadPlugins(List

plugins) { + List

orderedPlugins; + try { + orderedPlugins = determineLoadOrder(plugins); + } catch (IllegalStateException ex) { + ex.printStackTrace(); + + StringBuilder sb = new StringBuilder(); + for (P plugin : plugins) { + sb.append(", ").append(getPluginId(plugin)); + } + + return new CloseablePluginResults

().addResult(sb.substring(2), Result.ERROR); + } + + Collections.reverse(orderedPlugins); + return unloadOrderedPlugins(orderedPlugins); + } + + public abstract CloseablePluginResults

unloadOrderedPlugins(List

plugins); + + /** + * Determines the load order of a list of plugins. + */ + public List

determineLoadOrder(List

plugins) throws IllegalStateException { + Map descriptionMap = new HashMap<>(plugins.size()); + for (P plugin : plugins) { + descriptionMap.put(getLoadedPluginDescription(plugin), plugin); + } + + List

orderedPlugins = new ArrayList<>(plugins.size()); + for (D description : determineLoadOrder(descriptionMap.keySet())) { + orderedPlugins.add(descriptionMap.get(description)); + } + return orderedPlugins; + } + + /** + * Determines the load order for a given collection of descriptions. + * @throws IllegalStateException Iff circular dependency + */ + public List determineLoadOrder(Collection descriptions) throws IllegalStateException { + Map pluginIdToDescriptionMap = new HashMap<>(); + for (D description : descriptions) { + pluginIdToDescriptionMap.put(description.getId(), description); + } + + Map> dependencyMap = new HashMap<>(descriptions.size()); + for (D description : descriptions) { + Set dependencyStrings = description.getDependencies(); + Set dependencies = new HashSet<>(); + + for (String dependencyString : dependencyStrings) { + D dependency = pluginIdToDescriptionMap.get(dependencyString); + if (dependency != null) { + dependencies.add(dependency); + } + } + + dependencyMap.put(description, dependencies); + } + + return DependencyUtils.determineOrder(dependencyMap); + } /** * Starts watching the specified plugin for changes. * Reloads the plugin if a change is detected. - * @param pluginName The plugin to watch. - * @return The result of the action. */ - default AbstractResult watchPlugin(ServerCommandSender sender, String pluginName) { - if (getPlugin(pluginName) == null) return Result.NOT_EXISTS; + public AbstractResult watchPlugin(ServerCommandSender sender, String pluginId) { + if (getPlugin(pluginId).isPresent()) return Result.NOT_EXISTS; ServerUtilsApp.getPlugin().getTaskManager() - .runTaskAsynchronously(pluginName, new PluginWatcherTask(sender, pluginName)); + .runTaskAsynchronously(pluginId, new PluginWatcherTask(sender, pluginId)); return WatchResult.START; } /** * Stops watching the plugin for changes. - * @param pluginName The plugin to stop watching. - * @return The result of the action. */ - default AbstractResult unwatchPlugin(String pluginName) { - if (ServerUtilsApp.getPlugin().getTaskManager().cancelTask(pluginName)) return WatchResult.STOPPED; + public AbstractResult unwatchPlugin(String pluginId) { + if (ServerUtilsApp.getPlugin().getTaskManager().cancelTask(pluginId)) return WatchResult.STOPPED; return WatchResult.NOT_WATCHING; } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java b/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java index a961e7b..3507e67 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java @@ -4,11 +4,14 @@ import java.io.File; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import net.frankheijden.serverutils.common.ServerUtilsApp; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; -public interface PluginProvider

{ +public interface PluginProvider { default File getPluginsFolder() { return ServerUtilsApp.getPlugin().getDataFolder().getParentFile(); @@ -16,13 +19,43 @@ public interface PluginProvider

{ List

getPlugins(); - String getPluginName(P plugin); + default String getPluginId(P plugin) { + return getLoadedPluginDescription(plugin).getId(); + } - File getPluginFile(P plugin); + default File getPluginFile(P plugin) { + return getLoadedPluginDescription(plugin).getFile(); + } - File getPluginFile(String pluginName); + /** + * Attempts to find the file for a given plugin id. + */ + default Optional getPluginFile(String pluginId) { + for (File file : getPluginJars()) { + Optional pluginDescriptionOptional; + try { + pluginDescriptionOptional = getPluginDescription(file); + } catch (InvalidPluginDescriptionException ex) { + continue; + } - P getPlugin(String pluginName); + if (pluginDescriptionOptional.isPresent() && pluginDescriptionOptional.get().getId().equals(pluginId)) { + return Optional.of(file); + } + } + return Optional.empty(); + } + + Optional

getPlugin(String pluginId); + + D getLoadedPluginDescription(P plugin); + + default Optional getPluginDescription(String pluginId) throws InvalidPluginDescriptionException { + Optional fileOptional = getPluginFile(pluginId); + return fileOptional.flatMap(this::getPluginDescription); + } + + Optional getPluginDescription(File file) throws InvalidPluginDescriptionException; Object getInstance(P plugin); @@ -34,7 +67,7 @@ public interface PluginProvider

{ */ default List

getPluginsSorted() { List

plugins = getPlugins(); - plugins.sort(Comparator.comparing(this::getPluginName)); + plugins.sort(Comparator.comparing(this::getPluginId)); return plugins; } @@ -44,7 +77,7 @@ public interface PluginProvider

{ */ default List getPluginNames() { return getPlugins().stream() - .map(this::getPluginName) + .map(this::getPluginId) .collect(Collectors.toList()); } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java index 37cd7e6..3401f3e 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java @@ -5,7 +5,7 @@ import net.frankheijden.serverutils.common.ServerUtilsApp; import net.frankheijden.serverutils.common.entities.AbstractTask; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; -import net.frankheijden.serverutils.common.entities.WatchResult; +import net.frankheijden.serverutils.common.entities.results.WatchResult; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import net.frankheijden.serverutils.common.managers.AbstractTaskManager; import net.frankheijden.serverutils.common.providers.ChatProvider; @@ -28,8 +28,8 @@ public class PluginWatcherTask extends AbstractTask { StandardWatchEventKinds.ENTRY_DELETE }; - private final ServerUtilsPlugin plugin = ServerUtilsApp.getPlugin(); - private final AbstractPluginManager pluginManager = plugin.getPluginManager(); + private final ServerUtilsPlugin plugin = ServerUtilsApp.getPlugin(); + private final AbstractPluginManager pluginManager = plugin.getPluginManager(); private final ChatProvider chatProvider = plugin.getChatProvider(); @SuppressWarnings("rawtypes") private final AbstractTaskManager taskManager = plugin.getTaskManager(); @@ -52,7 +52,7 @@ public class PluginWatcherTask extends AbstractTask { public PluginWatcherTask(ServerCommandSender sender, String pluginName) { this.sender = sender; this.pluginName = pluginName; - this.file = pluginManager.getPluginFile(pluginName); + this.file = pluginManager.getPluginFile(pluginName).orElse(null); this.run = new AtomicBoolean(true); } @@ -82,7 +82,7 @@ public class PluginWatcherTask extends AbstractTask { send(WatchResult.CHANGE); pluginManager.reloadPlugin(pluginName); - file = pluginManager.getPluginFile(pluginName); + file = pluginManager.getPluginFile(pluginName).orElse(null); } }, 10L); } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/UpdateCheckerTask.java b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/UpdateCheckerTask.java index 541dd62..6bb32f7 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/UpdateCheckerTask.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/UpdateCheckerTask.java @@ -11,8 +11,8 @@ import java.net.UnknownHostException; import java.util.logging.Level; import net.frankheijden.serverutils.common.ServerUtilsApp; import net.frankheijden.serverutils.common.config.ServerUtilsConfig; -import net.frankheijden.serverutils.common.entities.LoadResult; -import net.frankheijden.serverutils.common.entities.Result; +import net.frankheijden.serverutils.common.entities.results.PluginResult; +import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; import net.frankheijden.serverutils.common.entities.http.GitHubAsset; @@ -22,7 +22,7 @@ import net.frankheijden.serverutils.common.utils.GitHubUtils; import net.frankheijden.serverutils.common.utils.VersionUtils; import net.frankheijden.serverutilsupdater.common.Updater; -public class UpdateCheckerTask, P> implements Runnable { +public class UpdateCheckerTask, P> implements Runnable { private final U plugin; private final ServerCommandSender sender; @@ -60,7 +60,7 @@ public class UpdateCheckerTask, P> imple * Checks for updates if enabled per config for the specific action. * Action must be 'login' or 'boot'. */ - public static , P> void tryStart( + public static , P> void tryStart( U plugin, ServerCommandSender sender, String action @@ -75,7 +75,7 @@ public class UpdateCheckerTask, P> imple * Checks for updates and downloads/installs if configured. * Action must be 'login' or 'boot'. */ - public static , P> void start( + public static , P> void start( U plugin, ServerCommandSender sender, String action @@ -241,21 +241,19 @@ public class UpdateCheckerTask, P> imple private void tryReloadPlugin(File pluginFile, File updaterFile) { plugin.getTaskManager().runTask(() -> { - LoadResult

loadResult = plugin.getPluginManager().loadPlugin(updaterFile); + PluginResult

loadResult = plugin.getPluginManager().loadPlugin(updaterFile); if (!loadResult.isSuccess()) { - plugin.getLogger().log(Level.INFO, UPDATER_LOAD_ERROR, - loadResult.getResult().name()); + plugin.getLogger().log(Level.INFO, UPDATER_LOAD_ERROR, loadResult.getResult().name()); return; } - P updaterPlugin = loadResult.get(); - Result result = plugin.getPluginManager().enablePlugin(updaterPlugin); - if (result != Result.SUCCESS && result != Result.ALREADY_ENABLED) { - plugin.getLogger().log(Level.INFO, UPDATER_ENABLE_ERROR, result.name()); + PluginResult

enableResult = plugin.getPluginManager().enablePlugin(loadResult.getPlugin()); + if (!enableResult.isSuccess() && enableResult.getResult() != Result.ALREADY_ENABLED) { + plugin.getLogger().log(Level.INFO, UPDATER_ENABLE_ERROR, enableResult.getResult().name()); return; } - Updater updater = (Updater) plugin.getPluginManager().getInstance(updaterPlugin); + Updater updater = (Updater) plugin.getPluginManager().getInstance(enableResult.getPlugin()); updater.update(pluginFile); updaterFile.delete(); }); diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/utils/DependencyUtils.java b/Common/src/main/java/net/frankheijden/serverutils/common/utils/DependencyUtils.java new file mode 100644 index 0000000..ea0022b --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/utils/DependencyUtils.java @@ -0,0 +1,117 @@ +package net.frankheijden.serverutils.common.utils; + +import com.google.common.graph.Graph; +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.MutableGraph; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class DependencyUtils { + + private DependencyUtils() {} + + /** + * Determines the topological order of a dependency map. + * Adapted from https://github.com/VelocityPowered/Velocity. + * @throws IllegalStateException Iff circular dependency. + */ + @SuppressWarnings("UnstableApiUsage") + public static List determineOrder(Map> dependencyMap) throws IllegalStateException { + MutableGraph dependencyGraph = GraphBuilder.directed().allowsSelfLoops(true).build(); + for (T node : dependencyMap.keySet()) { + dependencyGraph.addNode(node); + } + + for (Map.Entry> entry : dependencyMap.entrySet()) { + for (T dependency : entry.getValue()) { + dependencyGraph.putEdge(entry.getKey(), dependency); + } + } + + List orderedList = new ArrayList<>(dependencyMap.size()); + Map marks = new HashMap<>(dependencyMap.size()); + + for (T node : dependencyGraph.nodes()) { + visitNode(dependencyGraph, node, marks, orderedList, new LinkedList<>()); + } + + return orderedList; + } + + @SuppressWarnings("UnstableApiUsage") + private static void visitNode( + Graph dependencyGraph, + T node, + Map marks, + List orderedList, + Deque currentIteration + ) throws IllegalStateException { + Mark mark = marks.getOrDefault(node, Mark.NOT_VISITED); + if (mark == Mark.PERMANENT) { + return; + } else if (mark == Mark.TEMPORARY) { + currentIteration.addLast(node); + + StringBuilder sb = new StringBuilder(); + for (T currentNode : currentIteration) { + sb.append(" -> ").append(currentNode); + } + + throw new IllegalStateException("Circular dependency detected: " + sb.substring(4)); + } + + currentIteration.addLast(node); + marks.put(node, Mark.TEMPORARY); + + for (T successorNode : dependencyGraph.successors(node)) { + visitNode(dependencyGraph, successorNode, marks, orderedList, currentIteration); + } + + marks.put(node, Mark.PERMANENT); + currentIteration.removeLast(); + orderedList.add(node); + } + + private enum Mark { + NOT_VISITED, + TEMPORARY, + PERMANENT + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Common/src/main/resources/commands.json b/Common/src/main/resources/commands.json index db892da..b259406 100644 --- a/Common/src/main/resources/commands.json +++ b/Common/src/main/resources/commands.json @@ -41,21 +41,21 @@ "main": "loadplugin", "aliases": ["lp"], "permission": "serverutils.loadplugin", - "description": "Loads the specified jar file as a plugin.", + "description": "Loads the specified jar file(s).", "display-in-help": true }, "unloadplugin": { "main": "unloadplugin", "aliases": ["up"], "permission": "serverutils.unloadplugin", - "description": "Disables and unloads the specified plugin.", + "description": "Disables and unloads the specified plugin(s).", "display-in-help": true }, "reloadplugin": { "main": "reloadplugin", "aliases": ["rp"], "permission": "serverutils.reloadplugin", - "description": "Reloads the specified plugin.", + "description": "Reloads the specified plugin(s).", "display-in-help": true }, "watchplugin": { diff --git a/Common/src/test/java/net/frankheijden/serverutils/common/utils/DependencyUtilsTest.java b/Common/src/test/java/net/frankheijden/serverutils/common/utils/DependencyUtilsTest.java new file mode 100644 index 0000000..a1dcb34 --- /dev/null +++ b/Common/src/test/java/net/frankheijden/serverutils/common/utils/DependencyUtilsTest.java @@ -0,0 +1,127 @@ +package net.frankheijden.serverutils.common.utils; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.junit.jupiter.params.provider.Arguments.of; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class DependencyUtilsTest { + + @ParameterizedTest(name = "dependencyMap = {0}, expected = {1}") + @MethodSource("dependencyGenerator") + void determineOrderDependencies( + Map> dependencyMap, + List expected + ) { + assertThat(DependencyUtils.determineOrder(dependencyMap)).isEqualTo(expected); + } + + @ParameterizedTest(name = "dependencyMap = {0}") + @MethodSource("circularDependencyGenerator") + void determineOrderCircularDependencies( + Map> dependencyMap + ) { + assertThatIllegalStateException().isThrownBy(() -> DependencyUtils.determineOrder(dependencyMap)); + } + + private static Stream dependencyGenerator() { + return Stream.of( + of( + mapOf( + new Pair<>("B", asSet("A")) + ), + asList("A", "B") + ), + of( + mapOf( + new Pair<>("B", asSet("A")), + new Pair<>("C", asSet("A", "B")) + ), + asList("A", "B", "C") + ), + of( + mapOf( + new Pair<>("A", asSet("B")), + new Pair<>("B", asSet("C", "D")), + new Pair<>("C", asSet()), + new Pair<>("D", asSet("C", "E")), + new Pair<>("E", asSet("F")), + new Pair<>("F", asSet("C")) + ), + asList("C", "F", "E", "D", "B", "A") + ), + of( + mapOf( + new Pair<>("A", asSet()), + new Pair<>("B", asSet()), + new Pair<>("C", asSet()), + new Pair<>("D", asSet("C")) + ), + asList("A", "B", "C", "D") + ) + ); + } + + private static Stream circularDependencyGenerator() { + return Stream.of( + of( + mapOf( + new Pair<>("A", asSet("A")) + ) + ), + of( + mapOf( + new Pair<>("A", asSet("B")), + new Pair<>("B", asSet("A")) + ) + ), + of( + mapOf( + new Pair<>("B", asSet("A")), + new Pair<>("C", asSet("A", "B")), + new Pair<>("A", asSet("C")) + ) + ), + of( + mapOf( + new Pair<>("A", asSet("B")), + new Pair<>("B", asSet("C")), + new Pair<>("C", asSet("D")), + new Pair<>("D", asSet("A")) + ) + ) + ); + } + + private static Set asSet(T... elements) { + return new HashSet<>(asList(elements)); + } + + private static Map mapOf(Pair... pairs) { + Map map = new HashMap<>(); + for (Pair pair : pairs) { + map.put(pair.first, pair.second); + } + return map; + } + + private static final class Pair { + private final A first; + private final B second; + + private Pair(A first, B second) { + this.first = first; + this.second = second; + } + } +} diff --git a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/commands/VelocityCommandServerUtils.java b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/commands/VelocityCommandServerUtils.java index d64cee6..adf828b 100644 --- a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/commands/VelocityCommandServerUtils.java +++ b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/commands/VelocityCommandServerUtils.java @@ -21,16 +21,15 @@ public class VelocityCommandServerUtils extends CommandServerUtils { public VelocityCommandServerUtils(VelocityPlugin plugin) { - super(plugin); + super(plugin, PluginContainer[]::new); } @Override protected FormatBuilder createPluginInfo( FormatBuilder builder, Function>, String> listBuilderFunction, - String pluginName + PluginContainer container ) { - PluginContainer container = plugin.getPluginManager().getPlugin(pluginName); PluginDescription desc = container.getDescription(); return builder diff --git a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityLoadResult.java b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityLoadResult.java deleted file mode 100644 index 68d8bba..0000000 --- a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityLoadResult.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.frankheijden.serverutils.velocity.entities; - -import com.velocitypowered.api.plugin.PluginContainer; -import net.frankheijden.serverutils.common.entities.LoadResult; -import net.frankheijden.serverutils.common.entities.Result; - -public class VelocityLoadResult extends LoadResult { - - public VelocityLoadResult(PluginContainer obj, Result result) { - super(obj, result); - } - - public VelocityLoadResult(PluginContainer obj) { - super(obj); - } - - public VelocityLoadResult(Result result) { - super(result); - } -} diff --git a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPlugin.java b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPlugin.java index c153748..6005cfb 100644 --- a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPlugin.java +++ b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPlugin.java @@ -15,12 +15,7 @@ import net.frankheijden.serverutils.velocity.listeners.VelocityPlayerListener; import net.frankheijden.serverutils.velocity.managers.VelocityPluginManager; import net.frankheijden.serverutils.velocity.managers.VelocityTaskManager; -public class VelocityPlugin extends ServerUtilsPlugin< - PluginContainer, - ScheduledTask, - VelocityCommandSender, - CommandSource - > { +public class VelocityPlugin extends ServerUtilsPlugin { private final ServerUtils plugin; private final VelocityPluginManager pluginManager; @@ -46,13 +41,15 @@ public class VelocityPlugin extends ServerUtilsPlugin< @Override protected VelocityCommandManager newCommandManager() { - return new VelocityCommandManager<>( + VelocityCommandManager commandManager = new VelocityCommandManager<>( plugin.getPluginContainer(), plugin.getProxy(), AsynchronousCommandExecutionCoordinator.newBuilder().build(), chatProvider::get, VelocityCommandSender::getSource ); + handleBrigadier(commandManager.brigadierManager()); + return commandManager; } @Override diff --git a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPluginDescription.java b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPluginDescription.java new file mode 100644 index 0000000..7506c50 --- /dev/null +++ b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/entities/VelocityPluginDescription.java @@ -0,0 +1,69 @@ +package net.frankheijden.serverutils.velocity.entities; + +import com.velocitypowered.api.plugin.PluginDescription; +import com.velocitypowered.api.plugin.meta.PluginDependency; +import java.io.File; +import java.nio.file.Path; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; + +public class VelocityPluginDescription implements ServerUtilsPluginDescription { + + private final PluginDescription description; + private final File file; + private final String author; + private final Set dependencies; + + /** + * Constructs a new BungeePluginDescription. + */ + public VelocityPluginDescription(PluginDescription description) { + this.description = description; + + Optional sourceOptional = description.getSource(); + if (!sourceOptional.isPresent()) throw new InvalidPluginDescriptionException("Source path is null"); + + this.file = sourceOptional.get().toFile(); + this.author = String.join(", ", description.getAuthors()); + this.dependencies = description.getDependencies().stream() + .map(PluginDependency::getId) + .collect(Collectors.toSet()); + } + + @Override + public String getId() { + return this.description.getId(); + } + + @Override + public String getName() { + return this.description.getName().orElse(""); + } + + @Override + public String getVersion() { + return this.description.getVersion().orElse(""); + } + + @Override + public String getAuthor() { + return this.author; + } + + @Override + public File getFile() { + return this.file; + } + + @Override + public Set getDependencies() { + return this.dependencies; + } + + public PluginDescription getDescription() { + return description; + } +} diff --git a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/managers/VelocityPluginManager.java b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/managers/VelocityPluginManager.java index 250c89d..c97112e 100644 --- a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/managers/VelocityPluginManager.java +++ b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/managers/VelocityPluginManager.java @@ -26,11 +26,13 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import net.frankheijden.serverutils.common.entities.CloseableResult; -import net.frankheijden.serverutils.common.entities.Result; +import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; +import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; +import net.frankheijden.serverutils.common.entities.results.PluginResults; +import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.events.PluginEvent; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; -import net.frankheijden.serverutils.velocity.entities.VelocityLoadResult; +import net.frankheijden.serverutils.velocity.entities.VelocityPluginDescription; import net.frankheijden.serverutils.velocity.events.VelocityPluginDisableEvent; import net.frankheijden.serverutils.velocity.events.VelocityPluginEnableEvent; import net.frankheijden.serverutils.velocity.events.VelocityPluginLoadEvent; @@ -44,7 +46,7 @@ import net.frankheijden.serverutils.velocity.reflection.RVelocityPluginManager; import net.frankheijden.serverutils.velocity.reflection.RVelocityScheduler; import org.slf4j.Logger; -public class VelocityPluginManager implements AbstractPluginManager { +public class VelocityPluginManager extends AbstractPluginManager { private static VelocityPluginManager instance; private final ProxyServer proxy; @@ -66,195 +68,223 @@ public class VelocityPluginManager implements AbstractPluginManager loadPluginDescriptions(List descriptions) { + PluginResults loadResults = new PluginResults<>(); - @Override - public VelocityLoadResult loadPlugin(File file) { - if (!file.exists() || file.isDirectory()) return new VelocityLoadResult(Result.NOT_EXISTS); + for (VelocityPluginDescription description : descriptions) { + Path source = description.getFile().toPath(); + Path baseDirectory = source.getParent(); - Object javaPluginLoader = RJavaPluginLoader.newInstance(proxy, file.toPath().getParent()); - PluginDescription candidate = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, file.toPath()); - if (proxy.getPluginManager().isLoaded(candidate.getId())) return new VelocityLoadResult(Result.ALREADY_LOADED); + Object javaPluginLoader = RJavaPluginLoader.newInstance(proxy, baseDirectory); + PluginDescription candidate = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, source); - for (PluginDependency dependency : candidate.getDependencies()) { - if (!dependency.isOptional() && !proxy.getPluginManager().isLoaded(dependency.getId())) { - logger.error( - "Can't load plugin {} due to missing dependency {}", - candidate.getId(), - dependency.getId() - ); - return new VelocityLoadResult(Result.UNKNOWN_DEPENDENCY.arg(dependency.getId())); - } - } - - PluginDescription realPlugin = RJavaPluginLoader.loadPlugin(javaPluginLoader, candidate); - PluginContainer container = RVelocityPluginContainer.newInstance(realPlugin); - proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.PRE)); - proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.POST)); - - return new VelocityLoadResult(container); - } - - @Override - public Result enablePlugin(PluginContainer container) { - proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.PRE)); - if (proxy.getPluginManager().isLoaded(container.getDescription().getId())) return Result.ALREADY_ENABLED; - - Object javaPluginLoader = RJavaPluginLoader.newInstance( - proxy, - container.getDescription().getSource().map(Path::getParent).orElse(null) - ); - PluginDescription realPlugin = container.getDescription(); - Module module = RJavaPluginLoader.createModule(javaPluginLoader, container); - - AbstractModule commonModule = new AbstractModule() { - @Override - protected void configure() { - bind(ProxyServer.class).toInstance(proxy); - bind(PluginManager.class).toInstance(proxy.getPluginManager()); - bind(EventManager.class).toInstance(proxy.getEventManager()); - bind(CommandManager.class).toInstance(proxy.getCommandManager()); - for (PluginContainer container : proxy.getPluginManager().getPlugins()) { - bind(PluginContainer.class) - .annotatedWith(Names.named(container.getDescription().getId())) - .toInstance(container); + dependencyCheck: + for (PluginDependency dependency : candidate.getDependencies()) { + String pluginId = dependency.getId(); + for (VelocityPluginDescription desc : descriptions) { + if (desc.getId().equals(pluginId)) continue dependencyCheck; } - bind(PluginContainer.class) - .annotatedWith(Names.named(realPlugin.getId())) - .toInstance(container); - } - }; - try { - RJavaPluginLoader.createPlugin(javaPluginLoader, container, module, commonModule); - } catch (Exception ex) { - logger.error( - String.format("Can't create plugin %s", container.getDescription().getId()), - ex - ); - return Result.ERROR; + if (!dependency.isOptional() && !proxy.getPluginManager().isLoaded(dependency.getId())) { + logger.error( + "Can't load plugin {} due to missing dependency {}", + candidate.getId(), + dependency.getId() + ); + return loadResults.addResult( + description.getId(), + Result.UNKNOWN_DEPENDENCY.arg(dependency.getId()) + ); + } + } + + PluginDescription realPlugin = RJavaPluginLoader.loadPlugin(javaPluginLoader, candidate); + PluginContainer container = RVelocityPluginContainer.newInstance(realPlugin); + proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.PRE)); + proxy.getEventManager().fire(new VelocityPluginLoadEvent(container, PluginEvent.Stage.POST)); + + loadResults.addResult(description.getId(), container); } - logger.info( - "Loaded plugin {} {} by {}", - realPlugin.getId(), - realPlugin.getVersion().orElse(""), - Joiner.on(", ").join(realPlugin.getAuthors()) - ); + return loadResults; + } - RVelocityPluginManager.registerPlugin(proxy.getPluginManager(), container); - container.getInstance().ifPresent(pluginInstance -> { - RVelocityEventManager.registerInternally(proxy.getEventManager(), container, pluginInstance); - RVelocityEventManager.fireForPlugin( - proxy.getEventManager(), - new ProxyInitializeEvent(), - pluginInstance - ).join(); + @Override + public PluginResults enableOrderedPlugins(List containers) { + PluginResults enableResults = new PluginResults<>(); - ConsoleCommandSource console = proxy.getConsoleCommandSource(); - PermissionsSetupEvent event = new PermissionsSetupEvent( - console, - s -> PermissionFunction.ALWAYS_TRUE + List pluginInstances = new ArrayList<>(containers.size()); + for (PluginContainer container : containers) { + String pluginId = container.getDescription().getId(); + proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.PRE)); + if (isPluginEnabled(pluginId)) { + return enableResults.addResult(pluginId, Result.ALREADY_ENABLED); + } + + Object javaPluginLoader = RJavaPluginLoader.newInstance( + proxy, + container.getDescription().getSource().map(Path::getParent).orElse(null) ); - PermissionFunction permissionFunction = RVelocityEventManager.fireForPlugin( - proxy.getEventManager(), - event, - pluginInstance - ).join().createFunction(console); + PluginDescription realPlugin = container.getDescription(); + Module module = RJavaPluginLoader.createModule(javaPluginLoader, container); - if (permissionFunction == null) { + AbstractModule commonModule = new AbstractModule() { + @Override + protected void configure() { + bind(ProxyServer.class).toInstance(proxy); + bind(PluginManager.class).toInstance(proxy.getPluginManager()); + bind(EventManager.class).toInstance(proxy.getEventManager()); + bind(CommandManager.class).toInstance(proxy.getCommandManager()); + for (PluginContainer container : proxy.getPluginManager().getPlugins()) { + bind(PluginContainer.class) + .annotatedWith(Names.named(container.getDescription().getId())) + .toInstance(container); + } + for (PluginContainer container : containers) { + bind(PluginContainer.class) + .annotatedWith(Names.named(container.getDescription().getId())) + .toInstance(container); + } + } + }; + + try { + RJavaPluginLoader.createPlugin(javaPluginLoader, container, module, commonModule); + } catch (Exception ex) { logger.error( - "A plugin permission provider {} provided an invalid permission function for the console." - + " This is a bug in the plugin, not in Velocity." - + " Falling back to the default permission function.", - event.getProvider().getClass().getName() + String.format("Can't create plugin %s", container.getDescription().getId()), + ex ); - permissionFunction = PermissionFunction.ALWAYS_TRUE; + return enableResults.addResult(pluginId, Result.ERROR); } - RVelocityConsole.setPermissionFunction(console, permissionFunction); - }); + logger.info( + "Loaded plugin {} {} by {}", + realPlugin.getId(), + realPlugin.getVersion().orElse(""), + Joiner.on(", ").join(realPlugin.getAuthors()) + ); - proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.POST)); - return Result.SUCCESS; + RVelocityPluginManager.registerPlugin(proxy.getPluginManager(), container); + Optional instanceOptional = container.getInstance(); + if (instanceOptional.isPresent()) { + Object pluginInstance = instanceOptional.get(); + RVelocityEventManager.registerInternally(proxy.getEventManager(), container, pluginInstance); + pluginInstances.add(pluginInstance); + } + } + + RVelocityEventManager.fireForPlugins( + proxy.getEventManager(), + new ProxyInitializeEvent(), + pluginInstances + ).join(); + + ConsoleCommandSource console = proxy.getConsoleCommandSource(); + PermissionsSetupEvent event = new PermissionsSetupEvent( + console, + s -> PermissionFunction.ALWAYS_TRUE + ); + PermissionFunction permissionFunction = RVelocityEventManager.fireForPlugins( + proxy.getEventManager(), + event, + pluginInstances + ).join().createFunction(console); + + if (permissionFunction == null) { + logger.error( + "A plugin permission provider {} provided an invalid permission function for the console." + + " This is a bug in the plugin, not in Velocity." + + " Falling back to the default permission function.", + event.getProvider().getClass().getName() + ); + permissionFunction = PermissionFunction.ALWAYS_TRUE; + } + + RVelocityConsole.setPermissionFunction(console, permissionFunction); + + for (PluginContainer container : containers) { + proxy.getEventManager().fire(new VelocityPluginEnableEvent(container, PluginEvent.Stage.POST)); + enableResults.addResult(container.getDescription().getId(), container); + } + + return enableResults; } @Override - public Result disablePlugin(PluginContainer container) { - proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.PRE)); - Object pluginInstance = container.getInstance().orElse(null); - if (pluginInstance == null) return Result.NOT_EXISTS; + public boolean isPluginEnabled(String pluginId) { + return proxy.getPluginManager().isLoaded(pluginId); + } - RVelocityEventManager.fireForPlugin( + @Override + public PluginResults disableOrderedPlugins(List containers) { + PluginResults disableResults = new PluginResults<>(); + + List pluginInstances = new ArrayList<>(containers.size()); + for (PluginContainer container : containers) { + proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.PRE)); + String pluginId = getPluginId(container); + Object pluginInstance = container.getInstance().orElse(null); + if (pluginInstance == null) { + return disableResults.addResult(pluginId, Result.ALREADY_DISABLED); + } + + pluginInstances.add(pluginInstance); + } + + RVelocityEventManager.fireForPlugins( proxy.getEventManager(), - pluginInstance, - new ProxyShutdownEvent() + new ProxyShutdownEvent(), + pluginInstances ); - proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.POST)); - return Result.SUCCESS; - } - - @Override - public Result reloadPlugin(String pluginName) { - Optional pluginOptional = proxy.getPluginManager().getPlugin(pluginName); - if (!pluginOptional.isPresent()) return Result.NOT_EXISTS; - return reloadPlugin(pluginOptional.get()); - } - - @Override - public Result reloadPlugin(PluginContainer plugin) { - CloseableResult result = unloadPlugin(plugin); - if (result.getResult() != Result.SUCCESS) return result.getResult(); - result.tryClose(); - - File file = getPluginFile(plugin.getDescription().getId()); - if (file == null) return Result.FILE_DELETED; - - VelocityLoadResult loadResult = loadPlugin(file); - if (!loadResult.isSuccess()) return loadResult.getResult(); - - return enablePlugin(loadResult.get()); - } - - @Override - public CloseableResult unloadPlugin(String pluginName) { - Optional pluginOptional = proxy.getPluginManager().getPlugin(pluginName); - if (!pluginOptional.isPresent()) return new CloseableResult(Result.NOT_EXISTS); - return unloadPlugin(pluginOptional.get()); - } - - @Override - public CloseableResult unloadPlugin(PluginContainer container) { - proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.PRE)); - Optional pluginInstanceOptional = container.getInstance(); - if (!pluginInstanceOptional.isPresent()) return new CloseableResult(Result.INVALID_PLUGIN); - Object pluginInstance = pluginInstanceOptional.get(); - - proxy.getEventManager().unregisterListeners(pluginInstance); - for (ScheduledTask task : RVelocityScheduler.getTasksByPlugin(proxy.getScheduler()).removeAll(pluginInstance)) { - task.cancel(); + for (PluginContainer container : containers) { + proxy.getEventManager().fire(new VelocityPluginDisableEvent(container, PluginEvent.Stage.POST)); + disableResults.addResult(getPluginId(container), container); } - String pluginId = container.getDescription().getId(); - for (String alias : pluginCommandManager.getPluginCommands().removeAll(pluginId)) { - proxy.getCommandManager().unregister(alias); + return disableResults; + } + + @Override + public CloseablePluginResults unloadOrderedPlugins(List containers) { + CloseablePluginResults unloadResults = new CloseablePluginResults<>(); + + for (PluginContainer container : containers) { + proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.PRE)); + String pluginId = getPluginId(container); + Optional pluginInstanceOptional = container.getInstance(); + if (!pluginInstanceOptional.isPresent()) { + return unloadResults.addResult(pluginId, Result.INVALID_PLUGIN); + } + + Object pluginInstance = pluginInstanceOptional.get(); + + proxy.getEventManager().unregisterListeners(pluginInstance); + for (ScheduledTask task : RVelocityScheduler.getTasksByPlugin(proxy.getScheduler()) + .removeAll(pluginInstance)) { + task.cancel(); + } + + for (String alias : pluginCommandManager.getPluginCommands().removeAll(pluginId)) { + proxy.getCommandManager().unregister(alias); + } + + RVelocityPluginManager.getPlugins(proxy.getPluginManager()).remove(pluginId); + RVelocityPluginManager.getPluginInstances(proxy.getPluginManager()).remove(pluginInstance); + + List closeables = new ArrayList<>(); + + ClassLoader loader = pluginInstance.getClass().getClassLoader(); + if (loader instanceof Closeable) { + closeables.add((Closeable) loader); + } + + proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.POST)); + unloadResults.addResult(pluginId, container, closeables); } - RVelocityPluginManager.getPlugins(proxy.getPluginManager()).remove(pluginId); - RVelocityPluginManager.getPluginInstances(proxy.getPluginManager()).remove(pluginInstance); - - List closeables = new ArrayList<>(); - - ClassLoader loader = pluginInstance.getClass().getClassLoader(); - if (loader instanceof Closeable) { - closeables.add((Closeable) loader); - } - - proxy.getEventManager().fire(new VelocityPluginUnloadEvent(container, PluginEvent.Stage.POST)); - return new CloseableResult(closeables); + return unloadResults; } @Override @@ -263,7 +293,7 @@ public class VelocityPluginManager implements AbstractPluginManager getPluginFile(String pluginName) { Object javaPluginLoader = RJavaPluginLoader.newInstance(instance.proxy, getPluginsFolder().toPath()); for (File file : getPluginJars()) { PluginDescription desc = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, file.toPath()); if (desc.getId().equals(pluginName)) { - return file; + return Optional.of(file); } } - return null; + return Optional.empty(); } @Override - public PluginContainer getPlugin(String pluginName) { - return proxy.getPluginManager().getPlugin(pluginName).orElse(null); + public Optional getPlugin(String pluginName) { + return proxy.getPluginManager().getPlugin(pluginName); + } + + @Override + public VelocityPluginDescription getLoadedPluginDescription(PluginContainer plugin) { + return new VelocityPluginDescription(plugin.getDescription()); + } + + @Override + public Optional getPluginDescription( + File file + ) throws InvalidPluginDescriptionException { + Path source = file.toPath(); + Path baseDirectory = source.getParent(); + + try { + Object javaPluginLoader = RJavaPluginLoader.newInstance(proxy, baseDirectory); + PluginDescription candidate = RJavaPluginLoader.loadPluginDescription(javaPluginLoader, source); + return Optional.of(new VelocityPluginDescription(candidate)); + } catch (Exception ex) { + throw new InvalidPluginDescriptionException(ex); + } } @Override diff --git a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/reflection/RVelocityEventManager.java b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/reflection/RVelocityEventManager.java index f8a7d71..0161b11 100644 --- a/Velocity/src/main/java/net/frankheijden/serverutils/velocity/reflection/RVelocityEventManager.java +++ b/Velocity/src/main/java/net/frankheijden/serverutils/velocity/reflection/RVelocityEventManager.java @@ -9,6 +9,7 @@ import dev.frankheijden.minecraftreflection.MinecraftReflection; import java.lang.reflect.Array; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; public class RVelocityEventManager { @@ -27,9 +28,14 @@ public class RVelocityEventManager { * Retrieves the registrations from a plugin for a specific event. */ @SuppressWarnings("unchecked") - public static List getRegistrationsByPlugin(EventManager manager, Object plugin, Class eventClass) { + public static List getRegistrationsByPlugins( + EventManager manager, + List plugins, + Class eventClass + ) { return (List) getHandlersByType(manager).get(eventClass).stream() - .filter(r -> RHandlerRegistration.getPlugin(r).getInstance().orElse(null) == plugin) + .filter(r -> plugins.contains(RHandlerRegistration.getPlugin(r).getInstance().orElse(null))) + .sorted(reflection.get(manager, "handlerComparator")) .collect(Collectors.toList()); } @@ -48,26 +54,27 @@ public class RVelocityEventManager { /** * Fires an event specifically for one plugin. */ - public static CompletableFuture fireForPlugin( + public static CompletableFuture fireForPlugins( EventManager manager, E event, - Object plugin + List pluginInstances ) { - List registrations = getRegistrationsByPlugin(manager, plugin, event.getClass()); + List registrations = getRegistrationsByPlugins(manager, pluginInstances, event.getClass()); CompletableFuture future = new CompletableFuture<>(); Object registrationsEmptyArray = Array.newInstance(RHandlerRegistration.reflection.getClazz(), 0); Class registrationsArrayClass = registrationsEmptyArray.getClass(); - reflection.invoke( + ExecutorService executor = reflection.invoke(manager, "getAsyncExecutor"); + executor.execute(() -> reflection.invoke( manager, "fire", ClassObject.of(CompletableFuture.class, future), ClassObject.of(Object.class, event), ClassObject.of(int.class, 0), - ClassObject.of(boolean.class, false), + ClassObject.of(boolean.class, true), ClassObject.of(registrationsArrayClass, registrations.toArray((Object[]) registrationsEmptyArray)) - ); + )); return future; } diff --git a/build.gradle b/build.gradle index a91957b..cea2d49 100644 --- a/build.gradle +++ b/build.gradle @@ -19,14 +19,19 @@ subprojects { repositories { mavenCentral() maven { url 'https://jitpack.io' } - maven { url 'https://repo.aikar.co/content/groups/aikar/' } maven { url 'https://repo.incendo.org/content/repositories/snapshots' } maven { url 'https://papermc.io/repo/repository/maven-public/' } + maven { url 'https://libraries.minecraft.net' } } dependencies { implementation 'com.github.FrankHeijden.cloud:cloud-core:fea4605277' + implementation 'com.github.FrankHeijden.cloud:cloud-brigadier:fea4605277' implementation 'com.github.FrankHeijden:MinecraftReflection:1.0.0' + implementation 'com.google.guava:guava:30.1.1-jre' + implementation 'com.google.code.gson:gson:2.8.6' + implementation 'me.lucko:commodore:1.10' + compileOnly 'com.mojang:brigadier:1.0.17' testCompile 'org.assertj:assertj-core:3.18.1' testCompile 'org.junit.jupiter:junit-jupiter-api:5.7.0' @@ -55,6 +60,28 @@ subprojects { shadowJar { exclude 'com/mojang/**' + exclude 'javax/annotation/**' + exclude 'org/checkerframework/**' + exclude 'com/google/errorprone/**' + exclude 'com/google/j2objc/**' + exclude 'com/google/thirdparty/**' + exclude 'com/google/common/annotations/**' + exclude 'com/google/common/base/**' + exclude 'com/google/common/cache/**' + exclude 'com/google/common/collect/**' + exclude 'com/google/common/escape/**' + exclude 'com/google/common/eventbus/**' + exclude 'com/google/common/hash/**' + exclude 'com/google/common/html/**' + exclude 'com/google/common/io/**' + exclude 'com/google/common/math/**' + exclude 'com/google/common/net/**' + exclude 'com/google/common/primitives/**' + exclude 'com/google/common/reflect/**' + exclude 'com/google/common/util/**' + exclude 'com/google/common/xml/**' + relocate 'com.google.gson', dependencyDir + '.gson' + relocate 'com.google.common.graph', dependencyDir + '.common.graph' relocate 'dev.frankheijden.minecraftreflection', dependencyDir + '.minecraftreflection' relocate 'cloud.commandframework', dependencyDir + '.cloud' relocate 'io.leangen.geantyref', dependencyDir + '.typetoken' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 4a9bba5..6149d9f 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -39,7 +39,7 @@ - + From ff1bfc25149418f12b1eadac327bfea17f26201b Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Fri, 30 Jul 2021 13:36:17 +0200 Subject: [PATCH 2/6] Also clean commands and recipes when unloading --- .../serverutils/bukkit/managers/BukkitPluginManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java index fa47d45..63f58e2 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java @@ -123,6 +123,9 @@ public class BukkitPluginManager extends AbstractPluginManager closeables = new ArrayList<>(); try { RSimplePluginManager.getPlugins(Bukkit.getPluginManager()).remove(plugin); From 94664286e53a6fb2846f535aa5da086bead0bb91 Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Fri, 30 Jul 2021 14:19:23 +0200 Subject: [PATCH 3/6] Fix BungeeCord multi plugin management --- .../bungee/managers/BungeePluginManager.java | 47 ++++++++----------- .../bungee/reflection/RLibraryLoader.java | 16 ------- .../bungee/reflection/RPluginClassLoader.java | 22 +-------- .../bungee/reflection/RPluginManager.java | 17 +++---- .../results/CloseablePluginResults.java | 7 +-- 5 files changed, 32 insertions(+), 77 deletions(-) delete mode 100644 Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RLibraryLoader.java diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java index 8240623..00cf68f 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/managers/BungeePluginManager.java @@ -4,10 +4,10 @@ import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -21,7 +21,6 @@ import net.frankheijden.serverutils.bungee.events.BungeePluginDisableEvent; import net.frankheijden.serverutils.bungee.events.BungeePluginEnableEvent; import net.frankheijden.serverutils.bungee.events.BungeePluginLoadEvent; import net.frankheijden.serverutils.bungee.events.BungeePluginUnloadEvent; -import net.frankheijden.serverutils.bungee.reflection.RLibraryLoader; import net.frankheijden.serverutils.bungee.reflection.RPluginClassLoader; import net.frankheijden.serverutils.bungee.reflection.RPluginManager; import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; @@ -34,6 +33,7 @@ import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.PluginDescription; +import net.md_5.bungee.api.plugin.PluginManager; import org.yaml.snakeyaml.Yaml; public class BungeePluginManager extends AbstractPluginManager { @@ -72,35 +72,25 @@ public class BungeePluginManager extends AbstractPluginManager loadPluginDescriptions(List descriptions) { PluginResults loadResults = new PluginResults<>(); + PluginManager proxyPluginManager = proxy.getPluginManager(); + Map toLoad = RPluginManager.getToLoad(proxyPluginManager); + if (toLoad == null) toLoad = new HashMap<>(descriptions.size()); + for (BungeePluginDescription description : descriptions) { PluginDescription desc = description.getDescription(); - Plugin plugin; + toLoad.put(desc.getName(), desc); + } - try { - Object libraryLoader = RPluginManager.getLibraryLoader(proxy.getPluginManager()); - ClassLoader classLoader = RLibraryLoader.createLoader(libraryLoader, desc); - URLClassLoader loader = (URLClassLoader) RPluginClassLoader.newInstance( - proxy, - desc, - desc.getFile(), - classLoader - ); + RPluginManager.setToLoad(proxyPluginManager, toLoad); + proxyPluginManager.loadPlugins(); - Class main = loader.loadClass(desc.getMain()); - plugin = (Plugin) main.getDeclaredConstructor().newInstance(); + for (BungeePluginDescription description : descriptions) { + Optional pluginOptional = getPlugin(description.getId()); + if (!pluginOptional.isPresent()) return loadResults.addResult(description.getId(), Result.ERROR); - RPluginManager.getPlugins(proxy.getPluginManager()).put(description.getId(), plugin); - proxy.getPluginManager().callEvent(new BungeePluginLoadEvent(plugin, PluginEvent.Stage.PRE)); - plugin.onLoad(); - } catch (Throwable th) { - proxy.getLogger().log(Level.WARNING, "Error loading plugin " + description.getId(), th); - return loadResults.addResult(description.getId(), Result.ERROR); - } - - proxy.getLogger().log(Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[] { - desc.getName(), desc.getVersion(), desc.getAuthor() - }); - proxy.getPluginManager().callEvent(new BungeePluginLoadEvent(plugin, PluginEvent.Stage.POST)); + Plugin plugin = pluginOptional.get(); + proxyPluginManager.callEvent(new BungeePluginLoadEvent(plugin, PluginEvent.Stage.PRE)); + proxyPluginManager.callEvent(new BungeePluginLoadEvent(plugin, PluginEvent.Stage.POST)); loadResults.addResult(description.getId(), plugin); } @@ -134,7 +124,7 @@ public class BungeePluginManager extends AbstractPluginManager closeables = new ArrayList<>(); try { RPluginManager.clearPlugin(proxy.getPluginManager(), plugin); - addIfInstance(closeables, RPluginClassLoader.getPluginClassLoader(plugin)); + addIfInstance(closeables, RPluginClassLoader.removePluginClassLoader(plugin)); addIfInstance(closeables, plugin.getClass().getClassLoader()); } catch (Exception ex) { ex.printStackTrace(); diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RLibraryLoader.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RLibraryLoader.java deleted file mode 100644 index 1f19035..0000000 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RLibraryLoader.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.frankheijden.serverutils.bungee.reflection; - -import dev.frankheijden.minecraftreflection.MinecraftReflection; -import net.md_5.bungee.api.plugin.PluginDescription; - -public class RLibraryLoader { - - private static final MinecraftReflection reflection = MinecraftReflection - .of("net.md_5.bungee.api.plugin.LibraryLoader"); - - private RLibraryLoader() {} - - public static ClassLoader createLoader(Object instance, PluginDescription desc) { - return instance == null ? null : reflection.invoke(instance, "createLoader", desc); - } -} diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginClassLoader.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginClassLoader.java index caa9bc0..b127545 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginClassLoader.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginClassLoader.java @@ -1,12 +1,8 @@ package net.frankheijden.serverutils.bungee.reflection; -import java.io.File; import java.util.Set; -import dev.frankheijden.minecraftreflection.ClassObject; import dev.frankheijden.minecraftreflection.MinecraftReflection; -import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.api.plugin.PluginDescription; public class RPluginClassLoader { @@ -16,23 +12,9 @@ public class RPluginClassLoader { private RPluginClassLoader() {} /** - * Creates a new instance of a PluginClassLoader from given parameters. + * Removes the PluginClassLoader of a specific plugin. */ - public static Object newInstance(ProxyServer proxy, PluginDescription desc, File file, ClassLoader classLoader) { - return reflection.newInstance( - ClassObject.of(ProxyServer.class, proxy), - ClassObject.of(PluginDescription.class, desc), - ClassObject.of(File.class, file), - ClassObject.of(ClassLoader.class, classLoader) - ); - } - - /** - * Retrieves the PluginClassLoader of a specific plugin. - * @param plugin The plugin to lookup the PluginClassLoader for. - * @return The PluginClassLoader. - */ - public static Object getPluginClassLoader(Plugin plugin) { + public static Object removePluginClassLoader(Plugin plugin) { Set allLoaders = reflection.get(null, "allLoaders"); if (allLoaders == null) return null; diff --git a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginManager.java b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginManager.java index a812ddb..c928c16 100644 --- a/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginManager.java +++ b/Bungee/src/main/java/net/frankheijden/serverutils/bungee/reflection/RPluginManager.java @@ -6,6 +6,7 @@ import dev.frankheijden.minecraftreflection.MinecraftReflection; import net.frankheijden.serverutils.common.utils.MapUtils; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.api.plugin.PluginDescription; import org.yaml.snakeyaml.Yaml; public class RPluginManager { @@ -26,10 +27,6 @@ public class RPluginManager { MapUtils.remove(reflection.get(instance, "toLoad"), pluginName); } - public static Map getPlugins(Object instance) { - return reflection.get(instance, "plugins"); - } - public static Yaml getYaml(Object instance) { return reflection.get(instance, "yaml"); } @@ -38,6 +35,14 @@ public class RPluginManager { return reflection.get(instance, "commandMap"); } + public static Map getToLoad(Object pluginManager) { + return reflection.get(pluginManager, "toLoad"); + } + + public static void setToLoad(Object pluginManager, Map toLoad) { + reflection.set(pluginManager, "toLoad", toLoad); + } + /** * Retrieves the registered plugin of the command. * @param instance The PluginManager instance. @@ -53,8 +58,4 @@ public class RPluginManager { } return null; } - - public static Object getLibraryLoader(Object instance) { - return reflection.get(instance, "libraryLoader"); - } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java index 10bf827..4d10121 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/CloseablePluginResults.java @@ -60,11 +60,7 @@ public class CloseablePluginResults extends PluginResults implements Close */ public void tryClose() { try { - for (PluginResult pluginResult : this) { - if (pluginResult instanceof CloseablePluginResult) { - ((CloseablePluginResult) pluginResult).close(); - } - } + close(); } catch (IOException ex) { ex.printStackTrace(); } @@ -77,5 +73,6 @@ public class CloseablePluginResults extends PluginResults implements Close ((CloseablePluginResult) pluginResult).close(); } } + System.gc(); } } From d1aa18b30b226db3041ea35290dcdc556091985d Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Fri, 30 Jul 2021 14:27:34 +0200 Subject: [PATCH 4/6] Fix Velocity command suggestions --- .../common/commands/brigadier/BrigadierHandler.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java index fec659e..3b5f8c8 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/brigadier/BrigadierHandler.java @@ -21,11 +21,15 @@ public class BrigadierHandler, P> { public void registerTypes() { brigadierManager.registerMapping( new TypeToken>() {}, - builder -> builder.toConstant(StringArgumentType.greedyString()) + builder -> builder + .cloudSuggestions() + .toConstant(StringArgumentType.greedyString()) ); brigadierManager.registerMapping( new TypeToken>() {}, - builder -> builder.toConstant(StringArgumentType.greedyString()) + builder -> builder + .cloudSuggestions() + .toConstant(StringArgumentType.greedyString()) ); } } From 1b4997869d90d4657c6be0df3ff9c3cdbb2d4150 Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Sat, 31 Jul 2021 00:45:48 +0200 Subject: [PATCH 5/6] Add "depending plugin" warning --- Bukkit/build.gradle | 2 +- .../commands/BukkitCommandServerUtils.java | 11 ++- .../bukkit/managers/BukkitPluginManager.java | 21 +++--- .../bukkit/reflection/RJavaPlugin.java | 4 -- .../bukkit/reflection/RJavaPluginLoader.java | 2 +- .../bukkit/reflection/RPluginClassLoader.java | 13 ++++ .../src/main/resources/bukkit-commands.json | 11 ++- Bungee/build.gradle | 2 +- .../common/commands/CommandServerUtils.java | 71 +++++++++++++++++-- .../common/commands/ServerUtilsCommand.java | 49 ++++++++++--- .../commands/arguments/PluginsArgument.java | 33 +++++++-- .../common/config/CommandsResource.java | 23 ++++++ .../common/providers/PluginProvider.java | 17 +++++ Common/src/main/resources/commands.json | 22 +++++- Common/src/main/resources/messages.json | 7 ++ Velocity/build.gradle | 2 +- build.gradle | 10 ++- 17 files changed, 257 insertions(+), 43 deletions(-) diff --git a/Bukkit/build.gradle b/Bukkit/build.gradle index f3152fb..ca2a9f0 100644 --- a/Bukkit/build.gradle +++ b/Bukkit/build.gradle @@ -4,7 +4,7 @@ version = rootProject.version archivesBaseName = rootProject.name + '-Bukkit' dependencies { - implementation 'com.github.FrankHeijden.cloud:cloud-paper:fea4605277' + implementation "com.github.FrankHeijden.cloud:cloud-paper:${rootProject.cloudVersion}" implementation 'org.bstats:bstats-bukkit:2.2.1' implementation project(":Common") compileOnly 'com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT' diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java index 6d93f32..c2d9624 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/commands/BukkitCommandServerUtils.java @@ -19,6 +19,7 @@ import net.frankheijden.serverutils.bukkit.reflection.RDedicatedServer; import net.frankheijden.serverutils.bukkit.utils.ReloadHandler; import net.frankheijden.serverutils.bukkit.utils.VersionReloadHandler; import net.frankheijden.serverutils.common.commands.CommandServerUtils; +import net.frankheijden.serverutils.common.commands.arguments.PluginsArgument; import net.frankheijden.serverutils.common.entities.results.PluginResults; import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.ForwardFilter; @@ -76,7 +77,11 @@ public class BukkitCommandServerUtils extends CommandServerUtils( + true, + "plugins", + new PluginsArgument.PluginsParser<>(plugin, arrayCreator, getRawPath("disableplugin")) + )) .handler(this::handleDisablePlugin)); manager.command(buildSubcommand(builder, "reloadconfig") .argument(getArgument("config")) @@ -95,6 +100,10 @@ public class BukkitCommandServerUtils extends CommandServerUtils plugins = Arrays.asList(context.get("plugins")); + if (checkDependingPlugins(context, sender, plugins, "disableplugin")) { + return; + } + PluginResults result = plugin.getPluginManager().disablePlugins(plugins); result.sendTo(sender, "disabl"); } diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java index 63f58e2..5eb7b68 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/managers/BukkitPluginManager.java @@ -131,16 +131,17 @@ public class BukkitPluginManager extends AbstractPluginManager { - Map> classes = RPluginClassLoader.getClasses(loader); - getPluginLoader(getPluginFile(plugin)).ifPresent(pluginLoader -> { - RJavaPluginLoader.removeClasses(pluginLoader, classes.keySet()); - }); - }); - addIfInstance(closeables, loader); - addIfInstance(closeables, RJavaPlugin.clearJavaPlugin(plugin)); + ClassLoader classLoader = plugin.getClass().getClassLoader(); + RPluginClassLoader.clearClassLoader(classLoader); + + PluginLoader loader = RPluginClassLoader.getLoader(classLoader); + Map> classes = RPluginClassLoader.getClasses(classLoader); + RJavaPluginLoader.removeClasses(loader, classes.keySet()); + + RJavaPlugin.clearJavaPlugin(plugin); + + addIfInstance(closeables, RPluginClassLoader.getLibraryLoader(classLoader)); + addIfInstance(closeables, classLoader); } catch (Exception ex) { ex.printStackTrace(); return unloadResults.addResult(pluginId, Result.ERROR); diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java index 5e93d36..89541ee 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPlugin.java @@ -29,10 +29,6 @@ public class RJavaPlugin { public static Closeable clearJavaPlugin(Object instance) { reflection.set(instance, "loader", null); reflection.set(instance, "classLoader", null); - Class clazz = reflection.invoke(instance, "getClass"); - if (clazz != null && clazz.getClassLoader() instanceof Closeable) { - return (Closeable) clazz.getClassLoader(); - } return null; } } diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java index e3555ef..f9a310a 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RJavaPluginLoader.java @@ -1,10 +1,10 @@ package net.frankheijden.serverutils.bukkit.reflection; import dev.frankheijden.minecraftreflection.MinecraftReflection; +import dev.frankheijden.minecraftreflection.exceptions.MinecraftReflectionException; import java.util.Collection; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; -import dev.frankheijden.minecraftreflection.exceptions.MinecraftReflectionException; import org.bukkit.plugin.java.JavaPluginLoader; public class RJavaPluginLoader { diff --git a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java index 75d2acd..f5c8f6c 100644 --- a/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java +++ b/Bukkit/src/main/java/net/frankheijden/serverutils/bukkit/reflection/RPluginClassLoader.java @@ -1,7 +1,9 @@ package net.frankheijden.serverutils.bukkit.reflection; import dev.frankheijden.minecraftreflection.MinecraftReflection; +import dev.frankheijden.minecraftreflection.MinecraftReflectionVersion; import java.util.Map; +import org.bukkit.plugin.PluginLoader; public class RPluginClassLoader { @@ -23,12 +25,23 @@ public class RPluginClassLoader { } } + public static PluginLoader getLoader(ClassLoader loader) { + if (loader == null) return null; + return reflection.get(loader, "loader"); + } + + public static ClassLoader getLibraryLoader(ClassLoader loader) { + if (loader == null && MinecraftReflectionVersion.MINOR <= 16) return null; + return reflection.get(loader, "libraryLoader"); + } + /** * Clears the plugin fields from the specified PluginClassLoader. * @param pluginLoader The plugin loader instance. */ public static void clearPluginClassLoader(Object pluginLoader) { if (pluginLoader == null) return; + reflection.set(pluginLoader, "plugin", null); reflection.set(pluginLoader, "pluginInit", null); } diff --git a/Bukkit/src/main/resources/bukkit-commands.json b/Bukkit/src/main/resources/bukkit-commands.json index 191af7c..ca50a95 100644 --- a/Bukkit/src/main/resources/bukkit-commands.json +++ b/Bukkit/src/main/resources/bukkit-commands.json @@ -18,7 +18,16 @@ ], "permission": "serverutils.disableplugin", "description": "Disables the specified plugin(s).", - "display-in-help": true + "display-in-help": true, + "flags": { + "force": { + "main": "force", + "aliases": ["f"], + "permission": "serverutils.disableplugin", + "description": "Force disables the specified plugin(s).", + "display-in-help": false + } + } }, "reloadconfig": { "main": "reloadconfig", diff --git a/Bungee/build.gradle b/Bungee/build.gradle index 82dcd43..4e70908 100644 --- a/Bungee/build.gradle +++ b/Bungee/build.gradle @@ -8,7 +8,7 @@ repositories { } dependencies { - implementation 'com.github.FrankHeijden.cloud:cloud-bungee:fea4605277' + implementation "com.github.FrankHeijden.cloud:cloud-bungee:${rootProject.cloudVersion}" implementation 'org.bstats:bstats-bungeecord:2.2.1' implementation project(":Common") compileOnly 'net.md-5:bungeecord-api:1.17-R0.1-SNAPSHOT' diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java index c7c3684..4e28500 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java @@ -7,6 +7,7 @@ import cloud.commandframework.context.CommandContext; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; @@ -14,6 +15,7 @@ import java.util.function.IntFunction; import net.frankheijden.serverutils.common.commands.arguments.JarFilesArgument; import net.frankheijden.serverutils.common.commands.arguments.PluginArgument; import net.frankheijden.serverutils.common.commands.arguments.PluginsArgument; +import net.frankheijden.serverutils.common.config.MessagesResource; import net.frankheijden.serverutils.common.config.ServerUtilsConfig; import net.frankheijden.serverutils.common.entities.results.AbstractResult; import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; @@ -30,7 +32,7 @@ import net.frankheijden.serverutils.common.utils.ListFormat; public abstract class CommandServerUtils, P, C extends ServerCommandSender> extends ServerUtilsCommand { - private final IntFunction arrayCreator; + protected final IntFunction arrayCreator; protected CommandServerUtils(U plugin, IntFunction arrayCreator) { super(plugin, "serverutils"); @@ -40,7 +42,7 @@ public abstract class CommandServerUtils manager, Command.Builder builder) { addArgument(new JarFilesArgument<>(true, "jarFiles", plugin)); - addArgument(new PluginsArgument<>(true, "plugins", plugin, arrayCreator)); + addArgument(new PluginsArgument<>(true, "plugins", new PluginsArgument.PluginsParser<>(plugin, arrayCreator))); addArgument(new PluginArgument<>(true, "plugin", plugin)); addArgument(CommandArgument.ofType(String.class, "command") .manager(manager) @@ -57,10 +59,18 @@ public abstract class CommandServerUtils( + true, + "plugins", + new PluginsArgument.PluginsParser<>(plugin, arrayCreator, getRawPath("unloadplugin")) + )) .handler(this::handleUnloadPlugin)); manager.command(buildSubcommand(builder, "reloadplugin") - .argument(getArgument("plugins")) + .argument(new PluginsArgument<>( + true, + "plugins", + new PluginsArgument.PluginsParser<>(plugin, arrayCreator, getRawPath("reloadplugin")) + )) .handler(this::handleReloadPlugin)); manager.command(buildSubcommand(builder, "watchplugin") .argument(getArgument("plugin")) @@ -170,6 +180,10 @@ public abstract class CommandServerUtils plugins = Arrays.asList(context.get("plugins")); + if (checkDependingPlugins(context, sender, plugins, "unloadplugin")) { + return; + } + PluginResults

disableResults = plugin.getPluginManager().disablePlugins(plugins); for (PluginResult

disableResult : disableResults.getResults()) { if (!disableResult.isSuccess() && disableResult.getResult() != Result.ALREADY_DISABLED) { @@ -187,10 +201,59 @@ public abstract class CommandServerUtils plugins = Arrays.asList(context.get("plugins")); + if (checkDependingPlugins(context, sender, plugins, "reloadplugin")) { + return; + } + PluginResults

reloadResult = plugin.getPluginManager().reloadPlugins(plugins); reloadResult.sendTo(sender, "reload"); } + protected boolean checkDependingPlugins(CommandContext context, C sender, List

plugins, String subcommand) { + if (context.flags().contains("force")) return false; + + AbstractPluginManager pluginManager = plugin.getPluginManager(); + MessagesResource messages = plugin.getMessagesResource(); + + boolean hasDependingPlugins = false; + for (P plugin : plugins) { + String pluginId = pluginManager.getPluginId(plugin); + + List

dependingPlugins = pluginManager.getPluginsDependingOn(pluginId); + if (!dependingPlugins.isEmpty()) { + String prefixString = messages.getMessage( + "serverutils.depending_plugins.prefix", + "%what%", pluginId + ); + + String dependingPluginsString = ListBuilder.create(dependingPlugins) + .format(p -> messages.getMessage( + "serverutils.depending_plugins.format", + "%plugin%", pluginManager.getPluginId(p) + )) + .seperator(messages.getMessage("serverutils.depending_plugins.separator")) + .lastSeperator(messages.getMessage("serverutils.depending_plugins.last_separator")) + .toString(); + messages.sendRawMessage(sender, prefixString + dependingPluginsString); + hasDependingPlugins = true; + } + } + + if (hasDependingPlugins) { + String flagPath = getRawPath(subcommand) + ".flags.force"; + String forceFlag = plugin.getCommandsResource().getAllFlagAliases(flagPath).stream() + .min(Comparator.comparingInt(String::length)) + .orElse("-f"); + + messages.sendMessage(sender, + "serverutils.depending_plugins.override", + "%command%", context.getRawInputJoined() + " " + forceFlag + ); + } + + return hasDependingPlugins; + } + private void handleWatchPlugin(CommandContext context) { C sender = context.getSender(); P pluginArg = context.get("plugin"); diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java index d6c9775..59d13f0 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/ServerUtilsCommand.java @@ -7,7 +7,9 @@ import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.flags.CommandFlag; import cloud.commandframework.permission.CommandPermission; import cloud.commandframework.permission.Permission; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import net.frankheijden.serverutils.common.config.ServerUtilsConfig; import net.frankheijden.serverutils.common.entities.ServerCommandSender; @@ -58,9 +60,14 @@ public abstract class ServerUtilsCommand buildSubcommand(Command.Builder builder, String subcommandName) { CommandElement subcommand = parseSubcommand(subcommandName); - return builder + + builder = builder .literal(subcommand.getMain(), subcommand.getDescription(), subcommand.getAliases()) .permission(subcommand.getPermission()); + for (CommandElement flagElement : subcommand.getFlags()) { + builder = builder.flag(createFlag(flagElement)); + } + return builder; } /** @@ -76,7 +83,16 @@ public abstract class ServerUtilsCommand flags = new ArrayList<>(); + Object flagsObject = elementConfig.get("flags"); + if (flagsObject instanceof ServerUtilsConfig) { + ServerUtilsConfig flagsConfig = ((ServerUtilsConfig) flagsObject); + for (String flagName : flagsConfig.getKeys()) { + flags.add(parseElement((ServerUtilsConfig) flagsConfig.get(flagName))); + } + } + + return new CommandElement(main, description, permission, displayInHelp, aliases, flags); } /** @@ -86,15 +102,25 @@ public abstract class ServerUtilsCommand parseFlag(String flagName) { - CommandElement flag = parseElement((ServerUtilsConfig) commandConfig.get("flags." + flagName)); - return CommandFlag.newBuilder(flag.getMain()) - .withAliases(flag.getAliases()) - .withPermission(flag.getPermission()) - .withDescription(flag.getDescription()) + return createFlag(parseElement((ServerUtilsConfig) commandConfig.get("flags." + flagName))); + } + + /** + * Creates a flag from a CommandElement. + */ + public CommandFlag createFlag(CommandElement flagElement) { + return CommandFlag.newBuilder(flagElement.getMain()) + .withAliases(flagElement.getAliases()) + .withPermission(flagElement.getPermission()) + .withDescription(flagElement.getDescription()) .build(); } @@ -124,19 +150,22 @@ public abstract class ServerUtilsCommand flags; public CommandElement( String main, ArgumentDescription description, CommandPermission permission, boolean displayInHelp, - String... aliases + String[] aliases, + List flags ) { this.main = main; this.description = description; this.permission = permission; this.displayInHelp = displayInHelp; this.aliases = aliases; + this.flags = flags; } public String getMain() { @@ -158,5 +187,9 @@ public abstract class ServerUtilsCommand getFlags() { + return flags; + } } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java index bcb765c..1bb30fd 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/arguments/PluginsArgument.java @@ -6,9 +6,11 @@ import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.context.CommandContext; import cloud.commandframework.exceptions.parsing.NoInputProvidedException; import io.leangen.geantyref.TypeToken; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Queue; +import java.util.Set; import java.util.function.IntFunction; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; @@ -21,13 +23,12 @@ public class PluginsArgument, P> extends Comman public PluginsArgument( boolean required, String name, - ServerUtilsPlugin plugin, - IntFunction arrayCreator + PluginsParser parser ) { super( required, name, - new PluginsParser<>(plugin, arrayCreator), + parser, "", new TypeToken() {}, null @@ -38,10 +39,23 @@ public class PluginsArgument, P> extends Comman private final ServerUtilsPlugin plugin; private final IntFunction arrayCreator; + private final String commandConfigPath; public PluginsParser(ServerUtilsPlugin plugin, IntFunction arrayCreator) { + this(plugin, arrayCreator, null); + } + + /** + * Constructs a new PluginsParser. + */ + public PluginsParser( + ServerUtilsPlugin plugin, + IntFunction arrayCreator, + String commandConfigPath + ) { this.plugin = plugin; this.arrayCreator = arrayCreator; + this.commandConfigPath = commandConfigPath; } @Override @@ -50,8 +64,13 @@ public class PluginsArgument, P> extends Comman return ArgumentParseResult.failure(new NoInputProvidedException(PluginsParser.class, context)); } - P[] plugins = arrayCreator.apply(inputQueue.size()); - for (int i = 0; i < plugins.length; i++) { + Set flags = plugin.getCommandsResource().getAllFlagAliases(commandConfigPath + ".flags.force"); + + int queueSize = inputQueue.size(); + List

plugins = new ArrayList<>(queueSize); + for (int i = 0; i < queueSize; i++) { + if (flags.contains(inputQueue.peek())) continue; + Optional

pluginOptional = plugin.getPluginManager().getPlugin(inputQueue.peek()); if (!pluginOptional.isPresent()) { return ArgumentParseResult.failure(new IllegalArgumentException( @@ -60,10 +79,10 @@ public class PluginsArgument, P> extends Comman } inputQueue.remove(); - plugins[i] = pluginOptional.get(); + plugins.add(pluginOptional.get()); } - return ArgumentParseResult.success(plugins); + return ArgumentParseResult.success(plugins.stream().toArray(arrayCreator)); } @Override diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java b/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java index 7760fe4..8c24afd 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/config/CommandsResource.java @@ -1,5 +1,8 @@ package net.frankheijden.serverutils.common.config; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; /** @@ -13,6 +16,26 @@ public class CommandsResource extends ServerUtilsResource { super(plugin, COMMANDS_RESOURCE); } + /** + * Retrieves all flag aliases for the given flag path. + */ + public Set getAllFlagAliases(String path) { + Object flagObject = getConfig().get(path); + if (flagObject instanceof ServerUtilsConfig) { + ServerUtilsConfig flagConfig = (ServerUtilsConfig) flagObject; + + Set flagAliases = new HashSet<>(); + flagAliases.add("--" + flagConfig.getString("main")); + for (String alias : flagConfig.getStringList("aliases")) { + flagAliases.add("-" + alias); + } + + return flagAliases; + } + + return Collections.emptySet(); + } + @Override public void migrate(int currentConfigVersion) { diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java b/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java index 3507e67..f32ef55 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/providers/PluginProvider.java @@ -1,6 +1,7 @@ package net.frankheijden.serverutils.common.providers; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -46,6 +47,22 @@ public interface PluginProvider { return Optional.empty(); } + /** + * Retrieves plugins which depend on the given plugin. + */ + default List

getPluginsDependingOn(String pluginId) { + List

plugins = new ArrayList<>(); + + for (P loadedPlugin : getPlugins()) { + ServerUtilsPluginDescription description = getLoadedPluginDescription(loadedPlugin); + if (description.getDependencies().contains(pluginId)) { + plugins.add(loadedPlugin); + } + } + + return plugins; + } + Optional

getPlugin(String pluginId); D getLoadedPluginDescription(P plugin); diff --git a/Common/src/main/resources/commands.json b/Common/src/main/resources/commands.json index b259406..7357d8d 100644 --- a/Common/src/main/resources/commands.json +++ b/Common/src/main/resources/commands.json @@ -49,14 +49,32 @@ "aliases": ["up"], "permission": "serverutils.unloadplugin", "description": "Disables and unloads the specified plugin(s).", - "display-in-help": true + "display-in-help": true, + "flags": { + "force": { + "main": "force", + "aliases": ["f"], + "permission": "serverutils.unloadplugin", + "description": "Force disable and unload the specified plugin(s).", + "display-in-help": false + } + } }, "reloadplugin": { "main": "reloadplugin", "aliases": ["rp"], "permission": "serverutils.reloadplugin", "description": "Reloads the specified plugin(s).", - "display-in-help": true + "display-in-help": true, + "flags": { + "force": { + "main": "force", + "aliases": ["f"], + "permission": "serverutils.reloadplugin", + "description": "Force reloads the specified plugin(s).", + "display-in-help": false + } + } }, "watchplugin": { "main": "watchplugin", diff --git a/Common/src/main/resources/messages.json b/Common/src/main/resources/messages.json index 3cd99ab..b2a02ce 100644 --- a/Common/src/main/resources/messages.json +++ b/Common/src/main/resources/messages.json @@ -13,6 +13,13 @@ "invalid_description": "&cAn error occurred while %action%ing &4%what%&c, plugin doesn't have a valid description!", "invalid_plugin": "&cAn error occurred while %action%ing &4%what%&c, plugin is invalid!", "unknown_dependency": "&cAn error occurred while %action%ing &4%what%&c, plugin has a dependeny which is not loaded: &4%arg%", + "depending_plugins": { + "prefix": "&cPlugin &4%what%&c has depending plugins: ", + "format": "&4%plugin%", + "separator": "&c, ", + "last_separator": " &cand ", + "override": "&cUse \"&4/%command%&c\" to force command execution." + }, "watcher": { "start": "&3Started watching &b%what%&3!", "change": "&3Change detected for plugin &b%what%&3, reloading now...", diff --git a/Velocity/build.gradle b/Velocity/build.gradle index 6a9a9e7..e824765 100644 --- a/Velocity/build.gradle +++ b/Velocity/build.gradle @@ -13,7 +13,7 @@ repositories { } dependencies { - implementation 'com.github.FrankHeijden.cloud:cloud-velocity:fea4605277' + implementation "com.github.FrankHeijden.cloud:cloud-velocity:${rootProject.cloudVersion}" implementation 'org.bstats:bstats-velocity:2.2.1' implementation project(":Common") compileOnly 'com.velocitypowered:velocity-api:3.0.0' diff --git a/build.gradle b/build.gradle index cea2d49..3a39d74 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,12 @@ group = 'net.frankheijden.serverutils' String dependencyDir = group + '.dependencies' version = '2.5.6' +allprojects { + ext { + cloudVersion = '62e3bc97ef' + } +} + sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 subprojects { @@ -25,8 +31,8 @@ subprojects { } dependencies { - implementation 'com.github.FrankHeijden.cloud:cloud-core:fea4605277' - implementation 'com.github.FrankHeijden.cloud:cloud-brigadier:fea4605277' + implementation "com.github.FrankHeijden.cloud:cloud-core:${rootProject.cloudVersion}" + implementation "com.github.FrankHeijden.cloud:cloud-brigadier:${rootProject.cloudVersion}" implementation 'com.github.FrankHeijden:MinecraftReflection:1.0.0' implementation 'com.google.guava:guava:30.1.1-jre' implementation 'com.google.code.gson:gson:2.8.6' From 8d3a85d472476c6cf888368f9d1f655b5f6f9449 Mon Sep 17 00:00:00 2001 From: Frank van der Heijden Date: Sat, 31 Jul 2021 21:05:03 +0200 Subject: [PATCH 6/6] Add multi plugin watcher --- .../common/commands/CommandServerUtils.java | 27 +-- .../common/entities/ServerUtilsPlugin.java | 6 + .../common/entities/results/WatchResult.java | 56 +++-- .../managers/AbstractPluginManager.java | 24 --- .../common/managers/WatchManager.java | 73 +++++++ .../common/tasks/PluginWatcherTask.java | 195 +++++++++++++----- Common/src/main/resources/commands.json | 13 +- Common/src/main/resources/messages.json | 9 +- 8 files changed, 300 insertions(+), 103 deletions(-) create mode 100644 Common/src/main/java/net/frankheijden/serverutils/common/managers/WatchManager.java diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java index 4e28500..b2ec42d 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java @@ -17,13 +17,13 @@ import net.frankheijden.serverutils.common.commands.arguments.PluginArgument; import net.frankheijden.serverutils.common.commands.arguments.PluginsArgument; import net.frankheijden.serverutils.common.config.MessagesResource; import net.frankheijden.serverutils.common.config.ServerUtilsConfig; -import net.frankheijden.serverutils.common.entities.results.AbstractResult; import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; import net.frankheijden.serverutils.common.entities.results.PluginResult; import net.frankheijden.serverutils.common.entities.results.PluginResults; import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; +import net.frankheijden.serverutils.common.entities.results.WatchResult; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.ListBuilder; @@ -73,7 +73,11 @@ public abstract class CommandServerUtils( + true, + "plugins", + new PluginsArgument.PluginsParser<>(plugin, arrayCreator, getRawPath("watchplugin")) + )) .handler(this::handleWatchPlugin)); manager.command(buildSubcommand(builder, "unwatchplugin") .argument(getArgument("plugin")) @@ -256,24 +260,23 @@ public abstract class CommandServerUtils context) { C sender = context.getSender(); - P pluginArg = context.get("plugin"); + List

plugins = Arrays.asList(context.get("plugins")); - AbstractPluginManager pluginManager = plugin.getPluginManager(); - String pluginId = pluginManager.getPluginId(pluginArg); + if (checkDependingPlugins(context, sender, plugins, "watchplugin")) { + return; + } - AbstractResult result = pluginManager.watchPlugin(sender, pluginId); - result.sendTo(sender, "watch", pluginId); + WatchResult result = plugin.getWatchManager().watchPlugins(sender, plugins); + result.sendTo(sender); } private void handleUnwatchPlugin(CommandContext context) { C sender = context.getSender(); P pluginArg = context.get("plugin"); - AbstractPluginManager pluginManager = plugin.getPluginManager(); - String pluginId = pluginManager.getPluginId(pluginArg); - - AbstractResult result = pluginManager.unwatchPlugin(pluginId); - result.sendTo(sender, "unwatch", pluginId); + String pluginId = plugin.getPluginManager().getPluginId(pluginArg); + WatchResult result = plugin.getWatchManager().unwatchPluginsAssociatedWith(pluginId); + result.sendTo(sender); } private void handlePluginInfo(CommandContext context) { diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java index 8965299..81ffffe 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/ServerUtilsPlugin.java @@ -17,6 +17,7 @@ import net.frankheijden.serverutils.common.config.MessagesResource; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import net.frankheijden.serverutils.common.managers.AbstractTaskManager; import net.frankheijden.serverutils.common.managers.UpdateManager; +import net.frankheijden.serverutils.common.managers.WatchManager; import net.frankheijden.serverutils.common.providers.ChatProvider; import net.frankheijden.serverutils.common.providers.ResourceProvider; import net.frankheijden.serverutils.common.utils.FileUtils; @@ -24,6 +25,7 @@ import net.frankheijden.serverutils.common.utils.FileUtils; public abstract class ServerUtilsPlugin, S, D extends ServerUtilsPluginDescription> { private final UpdateManager updateManager = new UpdateManager(); + private final WatchManager watchManager = new WatchManager<>(this); private CommandsResource commandsResource; private ConfigResource configResource; private MessagesResource messagesResource; @@ -57,6 +59,10 @@ public abstract class ServerUtilsPlugin, return updateManager; } + public WatchManager getWatchManager() { + return watchManager; + } + public abstract Logger getLogger(); public abstract File getDataFolder(); diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java index 787378d..091981d 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java @@ -1,28 +1,60 @@ package net.frankheijden.serverutils.common.entities.results; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import net.frankheijden.serverutils.common.ServerUtilsApp; +import net.frankheijden.serverutils.common.config.MessagesResource; import net.frankheijden.serverutils.common.entities.ServerCommandSender; public enum WatchResult implements AbstractResult { START, CHANGE, + ALREADY_WATCHING, NOT_WATCHING, + FILE_DELETED, + DELETED_FILE_IS_CREATED, STOPPED; - /** - * Retrieves the associated message of the result - * and sends it to a CommandSender. - * @param sender The receiver. - * @param action The action which let to the result. - * @param what An associated variable. - */ + private List args = null; + + public WatchResult arg(String arg) { + return args(Collections.singletonList(arg)); + } + + public WatchResult args(List args) { + this.args = args; + return this; + } + @Override public void sendTo(ServerCommandSender sender, String action, String what) { - ServerUtilsApp.getPlugin().getMessagesResource().sendMessage( - sender, - "serverutils.watcher." + this.name().toLowerCase(), - "%what%", what - ); + arg(what); + sendTo(sender); + } + + /** + * Sends the result(s) to the console and specified sender. + */ + public void sendTo(ServerCommandSender sender) { + String path = "serverutils.watchplugin." + this.name().toLowerCase(); + List sendArguments = new ArrayList<>(); + if (args == null || args.isEmpty()) { + sendArguments.add(new String[0]); + } else { + for (String what : args) { + sendArguments.add(new String[] { "%what%", what }); + } + } + + MessagesResource messages = ServerUtilsApp.getPlugin().getMessagesResource(); + ServerCommandSender console = ServerUtilsApp.getPlugin().getChatProvider().getConsoleSender(); + for (String[] replacements : sendArguments) { + messages.sendMessage(sender, path, replacements); + if (sender.isPlayer()) { + messages.sendMessage(console, path, replacements); + } + } } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java b/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java index 3468956..c5ef500 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/managers/AbstractPluginManager.java @@ -10,19 +10,14 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import net.frankheijden.serverutils.common.ServerUtilsApp; -import net.frankheijden.serverutils.common.entities.results.AbstractResult; import net.frankheijden.serverutils.common.entities.results.CloseablePluginResult; import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; import net.frankheijden.serverutils.common.entities.results.PluginResult; import net.frankheijden.serverutils.common.entities.results.PluginResults; import net.frankheijden.serverutils.common.entities.results.Result; -import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; -import net.frankheijden.serverutils.common.entities.results.WatchResult; import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; import net.frankheijden.serverutils.common.providers.PluginProvider; -import net.frankheijden.serverutils.common.tasks.PluginWatcherTask; import net.frankheijden.serverutils.common.utils.DependencyUtils; public abstract class AbstractPluginManager implements PluginProvider { @@ -294,23 +289,4 @@ public abstract class AbstractPluginManager sender, String pluginId) { - if (getPlugin(pluginId).isPresent()) return Result.NOT_EXISTS; - ServerUtilsApp.getPlugin().getTaskManager() - .runTaskAsynchronously(pluginId, new PluginWatcherTask(sender, pluginId)); - return WatchResult.START; - } - - /** - * Stops watching the plugin for changes. - */ - public AbstractResult unwatchPlugin(String pluginId) { - if (ServerUtilsApp.getPlugin().getTaskManager().cancelTask(pluginId)) return WatchResult.STOPPED; - return WatchResult.NOT_WATCHING; - } } diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/managers/WatchManager.java b/Common/src/main/java/net/frankheijden/serverutils/common/managers/WatchManager.java new file mode 100644 index 0000000..d9e53f3 --- /dev/null +++ b/Common/src/main/java/net/frankheijden/serverutils/common/managers/WatchManager.java @@ -0,0 +1,73 @@ +package net.frankheijden.serverutils.common.managers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; +import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; +import net.frankheijden.serverutils.common.entities.results.WatchResult; +import net.frankheijden.serverutils.common.tasks.PluginWatcherTask; + +public class WatchManager { + + private final ServerUtilsPlugin plugin; + private final Map watchTasks; + + public WatchManager(ServerUtilsPlugin plugin) { + this.plugin = plugin; + this.watchTasks = new HashMap<>(); + } + + /** + * Starts watching the specified plugin and reloads it when a change is detected. + */ + public WatchResult watchPlugins(ServerCommandSender sender, List

plugins) { + List pluginIds = new ArrayList<>(plugins.size()); + for (P watchPlugin : plugins) { + String pluginId = plugin.getPluginManager().getPluginId(watchPlugin); + if (watchTasks.containsKey(pluginId)) { + return WatchResult.ALREADY_WATCHING.arg(pluginId); + } + + pluginIds.add(plugin.getPluginManager().getPluginId(watchPlugin)); + } + + UUID key = UUID.randomUUID(); + plugin.getTaskManager().runTaskAsynchronously( + key.toString(), + new PluginWatcherTask<>(plugin, sender, plugins) + ); + + WatchTask watchTask = new WatchTask(key, pluginIds); + for (String pluginId : pluginIds) { + watchTasks.put(pluginId, watchTask); + } + + return WatchResult.START.args(pluginIds); + } + + /** + * Stops watching plugins for changes. + */ + public WatchResult unwatchPluginsAssociatedWith(String pluginId) { + WatchTask task = watchTasks.get(pluginId); + if (task != null && plugin.getTaskManager().cancelTask(task.key.toString())) { + task.pluginIds.forEach(watchTasks::remove); + return WatchResult.STOPPED.args(task.pluginIds); + } + return WatchResult.NOT_WATCHING.arg(pluginId); + } + + private static final class WatchTask { + + private final UUID key; + private final List pluginIds; + + private WatchTask(UUID key, List pluginIds) { + this.key = key; + this.pluginIds = pluginIds; + } + } +} diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java index 3401f3e..70e705f 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/tasks/PluginWatcherTask.java @@ -1,26 +1,34 @@ package net.frankheijden.serverutils.common.tasks; import com.sun.nio.file.SensitivityWatchEventModifier; -import net.frankheijden.serverutils.common.ServerUtilsApp; -import net.frankheijden.serverutils.common.entities.AbstractTask; -import net.frankheijden.serverutils.common.entities.ServerCommandSender; -import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; -import net.frankheijden.serverutils.common.entities.results.WatchResult; -import net.frankheijden.serverutils.common.managers.AbstractPluginManager; -import net.frankheijden.serverutils.common.managers.AbstractTaskManager; -import net.frankheijden.serverutils.common.providers.ChatProvider; -import net.frankheijden.serverutils.common.utils.FileUtils; import java.io.File; import java.io.IOException; import java.nio.file.ClosedWatchServiceException; import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import net.frankheijden.serverutils.common.entities.AbstractTask; +import net.frankheijden.serverutils.common.entities.ServerCommandSender; +import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; +import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; +import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; +import net.frankheijden.serverutils.common.entities.results.PluginResult; +import net.frankheijden.serverutils.common.entities.results.PluginResults; +import net.frankheijden.serverutils.common.entities.results.WatchResult; +import net.frankheijden.serverutils.common.managers.AbstractPluginManager; +import net.frankheijden.serverutils.common.utils.FileUtils; -public class PluginWatcherTask extends AbstractTask { +public class PluginWatcherTask extends AbstractTask { private static final WatchEvent.Kind[] EVENTS = new WatchEvent.Kind[]{ StandardWatchEventKinds.ENTRY_CREATE, @@ -28,32 +36,33 @@ public class PluginWatcherTask extends AbstractTask { StandardWatchEventKinds.ENTRY_DELETE }; - private final ServerUtilsPlugin plugin = ServerUtilsApp.getPlugin(); - private final AbstractPluginManager pluginManager = plugin.getPluginManager(); - private final ChatProvider chatProvider = plugin.getChatProvider(); - @SuppressWarnings("rawtypes") - private final AbstractTaskManager taskManager = plugin.getTaskManager(); - + private final ServerUtilsPlugin plugin; private final ServerCommandSender sender; - private final String pluginName; - private final AtomicBoolean run; - private File file; - private String hash; - private long hashTimestamp = 0; + private final Map fileNameToWatchEntryMap; + private final Map pluginIdToWatchEntryMap; + private final AtomicBoolean run = new AtomicBoolean(true); private WatchService watchService; - private Object task = null; + private T task = null; /** * Constructs a new PluginWatcherTask for the specified plugin. - * - * @param pluginName The name of the plugin. */ - public PluginWatcherTask(ServerCommandSender sender, String pluginName) { + public PluginWatcherTask(ServerUtilsPlugin plugin, ServerCommandSender sender, List

plugins) { + this.plugin = plugin; this.sender = sender; - this.pluginName = pluginName; - this.file = pluginManager.getPluginFile(pluginName).orElse(null); - this.run = new AtomicBoolean(true); + this.fileNameToWatchEntryMap = new HashMap<>(); + this.pluginIdToWatchEntryMap = new HashMap<>(); + + AbstractPluginManager pluginManager = plugin.getPluginManager(); + for (P watchPlugin : plugins) { + File file = pluginManager.getPluginFile(watchPlugin); + + WatchEntry entry = new WatchEntry(pluginManager.getPluginId(watchPlugin)); + entry.update(file); + + this.fileNameToWatchEntryMap.put(file.getName(), entry); + } } @Override @@ -61,34 +70,21 @@ public class PluginWatcherTask extends AbstractTask { try (WatchService watchService = FileSystems.getDefault().newWatchService()) { this.watchService = watchService; - File folder = pluginManager.getPluginsFolder(); - folder.toPath().register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH); + AbstractPluginManager pluginManager = plugin.getPluginManager(); + Path basePath = pluginManager.getPluginsFolder().toPath(); + basePath.register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH); while (run.get()) { WatchKey key = watchService.take(); for (WatchEvent event : key.pollEvents()) { - if (file.getName().equals(event.context().toString())) { - if (task != null) { - taskManager.cancelTask(task); - } + Path path = basePath.resolve((Path) event.context()); - String previousHash = hash; - long previousHashTimestamp = hashTimestamp; - - hash = FileUtils.getHash(file.toPath()); - hashTimestamp = System.currentTimeMillis(); - task = ServerUtilsApp.getPlugin().getTaskManager().runTaskLater(() -> { - if (hash.equals(previousHash) || previousHashTimestamp < hashTimestamp - 1000L) { - send(WatchResult.CHANGE); - - pluginManager.reloadPlugin(pluginName); - file = pluginManager.getPluginFile(pluginName).orElse(null); - } - }, 10L); + if (!Files.isDirectory(path)) { + handleWatchEvent(path); } } - if (file == null || !key.reset()) { + if ((fileNameToWatchEntryMap.isEmpty() && pluginIdToWatchEntryMap.isEmpty()) || !key.reset()) { send(WatchResult.STOPPED); break; } @@ -102,10 +98,93 @@ public class PluginWatcherTask extends AbstractTask { } } + private void handleWatchEvent(Path path) { + String fileName = path.getFileName().toString(); + WatchEntry entry = fileNameToWatchEntryMap.get(fileName); + + if (entry == null && Files.exists(path)) { + Optional descriptionOptional; + try { + descriptionOptional = plugin.getPluginManager().getPluginDescription(path.toFile()); + } catch (InvalidPluginDescriptionException ignored) { + return; + } + + if (descriptionOptional.isPresent()) { + ServerUtilsPluginDescription description = descriptionOptional.get(); + WatchEntry foundEntry = pluginIdToWatchEntryMap.remove(description.getId()); + if (foundEntry != null) { + send(WatchResult.DELETED_FILE_IS_CREATED.arg(foundEntry.pluginId)); + fileNameToWatchEntryMap.put(fileName, foundEntry); + + if (pluginIdToWatchEntryMap.isEmpty()) { + entry = foundEntry; + } + } + } + } + + if (entry != null) { + checkWatchEntry(entry, fileName); + } + } + + private void checkWatchEntry(WatchEntry entry, String fileName) { + if (task != null) { + plugin.getTaskManager().cancelTask(task); + } + + AbstractPluginManager pluginManager = plugin.getPluginManager(); + Optional fileOptional = pluginManager.getPluginFile(entry.pluginId); + if (!fileOptional.isPresent()) { + send(WatchResult.FILE_DELETED.arg(entry.pluginId)); + + fileNameToWatchEntryMap.remove(fileName); + pluginIdToWatchEntryMap.put(entry.pluginId, entry); + return; + } + + String previousHash = entry.hash; + long previousTimestamp = entry.timestamp; + entry.update(fileOptional.get()); + + task = plugin.getTaskManager().runTaskLater(() -> { + if (entry.hash.equals(previousHash) || previousTimestamp < entry.timestamp - 1000L) { + send(WatchResult.CHANGE); + + List

plugins = new ArrayList<>(fileNameToWatchEntryMap.size()); + Map retainedWatchEntries = new HashMap<>(); + for (WatchEntry oldEntry : fileNameToWatchEntryMap.values()) { + Optional

pluginOptional = pluginManager.getPlugin(oldEntry.pluginId); + if (!pluginOptional.isPresent()) continue; + + plugins.add(pluginOptional.get()); + retainedWatchEntries.put(oldEntry.pluginId, oldEntry); + } + + fileNameToWatchEntryMap.clear(); + + PluginResults

reloadResults = pluginManager.reloadPlugins(plugins); + reloadResults.sendTo(sender, "reload"); + + for (PluginResult

reloadResult : reloadResults) { + if (!reloadResult.isSuccess()) continue; + + P reloadedPlugin = reloadResult.getPlugin(); + String pluginId = pluginManager.getPluginId(reloadedPlugin); + + WatchEntry retainedEntry = retainedWatchEntries.get(pluginId); + String pluginFileName = pluginManager.getPluginFile(reloadedPlugin).getName(); + fileNameToWatchEntryMap.put(pluginFileName, retainedEntry); + } + } + }, 10L); + } + private void send(WatchResult result) { - result.sendTo(sender, null, pluginName); + result.sendTo(sender); if (sender.isPlayer()) { - result.sendTo(chatProvider.getConsoleSender(), null, pluginName); + result.sendTo(plugin.getChatProvider().getConsoleSender()); } } @@ -118,4 +197,20 @@ public class PluginWatcherTask extends AbstractTask { ex.printStackTrace(); } } + + private static final class WatchEntry { + + private final String pluginId; + private String hash = null; + private long timestamp = 0L; + + public WatchEntry(String pluginId) { + this.pluginId = pluginId; + } + + public void update(File file) { + this.hash = FileUtils.getHash(file.toPath()); + this.timestamp = System.currentTimeMillis(); + } + } } diff --git a/Common/src/main/resources/commands.json b/Common/src/main/resources/commands.json index 7357d8d..ffecd21 100644 --- a/Common/src/main/resources/commands.json +++ b/Common/src/main/resources/commands.json @@ -80,8 +80,17 @@ "main": "watchplugin", "aliases": ["wp"], "permission": "serverutils.watchplugin", - "description": "Watches the specified plugin for changes.", - "display-in-help": true + "description": "Watches the specified plugin(s) for changes.", + "display-in-help": true, + "flags": { + "force": { + "main": "force", + "aliases": ["f"], + "permission": "serverutils.watchplugin", + "description": "Force watches the specified plugin(s) for changes.", + "display-in-help": false + } + } }, "unwatchplugin": { "main": "unwatchplugin", diff --git a/Common/src/main/resources/messages.json b/Common/src/main/resources/messages.json index b2a02ce..1cf76d5 100644 --- a/Common/src/main/resources/messages.json +++ b/Common/src/main/resources/messages.json @@ -20,11 +20,14 @@ "last_separator": " &cand ", "override": "&cUse \"&4/%command%&c\" to force command execution." }, - "watcher": { + "watchplugin": { "start": "&3Started watching &b%what%&3!", - "change": "&3Change detected for plugin &b%what%&3, reloading now...", + "change": "&3Changes detected, reloading plugins now...", "stopped": "&3Stopped watching &b%what%&3!", - "not_watching": "&cWe aren't watching that plugin!" + "file_deleted": "&cPlugin file &4%what%&c has been deleted! Waiting for plugin to show up...", + "deleted_file_is_created": "&3Plugin file &b%what%&3 has shown up again!", + "already_watching": "&cYou are already watching &4%what%&c!", + "not_watching": "&cYou are not watching &4%what%&c!" }, "update": { "available": "&8&m------------=&r&8[ &b&lServerUtils Update&r &8]&m=--------------\n &3Current version: &b%old%\n &3New version: &b%new%\n &3Release info: &b%info%\n&8&m-------------------------------------------------",