mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-11-30 22:53:39 +01:00
Added support for depend in addon.yml
This enables add-ons that must load after another addon to mark it in the depend line of addon.yml
This commit is contained in:
parent
f152d218fc
commit
9adabc4fb2
@ -4,6 +4,7 @@ import java.io.File;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -80,10 +81,14 @@ public class AddonClassLoader extends URLClassLoader {
|
|||||||
DefaultPermissions.registerPermission(perm, desc, pd);
|
DefaultPermissions.registerPermission(perm, desc, pd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AddonDescription asDescription(YamlConfiguration data){
|
private AddonDescription asDescription(YamlConfiguration data) {
|
||||||
return new AddonDescriptionBuilder(data.getString("name"))
|
AddonDescriptionBuilder adb = new AddonDescriptionBuilder(data.getString("name"))
|
||||||
.withVersion(data.getString("version"))
|
.withVersion(data.getString("version"))
|
||||||
.withAuthor(data.getString("authors")).build();
|
.withAuthor(data.getString("authors"));
|
||||||
|
if (data.getString("depend") != null) {
|
||||||
|
adb.withDepend(Arrays.asList(data.getString("depend").split("\\s*,\\s*")));
|
||||||
|
}
|
||||||
|
return adb.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package world.bentobox.bentobox.api.addons;
|
package world.bentobox.bentobox.api.addons;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -12,16 +13,18 @@ public final class AddonDescription {
|
|||||||
private String name;
|
private String name;
|
||||||
private String version;
|
private String version;
|
||||||
private String description;
|
private String description;
|
||||||
private List<String> authors;
|
private List<String> authors = new ArrayList<>();
|
||||||
|
private List<String> depend = new ArrayList<>();
|
||||||
|
|
||||||
public AddonDescription() {}
|
public AddonDescription() {}
|
||||||
|
|
||||||
public AddonDescription(String main, String name, String version, String description, List<String> authors) {
|
public AddonDescription(String main, String name, String version, String description, List<String> authors, List<String> depend) {
|
||||||
this.main = main;
|
this.main = main;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.authors = authors;
|
this.authors = authors;
|
||||||
|
this.depend = depend;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,6 +62,20 @@ public final class AddonDescription {
|
|||||||
this.authors = authors;
|
this.authors = authors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the loadAfter
|
||||||
|
*/
|
||||||
|
public List<String> getDependencies() {
|
||||||
|
return depend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param loadAfter the loadAfter to set
|
||||||
|
*/
|
||||||
|
public void setLoadAfter(List<String> loadAfter) {
|
||||||
|
this.depend = loadAfter;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -103,6 +120,11 @@ public final class AddonDescription {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AddonDescriptionBuilder withDepend(List<String> addons) {
|
||||||
|
description.setLoadAfter(addons);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public AddonDescription build(){
|
public AddonDescription build(){
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,18 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
@ -29,7 +33,6 @@ import world.bentobox.bentobox.api.events.addon.AddonEvent;
|
|||||||
*/
|
*/
|
||||||
public class AddonsManager {
|
public class AddonsManager {
|
||||||
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
private static final String LOCALE_FOLDER = "locales";
|
private static final String LOCALE_FOLDER = "locales";
|
||||||
private List<Addon> addons;
|
private List<Addon> addons;
|
||||||
private List<AddonClassLoader> loader;
|
private List<AddonClassLoader> loader;
|
||||||
@ -40,21 +43,30 @@ public class AddonsManager {
|
|||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
addons = new ArrayList<>();
|
addons = new ArrayList<>();
|
||||||
loader = new ArrayList<>();
|
loader = new ArrayList<>();
|
||||||
|
loadAddonsFromFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void loadAddonsFromFile() {
|
||||||
* Loads all the addons from the addons folder
|
|
||||||
*/
|
|
||||||
public void loadAddons() {
|
|
||||||
plugin.log("Loading addons...");
|
plugin.log("Loading addons...");
|
||||||
File f = new File(plugin.getDataFolder(), "addons");
|
File f = new File(plugin.getDataFolder(), "addons");
|
||||||
if (!f.exists() && !f.mkdirs()) {
|
if (!f.exists() && !f.mkdirs()) {
|
||||||
plugin.logError("Cannot make addons folder!");
|
plugin.logError("Cannot make addons folder!");
|
||||||
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);
|
||||||
addons.forEach(Addon::onLoad);
|
sortAddons();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all the addons from the addons folder
|
||||||
|
*/
|
||||||
|
public void loadAddons() {
|
||||||
|
// Run each onLoad
|
||||||
|
addons.forEach(addon -> {
|
||||||
|
addon.onLoad();
|
||||||
|
Bukkit.getPluginManager().callEvent(AddonEvent.builder().addon(addon).reason(AddonEvent.Reason.LOAD).build());
|
||||||
|
plugin.log("Loading " + addon.getDescription().getName() + "...");
|
||||||
|
});
|
||||||
plugin.log("Loaded " + addons.size() + " addons.");
|
plugin.log("Loaded " + addons.size() + " addons.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,56 +93,48 @@ public class AddonsManager {
|
|||||||
return addons.stream().filter(a -> a.getDescription().getName().contains(name)).findFirst();
|
return addons.stream().filter(a -> a.getDescription().getName().contains(name)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private YamlConfiguration addonDescription(JarFile jar) throws InvalidAddonFormatException, IOException, InvalidConfigurationException {
|
||||||
|
// Obtain the addon.yml file
|
||||||
|
JarEntry entry = jar.getJarEntry("addon.yml");
|
||||||
|
if (entry == null) {
|
||||||
|
throw new InvalidAddonFormatException("Addon '" + jar.getName() + "' doesn't contains addon.yml file");
|
||||||
|
}
|
||||||
|
// Open a reader to the jar
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(jar.getInputStream(entry)));
|
||||||
|
// Grab the description in the addon.yml file
|
||||||
|
YamlConfiguration data = new YamlConfiguration();
|
||||||
|
data.load(reader);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadAddon(File f) {
|
private void loadAddon(File f) {
|
||||||
try {
|
Addon addon;
|
||||||
Addon addon;
|
try (JarFile jar = new JarFile(f)) {
|
||||||
// Check that this is a jar
|
// Get description in the addon.yml file
|
||||||
if (!f.getName().endsWith(".jar")) {
|
YamlConfiguration data = addonDescription(jar);
|
||||||
throw new IOException("Filename must end in .jar. Name is '" + f.getName() + "'");
|
// Load the addon
|
||||||
|
AddonClassLoader addonClassLoader = new AddonClassLoader(this, data, f, this.getClass().getClassLoader());
|
||||||
|
// Add to the list of loaders
|
||||||
|
loader.add(addonClassLoader);
|
||||||
|
|
||||||
|
// Get the addon itself
|
||||||
|
addon = addonClassLoader.getAddon();
|
||||||
|
// Initialize some settings
|
||||||
|
addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName()));
|
||||||
|
addon.setAddonFile(f);
|
||||||
|
|
||||||
|
File localeDir = new File(plugin.getDataFolder(), LOCALE_FOLDER + File.separator + addon.getDescription().getName());
|
||||||
|
// Obtain any locale files and save them
|
||||||
|
for (String localeFile : listJarYamlFiles(jar, LOCALE_FOLDER)) {
|
||||||
|
addon.saveResource(localeFile, localeDir, false, true);
|
||||||
}
|
}
|
||||||
try (JarFile jar = new JarFile(f)) {
|
plugin.getLocalesManager().loadLocalesFromFile(addon.getDescription().getName());
|
||||||
// Obtain the addon.yml file
|
// Fire the load event
|
||||||
JarEntry entry = jar.getJarEntry("addon.yml");
|
Bukkit.getPluginManager().callEvent(AddonEvent.builder().addon(addon).reason(AddonEvent.Reason.LOAD).build());
|
||||||
if (entry == null) {
|
// Add it to the list of addons
|
||||||
throw new InvalidAddonFormatException("Addon '" + f.getName() + "' doesn't contains addon.yml file");
|
addons.add(addon);
|
||||||
}
|
|
||||||
// Open a reader to the jar
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(jar.getInputStream(entry)));
|
|
||||||
// Grab the description in the addon.yml file
|
|
||||||
YamlConfiguration data = new YamlConfiguration();
|
|
||||||
data.load(reader);
|
|
||||||
// Load the addon
|
|
||||||
AddonClassLoader addonClassLoader = new AddonClassLoader(this, data, f, this.getClass().getClassLoader());
|
|
||||||
// Add to the list of loaders
|
|
||||||
loader.add(addonClassLoader);
|
|
||||||
|
|
||||||
// Get the addon itself
|
|
||||||
addon = addonClassLoader.getAddon();
|
|
||||||
// Initialize some settings
|
|
||||||
addon.setDataFolder(new File(f.getParent(), addon.getDescription().getName()));
|
|
||||||
addon.setAddonFile(f);
|
|
||||||
|
|
||||||
File localeDir = new File(plugin.getDataFolder(), LOCALE_FOLDER + File.separator + addon.getDescription().getName());
|
|
||||||
// Obtain any locale files and save them
|
|
||||||
for (String localeFile : listJarYamlFiles(jar, LOCALE_FOLDER)) {
|
|
||||||
addon.saveResource(localeFile, localeDir, false, true);
|
|
||||||
}
|
|
||||||
plugin.getLocalesManager().loadLocalesFromFile(addon.getDescription().getName());
|
|
||||||
// Fire the load event
|
|
||||||
Bukkit.getPluginManager().callEvent(AddonEvent.builder().addon(addon).reason(AddonEvent.Reason.LOAD).build());
|
|
||||||
// Add it to the list of addons
|
|
||||||
addons.add(addon);
|
|
||||||
|
|
||||||
// Inform the console
|
|
||||||
plugin.log("Loaded addon " + addon.getDescription().getName() + "...");
|
|
||||||
} catch (Exception e) {
|
|
||||||
plugin.log(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (DEBUG) {
|
plugin.log(e.getMessage());
|
||||||
plugin.log(f.getName() + " is not a jarfile, ignoring...");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,4 +216,41 @@ public class AddonsManager {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sortAddons() {
|
||||||
|
Map<String,Addon> sortedAddons = new LinkedHashMap<>();
|
||||||
|
Map<Addon, List<String>> remaining = new HashMap<>();
|
||||||
|
// Start with nodes with no dependencies
|
||||||
|
addons.stream().filter(a -> a.getDescription().getDependencies().isEmpty()).forEach(a -> sortedAddons.put(a.getDescription().getName(), a));
|
||||||
|
// Fill remaining
|
||||||
|
addons.stream().filter(a -> !a.getDescription().getDependencies().isEmpty()).forEach(a -> remaining.put(a, a.getDescription().getDependencies()));
|
||||||
|
// Run through remaining addons
|
||||||
|
int index = 0;
|
||||||
|
while (index < 10 && !remaining.isEmpty()) {
|
||||||
|
index++;
|
||||||
|
Iterator<Entry<Addon, List<String>>> it = remaining.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Entry<Addon, List<String>> a = it.next();
|
||||||
|
// If the dependent addon is loaded
|
||||||
|
List<String> deps = new ArrayList<>(a.getValue());
|
||||||
|
Iterator<String> depIt = deps.iterator();
|
||||||
|
while(depIt.hasNext()) {
|
||||||
|
String dep = depIt.next();
|
||||||
|
if (sortedAddons.containsKey(dep)) {
|
||||||
|
depIt.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deps.isEmpty()) {
|
||||||
|
// Add addons loaded
|
||||||
|
sortedAddons.put(a.getKey().getDescription().getName(), a.getKey());
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index == 10) {
|
||||||
|
plugin.logError("Circular dependency reference when loading addons");
|
||||||
|
}
|
||||||
|
addons.clear();
|
||||||
|
sortedAddons.values().forEach(addons::add);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user