mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-04-08 08:46:13 +00:00
fleshed out addon loading
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
package com.dfsek.terra.addons.annotations.inject;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Inject {
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
|
||||
import com.dfsek.terra.addons.addon.TerraAddon;
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
|
||||
public interface EventManager {
|
||||
@@ -10,5 +11,5 @@ public interface EventManager {
|
||||
*/
|
||||
boolean callEvent(Event event);
|
||||
|
||||
void registerListener(EventListener listener);
|
||||
void registerListener(TerraAddon addon, EventListener listener);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
|
||||
import com.dfsek.terra.addons.addon.TerraAddon;
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.core.event.annotations.Global;
|
||||
import com.dfsek.terra.api.core.event.annotations.Priority;
|
||||
import com.dfsek.terra.api.core.event.events.Cancellable;
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.api.core.event.events.PackEvent;
|
||||
import com.dfsek.terra.api.util.ReflectionUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -29,7 +32,14 @@ public class TerraEventManager implements EventManager {
|
||||
public boolean callEvent(Event event) {
|
||||
listeners.getOrDefault(event.getClass(), Collections.emptyList()).forEach(listenerHolder -> {
|
||||
try {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
if(event instanceof PackEvent && !listenerHolder.global) {
|
||||
PackEvent packEvent = (PackEvent) event;
|
||||
if(packEvent.getPack().getTemplate().getAddons().contains(listenerHolder.addon)) {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
}
|
||||
} else {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
}
|
||||
} catch(InvocationTargetException e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
e.getTargetException().printStackTrace(new PrintWriter(writer));
|
||||
@@ -51,7 +61,7 @@ public class TerraEventManager implements EventManager {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void registerListener(EventListener listener) {
|
||||
public void registerListener(TerraAddon addon, EventListener listener) {
|
||||
Class<? extends EventListener> listenerClass = listener.getClass();
|
||||
Method[] methods = ReflectionUtil.getMethods(listenerClass);
|
||||
|
||||
@@ -68,7 +78,7 @@ public class TerraEventManager implements EventManager {
|
||||
|
||||
List<ListenerHolder> holders = listeners.computeIfAbsent((Class<? extends Event>) eventParam, e -> new ArrayList<>());
|
||||
|
||||
holders.add(new ListenerHolder(method, listener, priority));
|
||||
holders.add(new ListenerHolder(method, listener, priority, addon, method.getAnnotation(Global.class) != null));
|
||||
|
||||
holders.sort(Comparator.comparingInt(ListenerHolder::getPriority)); // Sort priorities.
|
||||
}
|
||||
@@ -78,11 +88,15 @@ public class TerraEventManager implements EventManager {
|
||||
private final Method method;
|
||||
private final EventListener listener;
|
||||
private final int priority;
|
||||
private final TerraAddon addon;
|
||||
private final boolean global;
|
||||
|
||||
private ListenerHolder(Method method, EventListener listener, int priority) {
|
||||
private ListenerHolder(Method method, EventListener listener, int priority, TerraAddon addon, boolean global) {
|
||||
this.method = method;
|
||||
this.listener = listener;
|
||||
this.priority = priority;
|
||||
this.addon = addon;
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package com.dfsek.terra.api.core.event.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies that an event handler is to handle all events.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Global {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.dfsek.terra.api.core.event.events;
|
||||
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
|
||||
public interface PackEvent extends Event {
|
||||
ConfigPack getPack();
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
package com.dfsek.terra.api.core.event.events.config;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.api.core.event.events.PackEvent;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
public abstract class ConfigPackLoadEvent implements Event {
|
||||
public abstract class ConfigPackLoadEvent implements PackEvent {
|
||||
private final ConfigPack pack;
|
||||
|
||||
public ConfigPackLoadEvent(ConfigPack pack) {
|
||||
this.pack = pack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigPack getPack() {
|
||||
return pack;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package com.dfsek.terra.api.core.event.events.world;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.api.core.event.events.PackEvent;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
/**
|
||||
* Called upon initialization of a TerraWorld.
|
||||
*/
|
||||
public class TerraWorldLoadEvent implements Event {
|
||||
public class TerraWorldLoadEvent implements PackEvent {
|
||||
private final TerraWorld world;
|
||||
|
||||
public TerraWorldLoadEvent(TerraWorld world) {
|
||||
@@ -16,4 +17,9 @@ public class TerraWorldLoadEvent implements Event {
|
||||
public TerraWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigPack getPack() {
|
||||
return world.getConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.dfsek.terra.config;
|
||||
|
||||
import com.dfsek.tectonic.loading.TypeRegistry;
|
||||
import com.dfsek.terra.addons.addon.TerraAddon;
|
||||
import com.dfsek.terra.api.LoaderRegistrar;
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.math.GridSpawn;
|
||||
@@ -90,6 +91,7 @@ public class GenericLoaders implements LoaderRegistrar {
|
||||
.registerLoader(CarverPalette.class, new CarverPaletteLoader())
|
||||
.registerLoader(SourceSeeded.class, new SourceBuilderLoader())
|
||||
.registerLoader(StageSeeded.class, new StageBuilderLoader())
|
||||
.registerLoader(TerraAddon.class, main.getAddons())
|
||||
.registerLoader(BiomeProvider.BiomeProviderBuilder.class, new BiomeProviderBuilderLoader())
|
||||
.registerLoader(ImageSampler.Channel.class, (t, object, cf) -> ImageSampler.Channel.valueOf((String) object))
|
||||
.registerLoader(BiomeProvider.Type.class, (t, object, cf) -> BiomeProvider.Type.valueOf((String) object))
|
||||
|
||||
@@ -3,12 +3,14 @@ package com.dfsek.terra.config.pack;
|
||||
import com.dfsek.tectonic.annotations.Default;
|
||||
import com.dfsek.tectonic.annotations.Value;
|
||||
import com.dfsek.tectonic.config.ConfigTemplate;
|
||||
import com.dfsek.terra.addons.addon.TerraAddon;
|
||||
import com.dfsek.terra.api.util.seeded.NoiseSeeded;
|
||||
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings({"unused", "FieldMayBeFinal"})
|
||||
public class ConfigPackTemplate implements ConfigTemplate {
|
||||
@@ -18,6 +20,10 @@ public class ConfigPackTemplate implements ConfigTemplate {
|
||||
@Value("noise")
|
||||
private Map<String, NoiseSeeded> noiseBuilderMap;
|
||||
|
||||
@Value("addons")
|
||||
@Default
|
||||
private Set<TerraAddon> addons;
|
||||
|
||||
@Value("variables")
|
||||
@Default
|
||||
private Map<String, Double> variables = new HashMap<>();
|
||||
@@ -121,4 +127,8 @@ public class ConfigPackTemplate implements ConfigTemplate {
|
||||
public boolean doBetaCarvers() {
|
||||
return betaCarvers;
|
||||
}
|
||||
|
||||
public Set<TerraAddon> getAddons() {
|
||||
return addons;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.dfsek.terra.registry;
|
||||
|
||||
import com.dfsek.terra.addons.addon.TerraAddon;
|
||||
import com.dfsek.terra.addons.annotations.Addon;
|
||||
import com.dfsek.terra.addons.annotations.Depends;
|
||||
import com.dfsek.terra.addons.loading.AddonClassLoader;
|
||||
import com.dfsek.terra.addons.loading.AddonLoadException;
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
@@ -9,48 +11,88 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AddonRegistry extends TerraRegistry<TerraAddon> {
|
||||
public boolean loadAll(TerraPlugin main) {
|
||||
private final TerraPlugin main;
|
||||
|
||||
public AddonRegistry(TerraPlugin main) {
|
||||
|
||||
this.main = main;
|
||||
}
|
||||
|
||||
public AddonRegistry(TerraAddon addon, TerraPlugin main) {
|
||||
this.main = main;
|
||||
add(addon.getName(), addon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(String name, TerraAddon addon) {
|
||||
addon.initialize();
|
||||
main.getLogger().info("Loaded addon " + addon.getName() + " v" + addon.getVersion() + ", by " + addon.getAuthor());
|
||||
return super.add(name, addon);
|
||||
}
|
||||
|
||||
public boolean loadAll() {
|
||||
boolean valid = true;
|
||||
File addonsFolder = new File(main.getDataFolder(), "addons");
|
||||
addonsFolder.mkdirs();
|
||||
for(File jar : addonsFolder.listFiles(file -> file.getName().endsWith(".jar"))) {
|
||||
try {
|
||||
|
||||
Map<String, Class<? extends TerraAddon>> addonIDs = new HashMap<>();
|
||||
|
||||
try {
|
||||
for(File jar : addonsFolder.listFiles(file -> file.getName().endsWith(".jar"))) {
|
||||
|
||||
main.getLogger().info("Loading Addon(s) from: " + jar.getName());
|
||||
load(jar, main);
|
||||
} catch(IOException | AddonLoadException e) {
|
||||
e.printStackTrace();
|
||||
valid = false;
|
||||
|
||||
Set<Class<? extends TerraAddon>> addonClasses = AddonClassLoader.fetchAddonClasses(jar);
|
||||
|
||||
for(Class<? extends TerraAddon> addonClass : addonClasses) {
|
||||
String id = addonClass.getAnnotation(Addon.class).value();
|
||||
if(addonIDs.containsKey(id))
|
||||
throw new AddonLoadException("Duplicate addon ID: " + id);
|
||||
addonIDs.put(id, addonClass);
|
||||
}
|
||||
}
|
||||
|
||||
for(Map.Entry<String, Class<? extends TerraAddon>> entry : addonIDs.entrySet()) {
|
||||
Class<? extends TerraAddon> addonClass = entry.getValue();
|
||||
|
||||
Depends dependencies = addonClass.getAnnotation(Depends.class);
|
||||
|
||||
if(dependencies != null) {
|
||||
for(String dependency : dependencies.value()) {
|
||||
if(!addonIDs.containsKey(dependency))
|
||||
throw new AddonLoadException("Addon " + entry.getKey() + " specifies dependency " + dependency + ", which is not loaded. Please install " + dependency + " to use " + entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
Constructor<? extends TerraAddon> constructor;
|
||||
try {
|
||||
constructor = addonClass.getConstructor();
|
||||
} catch(NoSuchMethodException e) {
|
||||
throw new AddonLoadException("Addon class has no valid constructor: " + addonClass.getCanonicalName(), e);
|
||||
}
|
||||
TerraAddon addon;
|
||||
try {
|
||||
addon = constructor.newInstance();
|
||||
} catch(InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AddonLoadException("Failed to instantiate addon: " + addonClass.getCanonicalName(), e);
|
||||
}
|
||||
try {
|
||||
addChecked(addon.getName(), addon);
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw new AddonLoadException("Duplicate addon ID; addon with ID " + addon.getName() + " is already loaded.");
|
||||
}
|
||||
}
|
||||
} catch(IOException | AddonLoadException e) {
|
||||
e.printStackTrace();
|
||||
valid = false;
|
||||
main.getLogger().severe("Addons failed to load. Please ensure all addons are properly installed.");
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void load(File file, TerraPlugin main) throws AddonLoadException, IOException {
|
||||
Set<Class<? extends TerraAddon>> addonClasses = AddonClassLoader.fetchAddonClasses(file);
|
||||
|
||||
for(Class<? extends TerraAddon> addonClass : addonClasses) {
|
||||
Constructor<? extends TerraAddon> constructor;
|
||||
try {
|
||||
constructor = addonClass.getConstructor();
|
||||
} catch(NoSuchMethodException e) {
|
||||
throw new AddonLoadException("Addon class has no valid constructor: " + addonClass.getCanonicalName(), e);
|
||||
}
|
||||
TerraAddon addon;
|
||||
try {
|
||||
addon = constructor.newInstance();
|
||||
} catch(InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AddonLoadException("Failed to instantiate addon: " + addonClass.getCanonicalName(), e);
|
||||
}
|
||||
try {
|
||||
addChecked(addon.getName(), addon);
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw new AddonLoadException("Duplicate addon ID; addon with ID " + addon.getName() + " is already loaded.");
|
||||
}
|
||||
main.getLogger().info("Loaded addon " + addon.getName() + " v" + addon.getVersion() + ", by " + addon.getAuthor());
|
||||
addon.initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public abstract class TerraRegistry<T> implements TypeLoader<T> {
|
||||
|
||||
public void addChecked(String name, T value) {
|
||||
if(objects.containsKey(name)) throw new IllegalArgumentException("Value is already defined in registry.");
|
||||
objects.put(name, value);
|
||||
add(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user