mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-11-24 19:55:17 +01:00
Fixed and improved error handling when loading addons
Fixes #440 * Added Addon#getState() * Added LOADED in Addon.State * Added AddonsManager#getLoadedAddons() and AddonsManager#getEnabledAddons() * Improved the loading addons "workflow"
This commit is contained in:
parent
37f509ea56
commit
c78efb8736
@ -74,6 +74,12 @@ public abstract class Addon {
|
|||||||
* @author Poslovitch
|
* @author Poslovitch
|
||||||
*/
|
*/
|
||||||
public enum State {
|
public enum State {
|
||||||
|
/**
|
||||||
|
* The addon has been correctly loaded.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
LOADED,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The addon has been correctly enabled and is now fully working.
|
* The addon has been correctly enabled and is now fully working.
|
||||||
*/
|
*/
|
||||||
@ -151,6 +157,15 @@ public abstract class Addon {
|
|||||||
return state == State.ENABLED;
|
return state == State.ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current {@link State} of this Addon.
|
||||||
|
* @return the current State of this Addon.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load YAML config file
|
* Load YAML config file
|
||||||
*
|
*
|
||||||
|
@ -55,51 +55,130 @@ public class AddonsManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Arrays.stream(Objects.requireNonNull(f.listFiles())).filter(x -> !x.isDirectory() && x.getName().endsWith(".jar")).forEach(this::loadAddon);
|
Arrays.stream(Objects.requireNonNull(f.listFiles())).filter(x -> !x.isDirectory() && x.getName().endsWith(".jar")).forEach(this::loadAddon);
|
||||||
plugin.log("Loaded " + addons.size() + " addons.");
|
plugin.log("Loaded " + getLoadedAddons().size() + " addons.");
|
||||||
|
|
||||||
if (!addons.isEmpty()) {
|
if (!getLoadedAddons().isEmpty()) {
|
||||||
sortAddons();
|
sortAddons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadAddon(File f) {
|
||||||
|
Addon addon;
|
||||||
|
AddonClassLoader addonClassLoader;
|
||||||
|
try (JarFile jar = new JarFile(f)) {
|
||||||
|
// try loading the addon
|
||||||
|
// Get description in the addon.yml file
|
||||||
|
YamlConfiguration data = addonDescription(jar);
|
||||||
|
|
||||||
|
// Load the addon
|
||||||
|
addonClassLoader = new AddonClassLoader(this, data, f, this.getClass().getClassLoader());
|
||||||
|
|
||||||
|
// Get the addon itself
|
||||||
|
addon = addonClassLoader.getAddon();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We couldn't load the addon, aborting.
|
||||||
|
plugin.logError(e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize some settings
|
||||||
|
addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName()));
|
||||||
|
addon.setFile(f);
|
||||||
|
|
||||||
|
// Locales
|
||||||
|
plugin.getLocalesManager().copyLocalesFromAddonJar(addon);
|
||||||
|
plugin.getLocalesManager().loadLocalesFromFile(addon.getDescription().getName());
|
||||||
|
|
||||||
|
// Fire the load event
|
||||||
|
Bukkit.getPluginManager().callEvent(new AddonEvent().builder().addon(addon).reason(AddonEvent.Reason.LOAD).build());
|
||||||
|
|
||||||
|
// Add it to the list of addons
|
||||||
|
addons.add(addon);
|
||||||
|
|
||||||
|
// Add to the list of loaders
|
||||||
|
loaders.put(addon, addonClassLoader);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Run the onLoad.
|
||||||
|
addon.onLoad();
|
||||||
|
// If this is a GameModeAddon create the worlds, register it and load the schems
|
||||||
|
if (addon instanceof GameModeAddon) {
|
||||||
|
GameModeAddon gameMode = (GameModeAddon) addon;
|
||||||
|
// Create the gameWorlds
|
||||||
|
gameMode.createWorlds();
|
||||||
|
plugin.getIWM().addGameMode(gameMode);
|
||||||
|
// Register the schems
|
||||||
|
plugin.getSchemsManager().loadIslands(gameMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addon successfully loaded
|
||||||
|
addon.setState(Addon.State.LOADED);
|
||||||
|
} catch (NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
|
||||||
|
// Looks like the addon is incompatible, because it tries to refer to missing classes...
|
||||||
|
handleAddonIncompatibility(addon);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Unhandled exception. We'll give a bit of debug here.
|
||||||
|
handleAddonError(addon, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables all the addons
|
* Enables all the addons
|
||||||
*/
|
*/
|
||||||
public void enableAddons() {
|
public void enableAddons() {
|
||||||
if (!addons.isEmpty()) {
|
if (!getLoadedAddons().isEmpty()) {
|
||||||
plugin.log("Enabling addons...");
|
plugin.log("Enabling addons...");
|
||||||
addons.forEach(addon -> {
|
getLoadedAddons().forEach(addon -> {
|
||||||
try {
|
try {
|
||||||
addon.onEnable();
|
addon.onEnable();
|
||||||
Bukkit.getPluginManager().callEvent(new AddonEvent().builder().addon(addon).reason(AddonEvent.Reason.ENABLE).build());
|
Bukkit.getPluginManager().callEvent(new AddonEvent().builder().addon(addon).reason(AddonEvent.Reason.ENABLE).build());
|
||||||
addon.setState(Addon.State.ENABLED);
|
addon.setState(Addon.State.ENABLED);
|
||||||
plugin.log("Enabling " + addon.getDescription().getName() + "...");
|
plugin.log("Enabling " + addon.getDescription().getName() + "...");
|
||||||
} catch (NoClassDefFoundError | NoSuchMethodError e) {
|
} catch (NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
|
||||||
// Looks like the addon is outdated, because it tries to refer to missing classes.
|
// Looks like the addon is incompatible, because it tries to refer to missing classes...
|
||||||
// Set the AddonState as "INCOMPATIBLE".
|
handleAddonIncompatibility(addon);
|
||||||
addon.setState(Addon.State.INCOMPATIBLE);
|
|
||||||
plugin.log("Skipping " + addon.getDescription().getName() + " as it is incompatible with the current version of BentoBox or of server software...");
|
|
||||||
plugin.log("NOTE: The addon is referring to no longer existing classes.");
|
|
||||||
plugin.log("NOTE: DO NOT report this as a bug from BentoBox.");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Unhandled exception. We'll give a bit of debug here.
|
// Unhandled exception. We'll give a bit of debug here.
|
||||||
// Set the AddonState as "ERROR".
|
handleAddonError(addon, e);
|
||||||
addon.setState(Addon.State.ERROR);
|
|
||||||
plugin.log("Skipping " + addon.getDescription().getName() + " due to an unhandled exception...");
|
|
||||||
plugin.log("STACKTRACE: " + e.getClass().getSimpleName() + " - " + e.getMessage() + " - " + e.getCause());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
plugin.log("Addons successfully enabled.");
|
plugin.log("Addons successfully enabled.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an addon which failed to load due to an incompatibility (missing class, missing method).
|
||||||
|
* @param addon instance of the Addon.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
private void handleAddonIncompatibility(Addon addon) {
|
||||||
|
// Set the AddonState as "INCOMPATIBLE".
|
||||||
|
addon.setState(Addon.State.INCOMPATIBLE);
|
||||||
|
plugin.log("Skipping " + addon.getDescription().getName() + " as it is incompatible with the current version of BentoBox or of server software...");
|
||||||
|
plugin.log("NOTE: The addon is referring to no longer existing classes.");
|
||||||
|
plugin.log("NOTE: DO NOT report this as a bug from BentoBox.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an addon which failed to load due to an error.
|
||||||
|
* @param addon instance of the Addon.
|
||||||
|
* @param throwable Throwable that was thrown and which lead to the error.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
private void handleAddonError(Addon addon, Throwable throwable) {
|
||||||
|
// Set the AddonState as "ERROR".
|
||||||
|
addon.setState(Addon.State.ERROR);
|
||||||
|
plugin.log("Skipping " + addon.getDescription().getName() + " due to an unhandled exception...");
|
||||||
|
plugin.log("STACKTRACE: " + throwable.getClass().getSimpleName() + " - " + throwable.getMessage() + " - " + throwable.getCause());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads all the enabled addons
|
* Reloads all the enabled addons
|
||||||
*/
|
*/
|
||||||
public void reloadAddons() {
|
public void reloadAddons() {
|
||||||
if (!addons.isEmpty()) {
|
if (!getEnabledAddons().isEmpty()) {
|
||||||
plugin.log("Reloading addons...");
|
plugin.log("Reloading addons...");
|
||||||
addons.stream().filter(Addon::isEnabled).forEach(addon -> {
|
getEnabledAddons().stream().filter(Addon::isEnabled).forEach(addon -> {
|
||||||
plugin.log("Reloading " + addon.getDescription().getName() + "...");
|
plugin.log("Reloading " + addon.getDescription().getName() + "...");
|
||||||
addon.onReload();
|
addon.onReload();
|
||||||
});
|
});
|
||||||
@ -130,60 +209,14 @@ public class AddonsManager {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadAddon(File f) {
|
|
||||||
Addon addon;
|
|
||||||
try (JarFile jar = new JarFile(f)) {
|
|
||||||
// try loading the addon
|
|
||||||
// Get description in the addon.yml file
|
|
||||||
YamlConfiguration data = addonDescription(jar);
|
|
||||||
|
|
||||||
// Load the addon
|
|
||||||
AddonClassLoader addonClassLoader = new AddonClassLoader(this, data, f, this.getClass().getClassLoader());
|
|
||||||
|
|
||||||
// Get the addon itself
|
|
||||||
addon = addonClassLoader.getAddon();
|
|
||||||
|
|
||||||
// Initialize some settings
|
|
||||||
addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName()));
|
|
||||||
addon.setFile(f);
|
|
||||||
|
|
||||||
// Locales
|
|
||||||
plugin.getLocalesManager().copyLocalesFromAddonJar(addon);
|
|
||||||
plugin.getLocalesManager().loadLocalesFromFile(addon.getDescription().getName());
|
|
||||||
|
|
||||||
// Fire the load event
|
|
||||||
Bukkit.getPluginManager().callEvent(new AddonEvent().builder().addon(addon).reason(AddonEvent.Reason.LOAD).build());
|
|
||||||
|
|
||||||
// Add it to the list of addons
|
|
||||||
addons.add(addon);
|
|
||||||
|
|
||||||
// Add to the list of loaders
|
|
||||||
loaders.put(addon, addonClassLoader);
|
|
||||||
|
|
||||||
// Run the onLoad.
|
|
||||||
addon.onLoad();
|
|
||||||
// If this is a GameModeAddon create the worlds, register it and load the schems
|
|
||||||
if (addon instanceof GameModeAddon) {
|
|
||||||
GameModeAddon gameMode = (GameModeAddon)addon;
|
|
||||||
// Create the gameWorlds
|
|
||||||
gameMode.createWorlds();
|
|
||||||
plugin.getIWM().addGameMode(gameMode);
|
|
||||||
// Register the schems
|
|
||||||
plugin.getSchemsManager().loadIslands(gameMode);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
plugin.logError(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable all the enabled addons
|
* Disable all the enabled addons
|
||||||
*/
|
*/
|
||||||
public void disableAddons() {
|
public void disableAddons() {
|
||||||
if (!addons.isEmpty()) {
|
if (!getEnabledAddons().isEmpty()) {
|
||||||
plugin.log("Disabling addons...");
|
plugin.log("Disabling addons...");
|
||||||
// Disable addons
|
// Disable addons
|
||||||
addons.forEach(addon -> {
|
getEnabledAddons().forEach(addon -> {
|
||||||
if (addon.isEnabled()) {
|
if (addon.isEnabled()) {
|
||||||
addon.onDisable();
|
addon.onDisable();
|
||||||
Bukkit.getPluginManager().callEvent(new AddonEvent().builder().addon(addon).reason(AddonEvent.Reason.DISABLE).build());
|
Bukkit.getPluginManager().callEvent(new AddonEvent().builder().addon(addon).reason(AddonEvent.Reason.DISABLE).build());
|
||||||
@ -206,6 +239,24 @@ public class AddonsManager {
|
|||||||
return addons;
|
return addons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of Addons that are loaded.
|
||||||
|
* @return list of loaded Addons.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public List<Addon> getLoadedAddons() {
|
||||||
|
return addons.stream().filter(addon -> addon.getState().equals(Addon.State.LOADED)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of Addons that are enabled.
|
||||||
|
* @return list of enabled Addons.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public List<Addon> getEnabledAddons() {
|
||||||
|
return addons.stream().filter(addon -> addon.getState().equals(Addon.State.ENABLED)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
public AddonClassLoader getLoader(final Addon addon) {
|
public AddonClassLoader getLoader(final Addon addon) {
|
||||||
return loaders.get(addon);
|
return loaders.get(addon);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user