Enable plugins to register Addons (#1768)

* Enable plugins to register Addons

* Pladdon approach. Loads addons as plugins.

* Added auto-move for pladdons.
This commit is contained in:
tastybento 2021-05-31 12:42:54 -07:00 committed by GitHub
parent 9fc22aa8e7
commit 6e9513f2ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 136 additions and 13 deletions

View File

@ -33,14 +33,14 @@ public class AddonClassLoader extends URLClassLoader {
private Addon addon;
private AddonsManager loader;
public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File path, ClassLoader parent)
public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File jarFile, ClassLoader parent)
throws InvalidAddonInheritException,
MalformedURLException,
InvalidDescriptionException,
InvalidAddonDescriptionException,
InstantiationException,
IllegalAccessException, InvocationTargetException, NoSuchMethodException {
super(new URL[]{path.toURI().toURL()}, parent);
super(new URL[]{jarFile.toURI().toURL()}, parent);
loader = addonsManager;
@ -55,7 +55,7 @@ public class AddonClassLoader extends URLClassLoader {
throw new InvalidAddonFormatException("Package declaration cannot start with 'world.bentobox.bentobox'");
}
} catch (Exception e) {
throw new InvalidDescriptionException("Could not load '" + path.getName() + "' in folder '" + path.getParent() + "' - " + e.getMessage());
throw new InvalidDescriptionException("Could not load '" + jarFile.getName() + "' in folder '" + jarFile.getParent() + "' - " + e.getMessage());
}
Class<? extends Addon> addonClass;
@ -78,7 +78,7 @@ public class AddonClassLoader extends URLClassLoader {
* @throws InvalidAddonDescriptionException - if there's a bug in the addon.yml
*/
@NonNull
private AddonDescription asDescription(YamlConfiguration data) throws InvalidAddonDescriptionException {
public static AddonDescription asDescription(YamlConfiguration data) throws InvalidAddonDescriptionException {
AddonDescription.Builder builder = new AddonDescription.Builder(data.getString("main"), data.getString("name"), data.getString("version"))
.authors(data.getString("authors"))
.metrics(data.getBoolean("metrics", true))

View File

@ -0,0 +1,51 @@
package world.bentobox.bentobox.api.addons;
import java.io.File;
import java.io.IOException;
import org.bukkit.plugin.java.JavaPlugin;
import com.google.common.io.Files;
/**
* @author tastybento
*
*/
public abstract class Pladdon extends JavaPlugin {
private static final String ADDONS_FOLDER = "BentoBox/addons";
public abstract Addon getAddon();
@Override
public void onLoad() {
String parentFolder = getFile().getParent();
if (parentFolder == null || !parentFolder.endsWith(ADDONS_FOLDER)) {
// Jar is in the wrong place. Let's move it
moveJar();
}
}
public void moveJar() {
getLogger().severe(getFile().getName() + " must be in the BentoBox/addons folder! Trying to move it there...");
File addons = new File(getFile().getParent(), ADDONS_FOLDER);
if (addons.exists() || addons.mkdirs()) {
File to = new File(addons, getFile().getName());
if (!to.exists()) {
try {
Files.move(getFile(), to);
getLogger().severe(getFile().getName() + " moved successfully.");
} catch (IOException ex) {
getLogger().severe("Failed to move it. " + ex.getMessage());
getLogger().severe("Move " + getFile().getName() + " manually into the BentoBox/addons folder. Then restart server.");
}
} else {
getLogger().warning(getFile().getName() + " already is in the addons folder. Delete the one in the plugins folder.");
}
} else {
getLogger().severe("BentoBox addons folder could not be made! " + addons.getAbsolutePath());
}
}
}

View File

@ -4,6 +4,8 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -29,6 +31,9 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginLoader;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.permissions.DefaultPermissions;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@ -38,6 +43,7 @@ import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.addons.Addon.State;
import world.bentobox.bentobox.api.addons.AddonClassLoader;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.addons.Pladdon;
import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonDescriptionException;
import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonFormatException;
import world.bentobox.bentobox.api.configuration.ConfigObject;
@ -65,6 +71,8 @@ public class AddonsManager {
private @NonNull Map<@NonNull String, @Nullable GameModeAddon> worldNames;
private @NonNull Map<@NonNull Addon, @NonNull List<Listener>> listeners;
private final PluginLoader pluginLoader;
public AddonsManager(@NonNull BentoBox plugin) {
this.plugin = plugin;
addons = new ArrayList<>();
@ -72,6 +80,42 @@ public class AddonsManager {
classes = new HashMap<>();
listeners = new HashMap<>();
worldNames = new HashMap<>();
pluginLoader = plugin.getPluginLoader();
}
/**
* Register a plugin as an addon
* @param parent - parent plugin
* @param addon - addon class
*/
public void registerAddon(Plugin parent, Addon addon) {
plugin.log("Registering " + parent.getDescription().getName());
// Get description in the addon.yml file
// Open a reader to the jar
try (BufferedReader reader = new BufferedReader(new InputStreamReader(parent.getResource("addon.yml")))) {
setAddonFile(parent, addon);
// Grab the description in the addon.yml file
YamlConfiguration data = new YamlConfiguration();
data.load(reader);
// Description
addon.setDescription(AddonClassLoader.asDescription(data));
// Set various files
addon.setDataFolder(parent.getDataFolder());
// Initialize
initializeAddon(addon);
sortAddons();
} catch (Exception e) {
plugin.logError("Failed to register addon: " + e);
}
}
private void setAddonFile(Plugin parent, Addon addon) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Method getFileMethod = JavaPlugin.class.getDeclaredMethod("getFile");
getFileMethod.setAccessible(true);
addon.setFile((File) getFileMethod.invoke(parent));
}
/**
@ -109,10 +153,24 @@ public class AddonsManager {
return;
}
// Load the addon
addonClassLoader = new AddonClassLoader(this, data, f, this.getClass().getClassLoader());
try {
// Get the addon itself
addon = addonClassLoader.getAddon();
Plugin pladdon = pluginLoader.loadPlugin(f);
if (pladdon instanceof Pladdon) {
addon = ((Pladdon) pladdon).getAddon();
addon.setDescription(AddonClassLoader.asDescription(data));
} else {
plugin.logError("Could not load pladdon!");
return;
}
} catch (Exception ex) {
// Addon not pladdon
addonClassLoader = new AddonClassLoader(this, data, f, this.getClass().getClassLoader());
// Get the addon itself
addon = addonClassLoader.getAddon();
// Add to the list of loaders
loaders.put(addon, addonClassLoader);
}
} catch (Exception e) {
// We couldn't load the addon, aborting.
plugin.logError("Could not load addon! " + e.getMessage());
@ -123,7 +181,12 @@ public class AddonsManager {
// Initialize some settings
addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName()));
addon.setFile(f);
// Initialize addon
initializeAddon(addon);
}
private void initializeAddon(Addon addon) {
// Locales
plugin.getLocalesManager().copyLocalesFromAddonJar(addon);
plugin.getLocalesManager().loadLocalesFromFile(addon.getDescription().getName());
@ -134,10 +197,6 @@ public class AddonsManager {
// Add it to the list of addons
addons.remove(addon);
addons.add(addon);
// Add to the list of loaders
loaders.put(addon, addonClassLoader);
// Checks if this addon is compatible with the current BentoBox version.
if (!isAddonCompatibleWithBentoBox(addon)) {
// It is not, abort.
@ -165,6 +224,7 @@ public class AddonsManager {
// Unhandled exception. We'll give a bit of debug here.
handleAddonError(addon, e);
}
}
/**
@ -383,6 +443,7 @@ public class AddonsManager {
// Grab the description in the addon.yml file
YamlConfiguration data = new YamlConfiguration();
data.load(reader);
reader.close();
return data;
}

View File

@ -12,11 +12,22 @@ load: STARTUP
loadbefore: [Multiverse-Core, Residence]
softdepend: [Vault, PlaceholderAPI, dynmap, WorldEdit, WorldBorderAPI, BsbMongo, WorldGeneratorApi, AdvancedChests, LangUtils, WildStacker, LuckPerms]
softdepend:
- Vault
- PlaceholderAPI
- dynmap
- WorldEdit
- WorldBorderAPI
- BsbMongo
- WorldGeneratorApi
- AdvancedChests
- LangUtils
- WildStacker
- LuckPerms
permissions:
bentobox.admin:
description: Allows most of bentobox commands usage
description: Allows admin command usage
default: op
children:
bentobox.admin.catalog: