Add initial multi plugin management

This commit is contained in:
Frank van der Heijden 2021-07-29 15:08:42 +02:00
parent df55162d73
commit 94e4693b5e
No known key found for this signature in database
GPG key ID: B808721C2DD5B5B8
54 changed files with 1988 additions and 937 deletions

View file

@ -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<Plugin> 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;
}

View file

@ -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<BukkitPlugin, P
}
public BukkitCommandServerUtils(BukkitPlugin plugin) {
super(plugin);
super(plugin, Plugin[]::new);
}
@Override
@ -72,10 +73,10 @@ public class BukkitCommandServerUtils extends CommandServerUtils<BukkitPlugin, P
.build());
manager.command(buildSubcommand(builder, "enableplugin")
.argument(getArgument("plugin"))
.argument(getArgument("plugins"))
.handler(this::handleEnablePlugin));
manager.command(buildSubcommand(builder, "disableplugin")
.argument(getArgument("plugin"))
.argument(getArgument("plugins"))
.handler(this::handleDisablePlugin));
manager.command(buildSubcommand(builder, "reloadconfig")
.argument(getArgument("config"))
@ -84,18 +85,18 @@ public class BukkitCommandServerUtils extends CommandServerUtils<BukkitPlugin, P
private void handleEnablePlugin(CommandContext<BukkitCommandSender> context) {
BukkitCommandSender sender = context.getSender();
String pluginName = context.get("plugin");
List<Plugin> plugins = Arrays.asList(context.get("plugins"));
Result result = plugin.getPluginManager().enablePlugin(pluginName);
result.sendTo(sender, "enabl", pluginName);
PluginResults<Plugin> result = plugin.getPluginManager().enablePlugins(plugins);
result.sendTo(sender, "enabl");
}
private void handleDisablePlugin(CommandContext<BukkitCommandSender> context) {
BukkitCommandSender sender = context.getSender();
String pluginName = context.get("plugin");
List<Plugin> plugins = Arrays.asList(context.get("plugins"));
Result result = plugin.getPluginManager().disablePlugin(pluginName);
result.sendTo(sender, "disabl", pluginName);
PluginResults<Plugin> result = plugin.getPluginManager().disablePlugins(plugins);
result.sendTo(sender, "disabl");
}
private void handleReloadConfig(CommandContext<BukkitCommandSender> context) {
@ -148,12 +149,11 @@ public class BukkitCommandServerUtils extends CommandServerUtils<BukkitPlugin, P
protected FormatBuilder createPluginInfo(
FormatBuilder builder,
Function<Consumer<ListBuilder<String>>, 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())

View file

@ -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<Plugin> {
private BukkitLoadResult(Plugin obj, Result result) {
super(obj, result);
}
public BukkitLoadResult(Plugin obj) {
this(obj, Result.SUCCESS);
}
public BukkitLoadResult(Result result) {
this(null, result);
}
}

View file

@ -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<Plugin, BukkitTask, BukkitCommandSender, CommandSender, BukkitPluginDescription> {
private final ServerUtils plugin;
private final BukkitPluginManager pluginManager;
@ -45,8 +41,9 @@ public class BukkitPlugin extends ServerUtilsPlugin<
@Override
protected PaperCommandManager<BukkitCommandSender> newCommandManager() {
PaperCommandManager<BukkitCommandSender> 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

View file

@ -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<String> 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<String> getDependencies() {
return this.dependencies;
}
public PluginDescriptionFile getDescriptionFile() {
return descriptionFile;
}
}

View file

@ -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<Plugin> {
public class BukkitPluginManager extends AbstractPluginManager<Plugin, BukkitPluginDescription> {
private static BukkitPluginManager instance;
@ -51,122 +54,101 @@ public class BukkitPluginManager implements AbstractPluginManager<Plugin> {
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<Plugin> loadPluginDescriptions(List<BukkitPluginDescription> descriptions) {
PluginResults<Plugin> 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<Plugin> disableOrderedPlugins(List<Plugin> plugins) {
PluginResults<Plugin> 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<Plugin> unloadOrderedPlugins(List<Plugin> plugins) {
CloseablePluginResults<Plugin> 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<Closeable> 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<String, Class<?>> 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<Closeable> 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<String, Class<?>> 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<Closeable> list, Object obj) {
@ -175,68 +157,29 @@ public class BukkitPluginManager implements AbstractPluginManager<Plugin> {
}
}
/**
* 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<Plugin> enableOrderedPlugins(List<Plugin> plugins) {
PluginResults<Plugin> 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<Plugin> {
* @param file The file.
* @return The appropiate PluginLoader.
*/
public static PluginLoader getPluginLoader(File file) {
public static Optional<PluginLoader> getPluginLoader(File file) {
Map<Pattern, PluginLoader> 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<Pattern, PluginLoader> 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<BukkitPluginDescription> getPluginDescription(File file) throws InvalidPluginDescriptionException {
if (!file.exists()) return Optional.empty();
Optional<PluginLoader> 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<Plugin> {
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<Plugin> 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<Plugin> {
}
@Override
public String getPluginName(Plugin plugin) {
public String getPluginId(Plugin plugin) {
return plugin.getName();
}
}

View file

@ -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": {

View file

@ -19,17 +19,16 @@ import net.md_5.bungee.api.plugin.PluginManager;
public class BungeeCommandServerUtils extends CommandServerUtils<BungeePlugin, Plugin, BungeeCommandSender> {
public BungeeCommandServerUtils(BungeePlugin plugin) {
super(plugin);
super(plugin, Plugin[]::new);
}
@Override
protected FormatBuilder createPluginInfo(
FormatBuilder builder,
Function<Consumer<ListBuilder<String>>, 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())

View file

@ -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<Plugin> {
public BungeeLoadResult(Plugin obj, Result result) {
super(obj, result);
}
public BungeeLoadResult(Plugin obj) {
super(obj);
}
public BungeeLoadResult(Result result) {
super(result);
}
}

View file

@ -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<Plugin, ScheduledTask, BungeeCommandSender, CommandSender, BungeePluginDescription> {
private final ServerUtils plugin;
private final BungeePluginManager pluginManager;

View file

@ -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<String> 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<String> getDependencies() {
return this.dependencies;
}
public PluginDescription getDescription() {
return description;
}
}

View file

@ -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<Plugin> {
public class BungeePluginManager extends AbstractPluginManager<Plugin, BungeePluginDescription> {
private static final ProxyServer proxy = ProxyServer.getInstance();
@ -65,128 +69,123 @@ public class BungeePluginManager implements AbstractPluginManager<Plugin> {
}
@Override
public BungeeLoadResult loadPlugin(String pluginFile) {
File file = getPluginFileExact(pluginFile);
if (!file.exists()) return new BungeeLoadResult(Result.NOT_EXISTS);
return loadPlugin(file);
}
public PluginResults<Plugin> loadPluginDescriptions(List<BungeePluginDescription> descriptions) {
PluginResults<Plugin> 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<Plugin> enableOrderedPlugins(List<Plugin> plugins) {
PluginResults<Plugin> 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<Plugin> disableOrderedPlugins(List<Plugin> plugins) {
PluginResults<Plugin> 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<Plugin> unloadOrderedPlugins(List<Plugin> plugins) {
CloseablePluginResults<Plugin> 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<Closeable> 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<Closeable> 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<Closeable> list, Object obj) {
@ -195,38 +194,31 @@ public class BungeePluginManager implements AbstractPluginManager<Plugin> {
}
}
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<File> getPluginFile(String pluginId) {
for (File file : getPluginJars()) {
PluginDescription desc;
BungeePluginDescription description;
try {
desc = getPluginDescription(file);
Optional<BungeePluginDescription> 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<Plugin> 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<Plugin> {
.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<BungeePluginDescription> 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<Plugin> {
.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<Plugin> {
*/
public List<Plugin> getPluginsSorted(boolean modules) {
List<Plugin> plugins = getPlugins(modules);
plugins.sort(Comparator.comparing(this::getPluginName));
plugins.sort(Comparator.comparing(this::getPluginId));
return plugins;
}
}

View file

@ -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'
}

View file

@ -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<U extends ServerUtilsPlugin<P, T, C, S>, P, T, C extends ServerCommandSender<S>, S> {
public class ServerUtilsApp<U extends ServerUtilsPlugin<P, T, C, S, D>, P, T, C extends ServerCommandSender<S>, 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<U extends ServerUtilsPlugin<P, T, C, S>, P, T, C ext
this.plugin = plugin;
}
public static <U extends ServerUtilsPlugin<P, T, C, S>, P, T, C extends ServerCommandSender<S>, S> void init(
public static <
U extends ServerUtilsPlugin<P, T, C, S, D>,
P,
T,
C extends ServerCommandSender<S>,
S,
D extends ServerUtilsPluginDescription
> void init(
Object platformPlugin,
U plugin
) {
@ -42,30 +51,31 @@ public class ServerUtilsApp<U extends ServerUtilsPlugin<P, T, C, S>, P, T, C ext
/**
* Unloads the ServerUtilsUpdater and deletes the file.
*/
public static <U extends ServerUtilsPlugin<P, T, C, S>, P, T, C extends ServerCommandSender<S>, S>
void unloadServerUtilsUpdater() {
U plugin = getPlugin();
public static <P> void unloadServerUtilsUpdater() {
ServerUtilsPlugin<P, ?, ?, ?, ?> 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<P> 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<P> 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<P> 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<U extends ServerUtilsPlugin<P, T, C, S>, P, T, C ext
}
@SuppressWarnings("unchecked")
public static <U extends ServerUtilsPlugin<P, T, C, S>, P, T, C extends ServerCommandSender<S>, S>
U getPlugin() {
public static <
U extends ServerUtilsPlugin<P, T, C, S, D>,
P,
T,
C extends ServerCommandSender<S>,
S,
D extends ServerUtilsPluginDescription
> U getPlugin() {
return (U) instance.plugin;
}
}

View file

@ -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<U extends ServerUtilsPlugin<P, ?, C, ?>, P, C extends ServerCommandSender<?>>
public abstract class CommandPlugins<U extends ServerUtilsPlugin<P, ?, C, ?, ?>, P, C extends ServerCommandSender<?>>
extends ServerUtilsCommand<U, C> {
protected CommandPlugins(U plugin) {

View file

@ -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<U extends ServerUtilsPlugin<P, ?, C, ?>, P, C extends ServerCommandSender<?>>
public abstract class CommandServerUtils<U extends ServerUtilsPlugin<P, ?, C, ?, ?>, P, C extends ServerCommandSender<?>>
extends ServerUtilsCommand<U, C> {
protected CommandServerUtils(U plugin) {
private final IntFunction<P[]> arrayCreator;
protected CommandServerUtils(U plugin, IntFunction<P[]> arrayCreator) {
super(plugin, "serverutils");
this.arrayCreator = arrayCreator;
}
@Override
public void register(CommandManager<C> manager, Command.Builder<C> builder) {
addArgument(CommandArgument.<C, String>ofType(String.class, "jarFile")
.manager(manager)
.withSuggestionsProvider((context, s) -> plugin.getPluginManager().getPluginFileNames())
.build());
addArgument(CommandArgument.<C, String>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.<C, String>ofType(String.class, "command")
.manager(manager)
.withSuggestionsProvider((context, s) -> new ArrayList<>(plugin.getPluginManager().getCommands()))
@ -48,13 +54,13 @@ public abstract class CommandServerUtils<U extends ServerUtilsPlugin<P, ?, C, ?>
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<U extends ServerUtilsPlugin<P, ?, C, ?>
private void handleLoadPlugin(CommandContext<C> context) {
C sender = context.getSender();
String jarFile = context.get("jarFile");
List<File> jarFiles = Arrays.asList(context.get("jarFiles"));
AbstractPluginManager<P> pluginManager = plugin.getPluginManager();
LoadResult<P> loadResult = pluginManager.loadPlugin(jarFile);
if (!loadResult.isSuccess()) {
loadResult.getResult().sendTo(sender, "load", jarFile);
AbstractPluginManager<P, ?> pluginManager = plugin.getPluginManager();
PluginResults<P> loadResults = pluginManager.loadPlugins(jarFiles);
if (!loadResults.isSuccess()) {
PluginResult<P> 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<P> enableResults = pluginManager.enablePlugins(loadResults.getPlugins());
enableResults.sendTo(sender, "load");
}
private void handleUnloadPlugin(CommandContext<C> context) {
C sender = context.getSender();
String pluginName = context.get("plugin");
List<P> plugins = Arrays.asList(context.get("plugins"));
CloseableResult result = plugin.getPluginManager().unloadPlugin(pluginName);
result.getResult().sendTo(sender, "unload", pluginName);
result.tryClose();
PluginResults<P> disableResults = plugin.getPluginManager().disablePlugins(plugins);
for (PluginResult<P> disableResult : disableResults.getResults()) {
if (!disableResult.isSuccess() && disableResult.getResult() != Result.ALREADY_DISABLED) {
disableResult.getResult().sendTo(sender, "disabl", disableResult.getPluginId());
return;
}
}
CloseablePluginResults<P> unloadResults = plugin.getPluginManager().unloadPlugins(plugins);
unloadResults.tryClose();
unloadResults.sendTo(sender, "unload");
}
private void handleReloadPlugin(CommandContext<C> context) {
C sender = context.getSender();
String pluginName = context.get("plugin");
List<P> plugins = Arrays.asList(context.get("plugins"));
Result result = plugin.getPluginManager().reloadPlugin(pluginName);
result.sendTo(sender, "reload", pluginName);
PluginResults<P> reloadResult = plugin.getPluginManager().reloadPlugins(plugins);
reloadResult.sendTo(sender, "reload");
}
private void handleWatchPlugin(CommandContext<C> 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<P, ?> pluginManager = plugin.getPluginManager();
String pluginId = pluginManager.getPluginId(pluginArg);
AbstractResult result = pluginManager.watchPlugin(sender, pluginId);
result.sendTo(sender, "watch", pluginId);
}
private void handleUnwatchPlugin(CommandContext<C> 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<P, ?> pluginManager = plugin.getPluginManager();
String pluginId = pluginManager.getPluginId(pluginArg);
AbstractResult result = pluginManager.unwatchPlugin(pluginId);
result.sendTo(sender, "unwatch", pluginId);
}
private void handlePluginInfo(CommandContext<C> 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<Consumer<ListBuilder<String>>, String> listBuilderFunction,
String pluginName
P pluginArg
);
private void handleCommandInfo(CommandContext<C> context) {
@ -229,7 +244,7 @@ public abstract class CommandServerUtils<U extends ServerUtilsPlugin<P, ?, C, ?>
String commandName
);
private void createInfo(C sender, String command, String item, InfoCreator creator) {
private <T> void createInfo(C sender, String command, T item, InfoCreator<T> 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<U extends ServerUtilsPlugin<P, ?, C, ?>
plugin.getMessagesResource().sendMessage(sender, messagePrefix + ".footer");
}
private interface InfoCreator {
private interface InfoCreator<T> {
FormatBuilder createInfo(
FormatBuilder builder,
Function<Consumer<ListBuilder<String>>, String> listBuilderFunction,
String item
T item
);
}
}

View file

@ -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<U extends ServerUtilsPlugin<?, ?, C, ?>, C extends ServerCommandSender<?>> {
public abstract class ServerUtilsCommand<U extends ServerUtilsPlugin<?, ?, C, ?, ?>, C extends ServerCommandSender<?>> {
protected final U plugin;
protected final String commandName;

View file

@ -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<C extends ServerCommandSender<?>> extends CommandArgument<C, File[]> {
/**
* Constructs a Jar Files argument.
*/
public JarFilesArgument(boolean required, String name, ServerUtilsPlugin<?, ?, C, ?, ?> plugin) {
super(
required,
name,
new JarFilesParser<>(plugin),
"",
new TypeToken<File[]>() {},
null
);
}
public static final class JarFilesParser<C extends ServerCommandSender<?>> implements ArgumentParser<C, File[]> {
private final ServerUtilsPlugin<?, ?, C, ?, ?> plugin;
public JarFilesParser(ServerUtilsPlugin<?, ?, C, ?, ?> plugin) {
this.plugin = plugin;
}
@Override
public ArgumentParseResult<File[]> parse(CommandContext<C> context, Queue<String> inputQueue) {
if (inputQueue.isEmpty()) {
return ArgumentParseResult.failure(new NoInputProvidedException(JarFilesParser.class, context));
}
Set<String> 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<String> suggestions(CommandContext<C> context, String input) {
return plugin.getPluginManager().getPluginFileNames();
}
@Override
public boolean isContextFree() {
return true;
}
}
}

View file

@ -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<C extends ServerCommandSender<?>, P> extends CommandArgument<C, P> {
/**
* Constructs a Plugin argument.
*/
public PluginArgument(boolean required, String name, ServerUtilsPlugin<P, ?, C, ?, ?> plugin) {
super(
required,
name,
new PluginParser<>(plugin),
"",
new TypeToken<P>() {},
null
);
}
public static final class PluginParser<C extends ServerCommandSender<?>, P> implements ArgumentParser<C, P> {
private final ServerUtilsPlugin<P, ?, C, ?, ?> plugin;
public PluginParser(ServerUtilsPlugin<P, ?, C, ?, ?> plugin) {
this.plugin = plugin;
}
@Override
public ArgumentParseResult<P> parse(CommandContext<C> context, Queue<String> inputQueue) {
if (inputQueue.isEmpty()) {
return ArgumentParseResult.failure(new NoInputProvidedException(PluginParser.class, context));
}
Optional<P> 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<String> suggestions(CommandContext<C> context, String input) {
return plugin.getPluginManager().getPluginNames();
}
@Override
public boolean isContextFree() {
return true;
}
}
}

View file

@ -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<C extends ServerCommandSender<?>, P> extends CommandArgument<C, P[]> {
/**
* Constructs a Plugins argument.
*/
public PluginsArgument(
boolean required,
String name,
ServerUtilsPlugin<P, ?, C, ?, ?> plugin,
IntFunction<P[]> arrayCreator
) {
super(
required,
name,
new PluginsParser<>(plugin, arrayCreator),
"",
new TypeToken<P[]>() {},
null
);
}
public static final class PluginsParser<C extends ServerCommandSender<?>, P> implements ArgumentParser<C, P[]> {
private final ServerUtilsPlugin<P, ?, C, ?, ?> plugin;
private final IntFunction<P[]> arrayCreator;
public PluginsParser(ServerUtilsPlugin<P, ?, C, ?, ?> plugin, IntFunction<P[]> arrayCreator) {
this.plugin = plugin;
this.arrayCreator = arrayCreator;
}
@Override
public ArgumentParseResult<P[]> parse(CommandContext<C> context, Queue<String> 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<P> 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<String> suggestions(CommandContext<C> context, String input) {
return plugin.getPluginManager().getPluginNames();
}
@Override
public boolean isContextFree() {
return true;
}
}
}

View file

@ -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<C extends ServerCommandSender<?>, P> {
private final CloudBrigadierManager<C, ?> brigadierManager;
public BrigadierHandler(CloudBrigadierManager<C, ?> brigadierManager) {
this.brigadierManager = brigadierManager;
}
/**
* Registers types with the cloud brigadier manager.
*/
public void registerTypes() {
brigadierManager.registerMapping(
new TypeToken<JarFilesArgument.JarFilesParser<C>>() {},
builder -> builder.toConstant(StringArgumentType.greedyString())
);
brigadierManager.registerMapping(
new TypeToken<PluginsArgument.PluginsParser<C, P>>() {},
builder -> builder.toConstant(StringArgumentType.greedyString())
);
}
}

View file

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

View file

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

View file

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

View file

@ -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(

View file

@ -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<Closeable> 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<Closeable> 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<Closeable> 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();
}
}
}

View file

@ -1,61 +0,0 @@
package net.frankheijden.serverutils.common.entities;
/**
* A result which contains a loaded object from a load operation.
* @param <T> The loaded object type
*/
public class LoadResult<T> {
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;
}
}

View file

@ -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<P, T, C extends ServerCommandSender<S>, S> {
public abstract class ServerUtilsPlugin<P, T, C extends ServerCommandSender<S>, S, D extends ServerUtilsPluginDescription> {
private final UpdateManager updateManager = new UpdateManager();
private CommandsResource commandsResource;
@ -43,7 +45,7 @@ public abstract class ServerUtilsPlugin<P, T, C extends ServerCommandSender<S>,
return messagesResource;
}
public abstract AbstractPluginManager<P> getPluginManager();
public abstract AbstractPluginManager<P, D> getPluginManager();
public abstract AbstractTaskManager<T> getTaskManager();
@ -91,6 +93,11 @@ public abstract class ServerUtilsPlugin<P, T, C extends ServerCommandSender<S>,
protected abstract CommandManager<C> newCommandManager();
protected void handleBrigadier(CloudBrigadierManager<C, ?> brigadierManager) {
BrigadierHandler<C, P> handler = new BrigadierHandler<>(brigadierManager);
handler.registerTypes();
}
/**
* Enables the plugin.
*/

View file

@ -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<String> getDependencies();
}

View file

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

View file

@ -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 {

View file

@ -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<T> extends PluginResult<T> implements Closeable {
private final List<Closeable> closeables;
public CloseablePluginResult(String pluginId, Result result) {
super(pluginId, result);
this.closeables = Collections.emptyList();
}
public CloseablePluginResult(String pluginId, T plugin, Result result, List<Closeable> 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();
}
}
}

View file

@ -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<T> extends PluginResults<T> implements Closeable {
@Override
public CloseablePluginResults<T> addResult(PluginResult<T> pluginResult) {
if (!(pluginResult instanceof CloseablePluginResult)) {
throw new IllegalArgumentException("Not an instance of CloseablePluginResult: " + pluginResult);
}
results.add(pluginResult);
return this;
}
@Override
public CloseablePluginResults<T> addResult(String pluginId, Result result) {
super.addResult(pluginId, result);
return this;
}
@Override
public CloseablePluginResults<T> addResult(String pluginId, T plugin) {
super.addResult(pluginId, plugin);
return this;
}
@Override
protected CloseablePluginResults<T> addResult(String pluginId, T plugin, Result result) {
super.addResult(pluginId, plugin, result);
return this;
}
public CloseablePluginResults<T> addResult(String pluginId, T plugin, List<Closeable> closeables) {
return addResult(new CloseablePluginResult<>(pluginId, plugin, Result.SUCCESS, closeables));
}
@Override
public CloseablePluginResult<T> first() {
PluginResult<T> pluginResult = super.first();
if (!(pluginResult instanceof CloseablePluginResult)) {
throw new IllegalArgumentException("Not an instance of CloseablePluginResult: " + pluginResult);
}
return (CloseablePluginResult<T>) pluginResult;
}
@Override
public CloseablePluginResult<T> last() {
PluginResult<T> pluginResult = super.last();
if (!(pluginResult instanceof CloseablePluginResult)) {
throw new IllegalArgumentException("Not an instance of CloseablePluginResult: " + pluginResult);
}
return (CloseablePluginResult<T>) pluginResult;
}
/**
* Attempts to close the {@link CloseablePluginResult}'s enclosed.
*/
public void tryClose() {
try {
for (PluginResult<T> pluginResult : this) {
if (pluginResult instanceof CloseablePluginResult) {
((CloseablePluginResult<T>) pluginResult).close();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void close() throws IOException {
for (PluginResult<T> pluginResult : this) {
if (pluginResult instanceof CloseablePluginResult) {
((CloseablePluginResult<T>) pluginResult).close();
}
}
}
}

View file

@ -0,0 +1,37 @@
package net.frankheijden.serverutils.common.entities.results;
public class PluginResult<T> {
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;
}
}

View file

@ -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<T> implements Iterable<PluginResult<T>> {
protected final List<PluginResult<T>> results;
public PluginResults() {
this.results = new ArrayList<>();
}
public PluginResults<T> addResult(String pluginId, Result result) {
addResult(pluginId, null, result);
return this;
}
public PluginResults<T> addResult(String pluginId, T plugin) {
addResult(pluginId, plugin, Result.SUCCESS);
return this;
}
protected PluginResults<T> addResult(String pluginId, T plugin, Result result) {
addResult(new PluginResult<>(pluginId, plugin, result));
return this;
}
public PluginResults<T> addResult(PluginResult<T> pluginResult) {
this.results.add(pluginResult);
return this;
}
public boolean isSuccess() {
return results.stream().allMatch(PluginResult::isSuccess);
}
public List<PluginResult<T>> getResults() {
return results;
}
/**
* Creates an array of all plugins.
* @throws IllegalArgumentException Iff a result was not successful (check {@link PluginResults#isSuccess()} first!)
*/
public List<T> getPlugins() {
List<T> plugins = new ArrayList<>(results.size());
for (PluginResult<T> 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<T> first() {
return results.get(0);
}
public PluginResult<T> last() {
return results.get(results.size() - 1);
}
/**
* Sends the results to given receiver.
*/
public void sendTo(ServerCommandSender<?> sender, String action) {
for (PluginResult<T> result : results) {
result.getResult().sendTo(sender, action, result.getPluginId());
}
}
@Override
public Iterator<PluginResult<T>> iterator() {
return results.iterator();
}
}

View file

@ -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.

View file

@ -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 {

View file

@ -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<U extends ServerUtilsPlugin<P, ?, C, ?>, P, C extends ServerCommandSender<?>>
public abstract class PlayerListener<U extends ServerUtilsPlugin<P, ?, C, ?, ?>, P, C extends ServerCommandSender<?>>
extends ServerUtilsListener<U, C> {
protected PlayerListener(U plugin) {

View file

@ -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<U extends ServerUtilsPlugin<?, ?, C, ?>, C extends ServerCommandSender<?>> {
public abstract class ServerUtilsListener<U extends ServerUtilsPlugin<?, ?, C, ?, ?>, C extends ServerCommandSender<?>> {
protected final U plugin;

View file

@ -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<P> extends PluginProvider<P> {
public abstract class AbstractPluginManager<P, D extends ServerUtilsPluginDescription> implements PluginProvider<P, D> {
LoadResult<P> loadPlugin(String pluginFile);
/**
* Loads the given plugin by their jar file.
*/
public PluginResult<P> loadPlugin(String pluginFile) {
File file = new File(getPluginsFolder(), pluginFile);
if (!file.exists()) return new PluginResult<>(pluginFile, Result.NOT_EXISTS);
return loadPlugin(file);
}
LoadResult<P> loadPlugin(File file);
public PluginResult<P> loadPlugin(File file) {
return loadPlugins(Collections.singletonList(file)).first();
}
Result enablePlugin(P plugin);
/**
* Loads a list of files as plugins.
*/
public PluginResults<P> loadPlugins(List<File> files) {
List<D> descriptions = new ArrayList<>(files.size());
Result disablePlugin(P plugin);
for (File file : files) {
D description;
try {
Optional<D> descriptionOptional = getPluginDescription(file);
if (!descriptionOptional.isPresent()) {
return new PluginResults<P>().addResult(file.getName(), Result.NOT_EXISTS);
}
Result reloadPlugin(String pluginName);
description = descriptionOptional.get();
} catch (InvalidPluginDescriptionException ex) {
return new PluginResults<P>().addResult(file.getName(), Result.INVALID_DESCRIPTION);
}
Result reloadPlugin(P plugin);
if (getPlugin(description.getId()).isPresent()) {
return new PluginResults<P>().addResult(description.getId(), Result.ALREADY_LOADED);
}
CloseableResult unloadPlugin(String pluginName);
descriptions.add(description);
}
CloseableResult unloadPlugin(P plugin);
List<D> 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<P>().addResult(sb.substring(2), Result.ERROR);
}
return loadPluginDescriptions(orderedDescriptions);
}
protected abstract PluginResults<P> loadPluginDescriptions(List<D> descriptions);
/**
* Enables the given plugin by name.
*/
public PluginResult<P> enablePlugin(String pluginId) {
return getPlugin(pluginId)
.map(this::enablePlugin)
.orElse(new PluginResult<>(pluginId, Result.NOT_EXISTS));
}
public PluginResult<P> enablePlugin(P plugin) {
return enablePlugins(Collections.singletonList(plugin)).first();
}
/**
* Enables a list of plugins.
*/
public PluginResults<P> enablePlugins(List<P> plugins) {
for (P plugin : plugins) {
String pluginId = getPluginId(plugin);
if (isPluginEnabled(pluginId)) {
return new PluginResults<P>().addResult(pluginId, Result.ALREADY_ENABLED);
}
}
return enableOrderedPlugins(determineLoadOrder(plugins));
}
protected abstract PluginResults<P> enableOrderedPlugins(List<P> plugins);
public boolean isPluginEnabled(P plugin) {
return isPluginEnabled(getPluginId(plugin));
}
public abstract boolean isPluginEnabled(String pluginId);
/**
* Disables the given plugin by name.
*/
public PluginResult<P> disablePlugin(String pluginId) {
return getPlugin(pluginId)
.map(this::disablePlugin)
.orElse(new PluginResult<>(pluginId, Result.NOT_EXISTS));
}
public PluginResult<P> disablePlugin(P plugin) {
return disablePlugins(Collections.singletonList(plugin)).first();
}
/**
* Disables a list of plugins.
*/
public PluginResults<P> disablePlugins(List<P> plugins) {
for (P plugin : plugins) {
if (!isPluginEnabled(plugin)) {
return new PluginResults<P>().addResult(getPluginId(plugin), Result.ALREADY_DISABLED);
}
}
List<P> 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<P>().addResult(sb.substring(2), Result.ERROR);
}
Collections.reverse(orderedPlugins);
return disableOrderedPlugins(orderedPlugins);
}
public abstract PluginResults<P> disableOrderedPlugins(List<P> plugins);
/**
* Reloads the given plugin by name.
*/
public PluginResult<P> reloadPlugin(String pluginId) {
return getPlugin(pluginId)
.map(this::reloadPlugin)
.orElse(new PluginResult<>(pluginId, Result.NOT_EXISTS));
}
public PluginResult<P> reloadPlugin(P plugin) {
return reloadPlugins(Collections.singletonList(plugin)).first();
}
/**
* Reloads the given plugins.
*/
public PluginResults<P> reloadPlugins(List<P> plugins) {
PluginResults<P> disableResults = disablePlugins(plugins);
for (PluginResult<P> disableResult : disableResults.getResults()) {
if (!disableResult.isSuccess() && disableResult.getResult() != Result.ALREADY_DISABLED) {
return disableResults;
}
}
List<String> pluginIds = new ArrayList<>(plugins.size());
for (P plugin : plugins) {
pluginIds.add(getPluginId(plugin));
}
CloseablePluginResults<P> unloadResults = unloadPlugins(plugins);
if (!unloadResults.isSuccess()) return unloadResults;
unloadResults.tryClose();
List<File> pluginFiles = new ArrayList<>(plugins.size());
for (String pluginId : pluginIds) {
Optional<File> pluginFile = getPluginFile(pluginId);
if (!pluginFile.isPresent()) return new PluginResults<P>().addResult(pluginId, Result.FILE_DELETED);
pluginFiles.add(pluginFile.get());
}
PluginResults<P> loadResults = loadPlugins(pluginFiles);
if (!loadResults.isSuccess()) return loadResults;
List<P> loadedPlugins = new ArrayList<>(pluginIds.size());
for (PluginResult<P> loadResult : loadResults) {
loadedPlugins.add(loadResult.getPlugin());
}
return enablePlugins(loadedPlugins);
}
/**
* Unloads the given plugin by name.
*/
public CloseablePluginResult<P> unloadPlugin(String pluginId) {
return getPlugin(pluginId)
.map(this::unloadPlugin)
.orElse(new CloseablePluginResult<>(pluginId, Result.NOT_EXISTS));
}
public CloseablePluginResult<P> unloadPlugin(P plugin) {
return unloadPlugins(Collections.singletonList(plugin)).first();
}
/**
* Unloads a list of plugins.
*/
public CloseablePluginResults<P> unloadPlugins(List<P> plugins) {
List<P> 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<P>().addResult(sb.substring(2), Result.ERROR);
}
Collections.reverse(orderedPlugins);
return unloadOrderedPlugins(orderedPlugins);
}
public abstract CloseablePluginResults<P> unloadOrderedPlugins(List<P> plugins);
/**
* Determines the load order of a list of plugins.
*/
public List<P> determineLoadOrder(List<P> plugins) throws IllegalStateException {
Map<D, P> descriptionMap = new HashMap<>(plugins.size());
for (P plugin : plugins) {
descriptionMap.put(getLoadedPluginDescription(plugin), plugin);
}
List<P> 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<D> determineLoadOrder(Collection<? extends D> descriptions) throws IllegalStateException {
Map<String, D> pluginIdToDescriptionMap = new HashMap<>();
for (D description : descriptions) {
pluginIdToDescriptionMap.put(description.getId(), description);
}
Map<D, Set<D>> dependencyMap = new HashMap<>(descriptions.size());
for (D description : descriptions) {
Set<String> dependencyStrings = description.getDependencies();
Set<D> 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;
}
}

View file

@ -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<P> {
public interface PluginProvider<P, D extends ServerUtilsPluginDescription> {
default File getPluginsFolder() {
return ServerUtilsApp.getPlugin().getDataFolder().getParentFile();
@ -16,13 +19,43 @@ public interface PluginProvider<P> {
List<P> 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<File> getPluginFile(String pluginId) {
for (File file : getPluginJars()) {
Optional<D> 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<P> getPlugin(String pluginId);
D getLoadedPluginDescription(P plugin);
default Optional<D> getPluginDescription(String pluginId) throws InvalidPluginDescriptionException {
Optional<File> fileOptional = getPluginFile(pluginId);
return fileOptional.flatMap(this::getPluginDescription);
}
Optional<D> getPluginDescription(File file) throws InvalidPluginDescriptionException;
Object getInstance(P plugin);
@ -34,7 +67,7 @@ public interface PluginProvider<P> {
*/
default List<P> getPluginsSorted() {
List<P> plugins = getPlugins();
plugins.sort(Comparator.comparing(this::getPluginName));
plugins.sort(Comparator.comparing(this::getPluginId));
return plugins;
}
@ -44,7 +77,7 @@ public interface PluginProvider<P> {
*/
default List<String> getPluginNames() {
return getPlugins().stream()
.map(this::getPluginName)
.map(this::getPluginId)
.collect(Collectors.toList());
}

View file

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

View file

@ -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<U extends ServerUtilsPlugin<P, ?, ?, ?>, P> implements Runnable {
public class UpdateCheckerTask<U extends ServerUtilsPlugin<P, ?, ?, ?, ?>, P> implements Runnable {
private final U plugin;
private final ServerCommandSender<?> sender;
@ -60,7 +60,7 @@ public class UpdateCheckerTask<U extends ServerUtilsPlugin<P, ?, ?, ?>, P> imple
* Checks for updates if enabled per config for the specific action.
* Action must be 'login' or 'boot'.
*/
public static <U extends ServerUtilsPlugin<P, ?, ?, ?>, P> void tryStart(
public static <U extends ServerUtilsPlugin<P, ?, ?, ?, ?>, P> void tryStart(
U plugin,
ServerCommandSender<?> sender,
String action
@ -75,7 +75,7 @@ public class UpdateCheckerTask<U extends ServerUtilsPlugin<P, ?, ?, ?>, P> imple
* Checks for updates and downloads/installs if configured.
* Action must be 'login' or 'boot'.
*/
public static <U extends ServerUtilsPlugin<P, ?, ?, ?>, P> void start(
public static <U extends ServerUtilsPlugin<P, ?, ?, ?, ?>, P> void start(
U plugin,
ServerCommandSender<?> sender,
String action
@ -241,21 +241,19 @@ public class UpdateCheckerTask<U extends ServerUtilsPlugin<P, ?, ?, ?>, P> imple
private void tryReloadPlugin(File pluginFile, File updaterFile) {
plugin.getTaskManager().runTask(() -> {
LoadResult<P> loadResult = plugin.getPluginManager().loadPlugin(updaterFile);
PluginResult<P> 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<P> 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();
});

View file

@ -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 <T> List<T> determineOrder(Map<T, Set<T>> dependencyMap) throws IllegalStateException {
MutableGraph<T> dependencyGraph = GraphBuilder.directed().allowsSelfLoops(true).build();
for (T node : dependencyMap.keySet()) {
dependencyGraph.addNode(node);
}
for (Map.Entry<T, Set<T>> entry : dependencyMap.entrySet()) {
for (T dependency : entry.getValue()) {
dependencyGraph.putEdge(entry.getKey(), dependency);
}
}
List<T> orderedList = new ArrayList<>(dependencyMap.size());
Map<T, Mark> marks = new HashMap<>(dependencyMap.size());
for (T node : dependencyGraph.nodes()) {
visitNode(dependencyGraph, node, marks, orderedList, new LinkedList<>());
}
return orderedList;
}
@SuppressWarnings("UnstableApiUsage")
private static <T> void visitNode(
Graph<T> dependencyGraph,
T node,
Map<T, Mark> marks,
List<T> orderedList,
Deque<T> 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
}
}

View file

@ -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": {

View file

@ -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<String, Set<String>> dependencyMap,
List<String> expected
) {
assertThat(DependencyUtils.determineOrder(dependencyMap)).isEqualTo(expected);
}
@ParameterizedTest(name = "dependencyMap = {0}")
@MethodSource("circularDependencyGenerator")
void determineOrderCircularDependencies(
Map<String, Set<String>> dependencyMap
) {
assertThatIllegalStateException().isThrownBy(() -> DependencyUtils.determineOrder(dependencyMap));
}
private static Stream<Arguments> 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<Arguments> 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 <T> Set<T> asSet(T... elements) {
return new HashSet<>(asList(elements));
}
private static <K, V> Map<K, V> mapOf(Pair<K, V>... pairs) {
Map<K, V> map = new HashMap<>();
for (Pair<K, V> pair : pairs) {
map.put(pair.first, pair.second);
}
return map;
}
private static final class Pair<A, B> {
private final A first;
private final B second;
private Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
}

View file

@ -21,16 +21,15 @@ public class VelocityCommandServerUtils
extends CommandServerUtils<VelocityPlugin, PluginContainer, VelocityCommandSender> {
public VelocityCommandServerUtils(VelocityPlugin plugin) {
super(plugin);
super(plugin, PluginContainer[]::new);
}
@Override
protected FormatBuilder createPluginInfo(
FormatBuilder builder,
Function<Consumer<ListBuilder<String>>, String> listBuilderFunction,
String pluginName
PluginContainer container
) {
PluginContainer container = plugin.getPluginManager().getPlugin(pluginName);
PluginDescription desc = container.getDescription();
return builder

View file

@ -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<PluginContainer> {
public VelocityLoadResult(PluginContainer obj, Result result) {
super(obj, result);
}
public VelocityLoadResult(PluginContainer obj) {
super(obj);
}
public VelocityLoadResult(Result result) {
super(result);
}
}

View file

@ -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<PluginContainer, ScheduledTask, VelocityCommandSender, CommandSource, VelocityPluginDescription> {
private final ServerUtils plugin;
private final VelocityPluginManager pluginManager;
@ -46,13 +41,15 @@ public class VelocityPlugin extends ServerUtilsPlugin<
@Override
protected VelocityCommandManager<VelocityCommandSender> newCommandManager() {
return new VelocityCommandManager<>(
VelocityCommandManager<VelocityCommandSender> commandManager = new VelocityCommandManager<>(
plugin.getPluginContainer(),
plugin.getProxy(),
AsynchronousCommandExecutionCoordinator.<VelocityCommandSender>newBuilder().build(),
chatProvider::get,
VelocityCommandSender::getSource
);
handleBrigadier(commandManager.brigadierManager());
return commandManager;
}
@Override

View file

@ -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<String> dependencies;
/**
* Constructs a new BungeePluginDescription.
*/
public VelocityPluginDescription(PluginDescription description) {
this.description = description;
Optional<Path> 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("<UNKNOWN>");
}
@Override
public String getVersion() {
return this.description.getVersion().orElse("<UNKNOWN>");
}
@Override
public String getAuthor() {
return this.author;
}
@Override
public File getFile() {
return this.file;
}
@Override
public Set<String> getDependencies() {
return this.dependencies;
}
public PluginDescription getDescription() {
return description;
}
}

View file

@ -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<PluginContainer> {
public class VelocityPluginManager extends AbstractPluginManager<PluginContainer, VelocityPluginDescription> {
private static VelocityPluginManager instance;
private final ProxyServer proxy;
@ -66,195 +68,223 @@ public class VelocityPluginManager implements AbstractPluginManager<PluginContai
}
@Override
public VelocityLoadResult loadPlugin(String pluginFile) {
return loadPlugin(new File(getPluginsFolder(), pluginFile));
}
public PluginResults<PluginContainer> loadPluginDescriptions(List<VelocityPluginDescription> descriptions) {
PluginResults<PluginContainer> 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("<UNKNOWN>"),
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<PluginContainer> enableOrderedPlugins(List<PluginContainer> containers) {
PluginResults<PluginContainer> enableResults = new PluginResults<>();
ConsoleCommandSource console = proxy.getConsoleCommandSource();
PermissionsSetupEvent event = new PermissionsSetupEvent(
console,
s -> PermissionFunction.ALWAYS_TRUE
List<Object> 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("<UNKNOWN>"),
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<PluginContainer> disableOrderedPlugins(List<PluginContainer> containers) {
PluginResults<PluginContainer> disableResults = new PluginResults<>();
List<Object> 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<PluginContainer> 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<PluginContainer> 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<PluginContainer> unloadOrderedPlugins(List<PluginContainer> containers) {
CloseablePluginResults<PluginContainer> 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<Closeable> 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<Closeable> 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<PluginContai
}
@Override
public String getPluginName(PluginContainer plugin) {
public String getPluginId(PluginContainer plugin) {
return plugin.getDescription().getId();
}
@ -275,22 +305,43 @@ public class VelocityPluginManager implements AbstractPluginManager<PluginContai
}
@Override
public File getPluginFile(String pluginName) {
public Optional<File> 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<PluginContainer> getPlugin(String pluginName) {
return proxy.getPluginManager().getPlugin(pluginName);
}
@Override
public VelocityPluginDescription getLoadedPluginDescription(PluginContainer plugin) {
return new VelocityPluginDescription(plugin.getDescription());
}
@Override
public Optional<VelocityPluginDescription> 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

View file

@ -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<Object> getRegistrationsByPlugin(EventManager manager, Object plugin, Class<?> eventClass) {
public static List<Object> getRegistrationsByPlugins(
EventManager manager,
List<Object> plugins,
Class<?> eventClass
) {
return (List<Object>) 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 <E> CompletableFuture<E> fireForPlugin(
public static <E> CompletableFuture<E> fireForPlugins(
EventManager manager,
E event,
Object plugin
List<Object> pluginInstances
) {
List<Object> registrations = getRegistrationsByPlugin(manager, plugin, event.getClass());
List<Object> registrations = getRegistrationsByPlugins(manager, pluginInstances, event.getClass());
CompletableFuture<E> 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;
}

View file

@ -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'

View file

@ -39,7 +39,7 @@
<property name="fileExtensions" value="java"/>
<!-- changed from 100 to 120, loosen a bit -->
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://|^[ ]*(public )?(static )?(final )?(abstract )?class.*"/>
</module>
<module name="TreeWalker">