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()
This commit is contained in:
Florian CUNY 2020-01-18 13:51:29 +01:00
parent d8dda75ca5
commit d8d9061f84
4 changed files with 106 additions and 1 deletions

View File

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

View File

@ -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.
* <br/>
* Examples:
* <ul>
* <li>{@code "1"} means that the addon relies on BentoBox {@code 1.0.0} or higher.</li>
* <li>Similarly, {@code "2"} sets the requirement to BentoBox {@code 2.0.0} or higher.</li>
* <li>
* More specific versions can be provided:
* <ul>
* <li>{@code "1.10"} -> BentoBox {@code 1.10.0} or higher.</li>
* <li>{@code "1.9.2"} -> BentoBox {@code 1.9.2} or higher.</li>
* </ul>
* </li>
* </ul>
* 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);

View File

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

View File

@ -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<String> apiVersion = Arrays.asList(addon.getDescription().getApiVersion().split("\\."));
List<String> 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.