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

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