mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-02-16 10:30:42 +00:00
addon dependency sorting
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
dependencies {
|
||||
"shadedApi"(project(":common:api:util"))
|
||||
"shadedApi"("ca.solo-studios:strata:1.0.0")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
package com.dfsek.terra.api.addon;
|
||||
|
||||
import ca.solostudios.strata.version.Version;
|
||||
import ca.solostudios.strata.version.VersionRange;
|
||||
|
||||
import com.dfsek.terra.api.util.StringIdentifiable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public interface BaseAddon extends StringIdentifiable {
|
||||
default void initialize() { }
|
||||
|
||||
default Map<String, VersionRange> getDependencies() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Version getVersion();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.dfsek.terra.api.addon;
|
||||
|
||||
|
||||
import ca.solostudios.strata.Versions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.dfsek.terra.api.addon.annotations.Addon;
|
||||
@@ -11,6 +12,8 @@ import com.dfsek.terra.api.addon.annotations.Version;
|
||||
/**
|
||||
* Represents an entry point for an com.dfsek.terra.addon. Implementations must be annotated with {@link Addon}.
|
||||
*/
|
||||
|
||||
//todo delete this
|
||||
public abstract class TerraAddon implements BaseAddon {
|
||||
/**
|
||||
* Invoked immediately after an com.dfsek.terra.addon is loaded.
|
||||
@@ -22,9 +25,9 @@ public abstract class TerraAddon implements BaseAddon {
|
||||
*
|
||||
* @return Addon version.
|
||||
*/
|
||||
public final @NotNull String getVersion() {
|
||||
public final @NotNull ca.solostudios.strata.version.Version getVersion() {
|
||||
Version version = getClass().getAnnotation(Version.class);
|
||||
return version == null ? "0.1.0" : version.value();
|
||||
return Versions.getVersion(1, 2, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,11 +13,13 @@ import java.io.OutputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.dfsek.terra.addon.BootstrapAddonLoader;
|
||||
import com.dfsek.terra.addon.DependencySorter;
|
||||
import com.dfsek.terra.api.Platform;
|
||||
import com.dfsek.terra.api.addon.BaseAddon;
|
||||
import com.dfsek.terra.api.command.CommandManager;
|
||||
@@ -137,8 +139,6 @@ public abstract class AbstractPlatform implements Platform {
|
||||
|
||||
logger().info("Initializing Terra...");
|
||||
|
||||
getPlatformAddon().ifPresent(addon -> addonRegistry.register(addon.getID(), addon));
|
||||
|
||||
try(InputStream stream = getClass().getResourceAsStream("/config.yml")) {
|
||||
File configFile = new File(getDataFolder(), "config.yml");
|
||||
if(!configFile.exists()) {
|
||||
@@ -192,13 +192,17 @@ public abstract class AbstractPlatform implements Platform {
|
||||
profiler.start();
|
||||
}
|
||||
|
||||
List<BaseAddon> addonList = new ArrayList<>();
|
||||
|
||||
InternalAddon internalAddon = new InternalAddon();
|
||||
|
||||
addonRegistry.register(internalAddon.getID(), internalAddon);
|
||||
addonList.add(internalAddon);
|
||||
|
||||
getPlatformAddon().ifPresent(addonList::add);
|
||||
|
||||
platformAddon().ifPresent(baseAddon -> {
|
||||
baseAddon.initialize();
|
||||
addonRegistry.register(baseAddon.getID(), baseAddon);
|
||||
addonList.add(baseAddon);
|
||||
});
|
||||
|
||||
BootstrapAddonLoader bootstrapAddonLoader = new BootstrapAddonLoader(this);
|
||||
@@ -212,13 +216,17 @@ public abstract class AbstractPlatform implements Platform {
|
||||
.forEach(bootstrap -> {
|
||||
platformInjector.inject(bootstrap);
|
||||
bootstrap.loadAddons(addonsFolder, getClass().getClassLoader())
|
||||
.forEach(addon -> {
|
||||
platformInjector.inject(addon);
|
||||
addon.initialize();
|
||||
addonRegistry.register(addon.getID(), addon);
|
||||
});
|
||||
.forEach(addonList::add);
|
||||
});
|
||||
|
||||
DependencySorter sorter = new DependencySorter();
|
||||
addonList.forEach(sorter::add);
|
||||
sorter.sort().forEach(addon -> {
|
||||
platformInjector.inject(addon);
|
||||
addon.initialize();
|
||||
addonRegistry.register(addon.getID(), addon);
|
||||
});
|
||||
|
||||
eventManager
|
||||
.getHandler(FunctionalEventHandler.class)
|
||||
.register(internalAddon, PlatformInitializationEvent.class)
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
package com.dfsek.terra;
|
||||
|
||||
import ca.solostudios.strata.Versions;
|
||||
import ca.solostudios.strata.version.Version;
|
||||
|
||||
import com.dfsek.terra.api.addon.BaseAddon;
|
||||
|
||||
|
||||
public class InternalAddon implements BaseAddon {
|
||||
private static final Version VERSION = Versions.getVersion(1, 0, 0);
|
||||
@Override
|
||||
public String getID() {
|
||||
return "terra";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.dfsek.terra.addon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.dfsek.terra.addon.dependency.CircularDependencyException;
|
||||
import com.dfsek.terra.addon.dependency.DependencyException;
|
||||
import com.dfsek.terra.addon.dependency.DependencyVersionException;
|
||||
import com.dfsek.terra.api.addon.BaseAddon;
|
||||
|
||||
|
||||
public class DependencySorter {
|
||||
private final Map<String, BaseAddon> addons = new HashMap<>();
|
||||
private final Map<BaseAddon, Boolean> visited = new HashMap<>();
|
||||
|
||||
private final List<BaseAddon> addonList = new ArrayList<>();
|
||||
|
||||
public void add(BaseAddon addon) {
|
||||
addons.put(addon.getID(), addon);
|
||||
visited.put(addon, false);
|
||||
addonList.add(addon);
|
||||
}
|
||||
|
||||
private void sortDependencies(BaseAddon addon, List<BaseAddon> sort) {
|
||||
addon.getDependencies().forEach((id, range) -> {
|
||||
if(!addons.containsKey(id)) {
|
||||
throw new DependencyException("Addon " + addon.getID() + " specifies dependency on " + id + ", versions " + range + ", but no such addon is installed.");
|
||||
}
|
||||
|
||||
BaseAddon dependency = addons.get(id);
|
||||
|
||||
if(!range.isSatisfiedBy(dependency.getVersion())) {
|
||||
throw new DependencyVersionException("Addon " + addon.getID() + " specifies dependency on " + id + ", versions " + range + ", but non-matching version " + dependency.getVersion() + " is installed..");
|
||||
}
|
||||
|
||||
if(!visited.get(dependency)) { // if we've not visited it yet
|
||||
visited.put(dependency, true); // we've visited it now
|
||||
|
||||
if(!dependency.getDependencies().isEmpty()) { // if this addon has dependencies...
|
||||
sortDependencies(dependency, sort); // sort them first.
|
||||
}
|
||||
|
||||
if(sort.contains(dependency)) {
|
||||
throw new CircularDependencyException("Addon " + addon.getID() + " has circular dependency beginning with " + dependency.getID());
|
||||
}
|
||||
|
||||
sort.add(dependency); // add it to the list.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<BaseAddon> sort() {
|
||||
List<BaseAddon> sorted = new ArrayList<>();
|
||||
|
||||
for(int i = addonList.size() - 1; i >= 0; i--) {
|
||||
BaseAddon addon = addonList.get(i);
|
||||
addonList.remove(i);
|
||||
|
||||
if(!visited.get(addon)) { // if we've not visited it yet
|
||||
visited.put(addon, true); // we've visited it now
|
||||
if(!addon.getDependencies().isEmpty()) { // if this addon has dependencies...
|
||||
sortDependencies(addon, sorted);
|
||||
}
|
||||
}
|
||||
|
||||
sorted.add(addon);
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.dfsek.terra.addon.dependency;
|
||||
|
||||
public class CircularDependencyException extends DependencyException{
|
||||
public CircularDependencyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CircularDependencyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addon.dependency;
|
||||
|
||||
|
||||
|
||||
public class DependencyException extends RuntimeException {
|
||||
public DependencyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DependencyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.dfsek.terra.addon.dependency;
|
||||
|
||||
public class DependencyVersionException extends DependencyException{
|
||||
public DependencyVersionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DependencyVersionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,9 @@ import java.nio.file.Path;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ca.solostudios.strata.Versions;
|
||||
import ca.solostudios.strata.version.Version;
|
||||
|
||||
import com.dfsek.terra.addon.exception.AddonLoadException;
|
||||
import com.dfsek.terra.api.Platform;
|
||||
import com.dfsek.terra.api.addon.bootstrap.BootstrapBaseAddon;
|
||||
@@ -17,6 +20,8 @@ import com.dfsek.terra.api.addon.bootstrap.BootstrapBaseAddon;
|
||||
public class BootstrapAddonLoader implements BootstrapBaseAddon<BootstrapBaseAddon<?>> {
|
||||
private final Platform platform;
|
||||
|
||||
private static final Version VERSION = Versions.getVersion(1, 0, 0);
|
||||
|
||||
public BootstrapAddonLoader(Platform platform) { this.platform = platform; }
|
||||
|
||||
@Override
|
||||
@@ -66,4 +71,9 @@ public class BootstrapAddonLoader implements BootstrapBaseAddon<BootstrapBaseAdd
|
||||
public String getID() {
|
||||
return "BOOTSTRAP";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.dfsek.terra.fabric;
|
||||
|
||||
import ca.solostudios.strata.Versions;
|
||||
import ca.solostudios.strata.version.Version;
|
||||
import com.dfsek.tectonic.exception.ConfigException;
|
||||
|
||||
import com.dfsek.terra.api.addon.BaseAddon;
|
||||
@@ -16,7 +18,6 @@ import java.util.Map;
|
||||
import com.dfsek.terra.api.addon.TerraAddon;
|
||||
import com.dfsek.terra.api.addon.annotations.Addon;
|
||||
import com.dfsek.terra.api.addon.annotations.Author;
|
||||
import com.dfsek.terra.api.addon.annotations.Version;
|
||||
import com.dfsek.terra.api.config.ConfigPack;
|
||||
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPostLoadEvent;
|
||||
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
|
||||
@@ -33,6 +34,7 @@ import com.dfsek.terra.fabric.util.FabricUtil;
|
||||
|
||||
|
||||
public final class FabricAddon implements BaseAddon {
|
||||
private static final Version VERSION = Versions.getVersion(1, 0, 0);
|
||||
private final PlatformImpl terraFabricPlugin;
|
||||
private final Map<ConfigPack, Pair<PreLoadCompatibilityOptions, PostLoadCompatibilityOptions>> templates = new HashMap<>();
|
||||
|
||||
@@ -107,6 +109,11 @@ public final class FabricAddon implements BaseAddon {
|
||||
.global();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
|
||||
private void injectTree(CheckedRegistry<Tree> registry, String id, ConfiguredFeature<?, ?> tree) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user