From d8d9061f841432fe486fd43be18d0fe7a9e37ecc Mon Sep 17 00:00:00 2001 From: Florian CUNY Date: Sat, 18 Jan 2020 13:51:29 +0100 Subject: [PATCH] Added 'api-version' parameter to addon.yml to allow specifying a minimum required BentoBox version Implements https://github.com/BentoBoxWorld/BentoBox/issues/1131 API: added InvalidAddonDescriptionException, AddonDescription#getApiVersion() --- .../bentobox/api/addons/AddonClassLoader.java | 12 +++++- .../bentobox/api/addons/AddonDescription.java | 43 +++++++++++++++++++ .../InvalidAddonDescriptionException.java | 16 +++++++ .../bentobox/managers/AddonsManager.java | 36 ++++++++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java diff --git a/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java b/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java index 184675b6a..867f0c3f5 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/AddonClassLoader.java @@ -20,6 +20,7 @@ import org.bukkit.util.permissions.DefaultPermissions; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonDescriptionException; import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonFormatException; import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonInheritException; import world.bentobox.bentobox.managers.AddonsManager; @@ -39,6 +40,7 @@ public class AddonClassLoader extends URLClassLoader { throws InvalidAddonInheritException, MalformedURLException, InvalidDescriptionException, + InvalidAddonDescriptionException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { super(new URL[]{path.toURI().toURL()}, parent); @@ -94,7 +96,7 @@ public class AddonClassLoader extends URLClassLoader { } @NonNull - private AddonDescription asDescription(YamlConfiguration data) { + private 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)) @@ -110,6 +112,14 @@ public class AddonClassLoader extends URLClassLoader { } builder.icon(Material.getMaterial(data.getString("icon", "PAPER"))); + String apiVersion = data.getString("api-version"); + if (apiVersion != null) { + if (!apiVersion.matches("^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$")) { + throw new InvalidAddonDescriptionException("Provided API version '" + apiVersion + "' is not valid. It must only contain digits and dots and not end with a dot."); + } + builder.apiVersion(apiVersion); + } + return builder.build(); } diff --git a/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java b/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java index 287082cf6..52192b918 100644 --- a/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java +++ b/src/main/java/world/bentobox/bentobox/api/addons/AddonDescription.java @@ -35,6 +35,12 @@ public final class AddonDescription { * @since 1.5.0 */ private final @NonNull Material icon; + /** + * Minimum BentoBox version this addon requires in order to work properly. + * Defaults to {@code "1"}. + * @since 1.11.0 + */ + private final @NonNull String apiVersion; private AddonDescription(@NonNull Builder builder) { this.main = builder.main; @@ -47,6 +53,7 @@ public final class AddonDescription { this.metrics = builder.metrics; this.repository = builder.repository; this.icon = builder.icon; + this.apiVersion = builder.apiVersion; } @NonNull @@ -120,6 +127,29 @@ public final class AddonDescription { return icon; } + /** + * Returns the minimum BentoBox version this addon requires in order to work properly. + *
+ * Examples: + * + * Defaults to {@code "1"}. + * @return the minimum BentoBox version this addon requires in order to work properly. + */ + @NonNull + public String getApiVersion() { + return apiVersion; + } + public static class Builder { private @NonNull String main; private @NonNull String name; @@ -131,6 +161,7 @@ public final class AddonDescription { private boolean metrics = true; private @NonNull String repository = ""; private @NonNull Material icon = Material.PAPER; + private @NonNull String apiVersion = "1"; /** * @since 1.1 @@ -196,6 +227,18 @@ public final class AddonDescription { return this; } + /** + * Sets the minimum BentoBox version this addon requires in order to work properly. + * @param apiVersion the minimum BentoBox version this addon requires in order to work properly. + * @since 1.11.0 + * @see AddonDescription#getApiVersion() + */ + @NonNull + public Builder apiVersion(@NonNull String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + @NonNull public AddonDescription build() { return new AddonDescription(this); diff --git a/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java new file mode 100644 index 000000000..b6f3fae62 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/addons/exceptions/InvalidAddonDescriptionException.java @@ -0,0 +1,16 @@ +package world.bentobox.bentobox.api.addons.exceptions; + +/** + * @since 1.11.0 + */ +public class InvalidAddonDescriptionException extends AddonException { + + /** + * + */ + private static final long serialVersionUID = 7741502900847049986L; + + public InvalidAddonDescriptionException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java b/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java index 7ab4750d3..b0a257cdd 100644 --- a/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/AddonsManager.java @@ -117,6 +117,16 @@ public class AddonsManager { // 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. + plugin.logWarning("Skipping " + addon.getDescription().getName() + " as it can only run on a more recent BentoBox version."); + plugin.logWarning("NOTE: The addon relies on API that is not available in the BentoBox version you are using."); + plugin.logWarning("NOTE: Please update BentoBox."); + addon.setState(State.INCOMPATIBLE); + return; + } + try { // Run the onLoad. addon.onLoad(); @@ -204,6 +214,32 @@ public class AddonsManager { plugin.log("NOTE: DO NOT report this as a bug from BentoBox."); } + /** + * Checks if the addon does not explicitly rely on API from a more recent BentoBox version. + * @param addon instance of the Addon. + * @return {@code true} if the addon relies on available BentoBox API, {@code false} otherwise. + * @since 1.11.0 + */ + private boolean isAddonCompatibleWithBentoBox(@NonNull Addon addon) { + // We have to use lists instead of arrays for dynamic changes and optimization (appending something to an array is O(n) whereas O(1) with lists). + List apiVersion = Arrays.asList(addon.getDescription().getApiVersion().split("\\.")); + List bentoboxVersion = Arrays.asList(plugin.getDescription().getVersion().split("\\.")); + + while (bentoboxVersion.size() < apiVersion.size()) { + bentoboxVersion.add("0"); + } + + for (int i = 0; i < apiVersion.size(); i++) { + int apiNumber = Integer.parseInt(apiVersion.get(i)); + int bentoboxNumber = Integer.parseInt(bentoboxVersion.get(i)); + if (bentoboxNumber < apiNumber) { + return false; + } + } + + return true; + } + /** * Handles an addon which failed to load due to an error. * @param addon instance of the Addon.