Added support for softdepend command in addon.yml.

Addons can now be sure to be loaded after others that may or may not
exist.
This commit is contained in:
tastybento 2018-08-17 21:58:11 -07:00
parent 9adabc4fb2
commit 46c1ff4761
3 changed files with 61 additions and 22 deletions

View File

@ -88,6 +88,9 @@ public class AddonClassLoader extends URLClassLoader {
if (data.getString("depend") != null) { if (data.getString("depend") != null) {
adb.withDepend(Arrays.asList(data.getString("depend").split("\\s*,\\s*"))); adb.withDepend(Arrays.asList(data.getString("depend").split("\\s*,\\s*")));
} }
if (data.getString("softdepend") != null) {
adb.withSoftDepend(Arrays.asList(data.getString("softdepend").split("\\s*,\\s*")));
}
return adb.build(); return adb.build();
} }

View File

@ -14,17 +14,19 @@ public final class AddonDescription {
private String version; private String version;
private String description; private String description;
private List<String> authors = new ArrayList<>(); private List<String> authors = new ArrayList<>();
private List<String> depend = new ArrayList<>(); private List<String> dependencies = new ArrayList<>();
private List<String> softDependencies = new ArrayList<>();
public AddonDescription() {} public AddonDescription() {}
public AddonDescription(String main, String name, String version, String description, List<String> authors, List<String> depend) { public AddonDescription(String main, String name, String version, String description, List<String> authors, List<String> dependencies, List<String> softDependencies) {
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; this.dependencies = dependencies;
this.softDependencies = softDependencies;
} }
/** /**
@ -63,17 +65,31 @@ public final class AddonDescription {
} }
/** /**
* @return the loadAfter * @return the dependencies
*/ */
public List<String> getDependencies() { public List<String> getDependencies() {
return depend; return dependencies;
} }
/** /**
* @param loadAfter the loadAfter to set * @return the softDependencies
*/ */
public void setLoadAfter(List<String> loadAfter) { public List<String> getSoftDependencies() {
this.depend = loadAfter; return softDependencies;
}
/**
* @param dependencies the dependencies to set
*/
public void setDependencies(List<String> dependencies) {
this.dependencies = dependencies;
}
/**
* @param softDependencies the softDependencies to set
*/
public void setSoftDependencies(List<String> softDependencies) {
this.softDependencies = softDependencies;
} }
public String getName() { public String getName() {
@ -121,7 +137,12 @@ public final class AddonDescription {
} }
public AddonDescriptionBuilder withDepend(List<String> addons) { public AddonDescriptionBuilder withDepend(List<String> addons) {
description.setLoadAfter(addons); description.setDependencies(addons);
return this;
}
public AddonDescriptionBuilder withSoftDepend(List<String> addons) {
description.setSoftDependencies(addons);
return this; return this;
} }
@ -130,4 +151,5 @@ public final class AddonDescription {
} }
} }
} }

View File

@ -12,11 +12,11 @@ import java.util.Iterator;
import java.util.LinkedHashMap; 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 java.util.stream.Collectors;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
@ -218,21 +218,38 @@ public class AddonsManager {
} }
private void sortAddons() { private void sortAddons() {
// Check that any dependencies exist
List<String> names = addons.stream().map(a -> a.getDescription().getName()).collect(Collectors.toList());
Iterator<Addon> ita = addons.iterator();
while (ita.hasNext()) {
Addon a = ita.next();
for (String dep : a.getDescription().getDependencies()) {
if (!names.contains(dep)) {
plugin.logError(a.getDescription().getName() + " has dependency on " + dep + " that does not exist. Addon will not load!");
ita.remove();
break;
}
}
}
// Load dependencies or soft dependencies
Map<String,Addon> sortedAddons = new LinkedHashMap<>(); Map<String,Addon> sortedAddons = new LinkedHashMap<>();
Map<Addon, List<String>> remaining = new HashMap<>(); List<Addon> remaining = new ArrayList<>();
// Start with nodes with no dependencies // Start with nodes with no dependencies
addons.stream().filter(a -> a.getDescription().getDependencies().isEmpty()).forEach(a -> sortedAddons.put(a.getDescription().getName(), a)); addons.stream().filter(a -> a.getDescription().getDependencies().isEmpty() && a.getDescription().getSoftDependencies().isEmpty())
.forEach(a -> sortedAddons.put(a.getDescription().getName(), a));
// Fill remaining // Fill remaining
addons.stream().filter(a -> !a.getDescription().getDependencies().isEmpty()).forEach(a -> remaining.put(a, a.getDescription().getDependencies())); remaining = addons.stream().filter(a -> !sortedAddons.containsKey(a.getDescription().getName())).collect(Collectors.toList());
// Run through remaining addons // Run through remaining addons
int index = 0; int index = 0;
while (index < 10 && !remaining.isEmpty()) { while (index < 10 && !remaining.isEmpty()) {
index++; index++;
Iterator<Entry<Addon, List<String>>> it = remaining.entrySet().iterator(); Iterator<Addon> it = remaining.iterator();
while (it.hasNext()) { while (it.hasNext()) {
Entry<Addon, List<String>> a = it.next(); Addon a = it.next();
// If the dependent addon is loaded // Check if dependencies are loaded - make a list of all hard and soft deps
List<String> deps = new ArrayList<>(a.getValue()); List<String> deps = new ArrayList<>(a.getDescription().getDependencies());
deps.addAll(a.getDescription().getSoftDependencies());
Iterator<String> depIt = deps.iterator(); Iterator<String> depIt = deps.iterator();
while(depIt.hasNext()) { while(depIt.hasNext()) {
String dep = depIt.next(); String dep = depIt.next();
@ -240,16 +257,13 @@ public class AddonsManager {
depIt.remove(); depIt.remove();
} }
} }
if (deps.isEmpty()) { if (deps.stream().allMatch(s -> !a.getDescription().getDependencies().contains(s))) {
// Add addons loaded // Add addons loaded
sortedAddons.put(a.getKey().getDescription().getName(), a.getKey()); sortedAddons.put(a.getDescription().getName(), a);
it.remove(); it.remove();
} }
} }
} }
if (index == 10) {
plugin.logError("Circular dependency reference when loading addons");
}
addons.clear(); addons.clear();
sortedAddons.values().forEach(addons::add); sortedAddons.values().forEach(addons::add);
} }