diff --git a/minecraft/src/main/resources/frog.mod.toml b/minecraft/src/main/resources/frog.mod.toml
index ef25ba2..f6de61e 100644
--- a/minecraft/src/main/resources/frog.mod.toml
+++ b/minecraft/src/main/resources/frog.mod.toml
@@ -10,8 +10,8 @@ credits = [
{ name = "You", roles = ["author", "other_role"] }
]
-[frog.extensions]
-prelaunch = "dev.frogmc.frogloader.example.ExamplePreLaunchExtension"
+[frog.extensions.frogloader]
+prelaunch = "dev.frogmc.frogloader.example.ExamplePreLaunchExtension::onPreLaunch"
mixin = "example_mod.mixins.json"
accesswidener = "example_mod.accesswidener"
modprovider = "dev.frogmc.frogloader.example.ExampleModProvider"
diff --git a/minecraft/src/test/resources/frog.mod.toml b/minecraft/src/test/resources/frog.mod.toml
index ce51f13..877614b 100644
--- a/minecraft/src/test/resources/frog.mod.toml
+++ b/minecraft/src/test/resources/frog.mod.toml
@@ -10,6 +10,6 @@ credits = [
{ name = "You", roles = ["author", "other_role"] }
]
-[frog.extensions]
+[frog.extensions.frogloader]
prelaunch="dev.frogmc.example.ExampleTestMod"
diff --git a/src/main/java/dev/frogmc/frogloader/api/exception/ModExtensionResolutionException.java b/src/main/java/dev/frogmc/frogloader/api/exception/ModExtensionResolutionException.java
new file mode 100644
index 0000000..27f3259
--- /dev/null
+++ b/src/main/java/dev/frogmc/frogloader/api/exception/ModExtensionResolutionException.java
@@ -0,0 +1,17 @@
+package dev.frogmc.frogloader.api.exception;
+
+public class ModExtensionResolutionException extends Exception {
+
+ public ModExtensionResolutionException(String message) {
+ super(message);
+ }
+
+ public ModExtensionResolutionException(Throwable cause) {
+ super(cause);
+ }
+
+ public ModExtensionResolutionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/main/java/dev/frogmc/frogloader/api/extensions/PreLaunchExtension.java b/src/main/java/dev/frogmc/frogloader/api/extensions/PreLaunchExtension.java
index 06e12b2..bc3bfca 100644
--- a/src/main/java/dev/frogmc/frogloader/api/extensions/PreLaunchExtension.java
+++ b/src/main/java/dev/frogmc/frogloader/api/extensions/PreLaunchExtension.java
@@ -1,7 +1,7 @@
package dev.frogmc.frogloader.api.extensions;
import dev.frogmc.frogloader.api.mod.ModExtensions;
-import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
+import dev.frogmc.frogloader.impl.Constants;
/**
* The Pre-Launch Extension.
@@ -13,7 +13,7 @@ public interface PreLaunchExtension {
/**
* This extension's id. This is the key to use in your frog.mod.toml.
*/
- String ID = BuiltinExtensions.PRE_LAUNCH;
+ String ID = Constants.EXTENSION_PRE_LAUNCH;
/**
* The initializer. This method will be invoked when this extension is run.
diff --git a/src/main/java/dev/frogmc/frogloader/api/mod/ModExtensions.java b/src/main/java/dev/frogmc/frogloader/api/mod/ModExtensions.java
index 64f7dc0..ff244b3 100644
--- a/src/main/java/dev/frogmc/frogloader/api/mod/ModExtensions.java
+++ b/src/main/java/dev/frogmc/frogloader/api/mod/ModExtensions.java
@@ -1,150 +1,74 @@
package dev.frogmc.frogloader.api.mod;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandleProxies;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.Map;
import java.util.function.Consumer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import dev.frogmc.frogloader.api.exception.ModExtensionResolutionException;
/**
* This class stores a mod's extensions.
- *
An extension is a simple key-value mapping of a string name to any value.
- * This class further provides utility methods to easily work with extension values of various types,
- * especially for extensions providing some form of class or method reference.
+ *
+ * An extension is a simple key-value mapping of a string name to any value.
+ *
+ *
+ * This class further provides utility methods to easily work with extension
+ * values of various types, especially for extensions providing some form of
+ * class or method reference.
+ *
*
* @see ModProperties
*/
-public final class ModExtensions {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ModExtensions.class);
- private final Map extensions;
-
- private ModExtensions(Map entries) {
- extensions = entries;
- }
-
- /**
- * Retrieve a new instance of this class.
- * Internal use only.
- *
- * @param entries the entries read from the mod's properties file
- * @return an instance of this class
- */
- public static ModExtensions of(Map entries) {
- return new ModExtensions(entries);
- }
+public interface ModExtensions {
/**
* Get the value of the provided extension name.
*
+ * @param modId The mod ID this extension is stored under
* @param key The extension name to query
* @param The type of the value of this extension
+ *
* @return The value of the extension, or null
*/
- @SuppressWarnings("unchecked")
- public T get(String key) {
- return (T) extensions.get(key);
- }
+ T get(String modId, String key);
/**
* Get the value of the provided extension name.
*
- * @param key The extension name to query
- * @param defaultValue a default value
- * @param The type of the value of this extension
+ * @param modId The mod ID this extension is stored under
+ * @param key The extension name to query
+ * @param defaultValue A default value
+ * @param The type of the value of this extension
+ *
* @return The value of the extension, or the default value if it isn't present
*/
- @SuppressWarnings("unchecked")
- public T getOrDefault(String key, T defaultValue) {
- return (T) extensions.getOrDefault(key, defaultValue);
- }
+ T getOrDefault(String modId, String key, T defaultValue);
/**
* Run the given action on this extension if it is present.
*
- * @param key the extension name to query
- * @param action the action to run on the value of the extension if it is present
- * @param The type of the value of this extension
+ * @param modId The mod ID this extension is stored under
+ * @param key The extension name to query
+ * @param action The action to run on the value of the extension if it is present
+ * @param The type of the value of this extension
*/
- @SuppressWarnings({"rawtypes", "unchecked"})
- public void runIfPresent(String key, Consumer action) {
- Object value = get(key);
- if (value == null) {
- return;
- }
- if (value instanceof Collection c) {
- ((Collection) c).forEach(action);
- } else {
- action.accept((T) value);
- }
- }
+ void runIfPresent(String modId, String key, Consumer action);
/**
* Run the given action on this extension if it is present.
- * This method simplifies handling references to classes or methods.
- * It will first query the string value of the extension and, if it is found, create a new instance of the referenced class
- * or invoke the referenced method to retrieve an instance of the provided class. Then the provided action is run on this
- * object.
+ *
+ * This method simplifies handling references to classes or methods.
+ *
+ * It will first query the string value of the extension and, if it is found,
+ * create a new instance of the referenced class or invoke the referenced method
+ * to retrieve an instance of the provided class. Then the provided action is
+ * run on this object.
*
- * @param key The name of the extension
- * @param type The class type of the extension (The class the extension class is extending/implementing)
+ * @param modId The mod ID this extension is stored under
+ * @param key The name of the extension
+ * @param type The class type of the extension (The class the extension class is extending/implementing)
* @param action The action to run on the newly retrieved instance of the provided class
- * @param The type of the class
+ * @param The type of the class
+ *
+ * @throws ModExtensionResolutionException If the reference could not be resolved
*/
- @SuppressWarnings({"unchecked"})
- public void runIfPresent(String key, Class type, Consumer action) {
- runIfPresent(key, (Consumer) s -> {
- try {
- T object;
- if (s.contains("::")) {
- String[] parts = s.split("::");
- Class> extension = Class.forName(parts[0]);
- object = (T) handleReference(extension, type, parts[1]);
- } else {
- object = (T) MethodHandles.lookup().findConstructor(Class.forName(s), MethodType.methodType(void.class)).invoke();
- }
- if (object != null) {
- action.accept(object);
- }
- } catch (Throwable e) {
- LOGGER.warn("Failed to instantiate Extension: ", e);
- }
- });
- }
-
- private Object handleReference(Class> clazz, Class> type, String ref) throws Throwable {
- try {
- Field found = clazz.getDeclaredField(ref);
- found.setAccessible(true);
- return found.get(null);
- } catch (Exception ignored) {
- }
-
- Method found = null;
- for (Method method : clazz.getDeclaredMethods()) {
- if (method.getName().equals(ref)) {
- if (found != null) {
- throw new IllegalArgumentException("Ambiguous method reference: "+ref+" in "+clazz.getName());
- }
- found = method;
- }
- }
-
- if (found != null) {
- MethodHandle method = MethodHandles.lookup().unreflect(found);
- if (!Modifier.isStatic(found.getModifiers())) {
- method = method.bindTo(MethodHandles.lookup().findConstructor(clazz, MethodType.methodType(void.class)).invoke());
- }
- return MethodHandleProxies.asInterfaceInstance(type, method);
- }
- throw new IllegalArgumentException("Could not find either a static field or a method named '" + ref + "' in class " + clazz.getName() + "!");
- }
+ void runIfPresent(String modId, String key, Class type, Consumer action) throws ModExtensionResolutionException;
}
diff --git a/src/main/java/dev/frogmc/frogloader/api/plugin/ModProvider.java b/src/main/java/dev/frogmc/frogloader/api/plugin/ModProvider.java
index 493c56e..1cf1096 100644
--- a/src/main/java/dev/frogmc/frogloader/api/plugin/ModProvider.java
+++ b/src/main/java/dev/frogmc/frogloader/api/plugin/ModProvider.java
@@ -65,8 +65,9 @@ public interface ModProvider {
/**
* This method will be invoked just before the game is launched. It may be used for a pre-launch entrypoint for mods.
* @param mods The mods loaded by this provider
+ * @throws Exception If an exception occurs during pre-launch. It will be handled by the loader.
*/
- default void preLaunch(Collection mods) {
+ default void preLaunch(Collection mods) throws Exception {
}
/**
diff --git a/src/main/java/dev/frogmc/frogloader/impl/Constants.java b/src/main/java/dev/frogmc/frogloader/impl/Constants.java
new file mode 100644
index 0000000..2e462aa
--- /dev/null
+++ b/src/main/java/dev/frogmc/frogloader/impl/Constants.java
@@ -0,0 +1,14 @@
+package dev.frogmc.frogloader.impl;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class Constants {
+ public final String MOD_ID = "frogloader";
+ public final String EXTENSION_MIXIN_CONFIG = "mixin";
+ public final String EXTENSION_INCLUDED_JARS = "included_jars";
+ public final String EXTENSION_PRE_LAUNCH = "prelaunch";
+ public final String EXTENSION_ACCESSWIDENER = "accesswidener";
+ public final String EXTENSION_LOADING_TYPE = "loading_type";
+ public final String EXTENSION_MOD_PROVIDER = "modprovider";
+}
diff --git a/src/main/java/dev/frogmc/frogloader/impl/FrogLoaderImpl.java b/src/main/java/dev/frogmc/frogloader/impl/FrogLoaderImpl.java
index cecd477..6f38058 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/FrogLoaderImpl.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/FrogLoaderImpl.java
@@ -78,7 +78,8 @@ public class FrogLoaderImpl implements FrogLoader {
modIds = collectModIds();
LOGGER.info(ModUtil.getModList(getMods()));
LOGGER.info("Launching...");
- modProviders.forEach(plugin -> plugin.preLaunch(mods.get(plugin.id()).values()));
+ for (ModProvider plugin : modProviders)
+ plugin.preLaunch(mods.get(plugin.id()).values());
gamePlugin.run();
} catch (Throwable t) {
LoaderGui.execReport(CrashReportGenerator.writeReport(t, getMods()), false);
diff --git a/src/main/java/dev/frogmc/frogloader/impl/PluginLoader.java b/src/main/java/dev/frogmc/frogloader/impl/PluginLoader.java
index c9b0311..79c077d 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/PluginLoader.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/PluginLoader.java
@@ -11,7 +11,6 @@ import dev.frogmc.frogloader.api.plugin.GamePlugin;
import dev.frogmc.frogloader.api.plugin.ModProvider;
import dev.frogmc.frogloader.impl.gui.LoaderGui;
import dev.frogmc.frogloader.impl.mixin.AWProcessor;
-import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
import dev.frogmc.frogloader.impl.util.CrashReportGenerator;
import org.slf4j.Logger;
@@ -47,10 +46,12 @@ public class PluginLoader {
properties.put(plugin.id(), loadedMods);
LOGGER.debug("Loaded {} mod(s) from provider: {}", loadedMods.size(), plugin.id());
- loadedMods.forEach(p -> p.extensions().runIfPresent(BuiltinExtensions.MOD_PROVIDER, ModProvider.class, provider -> {
- providers.add(provider);
- size[0]++;
- }));
+ for (ModProperties p : loadedMods) {
+ p.extensions().runIfPresent(Constants.MOD_ID, Constants.EXTENSION_MOD_PROVIDER, ModProvider.class, provider -> {
+ providers.add(provider);
+ size[0]++;
+ });
+ }
modProviders.add(plugin);
} catch (Throwable e) {
LOGGER.error("Error during plugin initialisation: ", e);
@@ -78,7 +79,7 @@ public class PluginLoader {
private static void initializeModMixins(Collection loadedMods) {
Map configs = new HashMap<>();
loadedMods.forEach(props -> {
- Object o = props.extensions().get(BuiltinExtensions.MIXIN_CONFIG);
+ Object o = props.extensions().get(Constants.MOD_ID, Constants.EXTENSION_MIXIN_CONFIG);
if (o instanceof String name) {
configs.put(name, props);
Mixins.addConfiguration(name);
diff --git a/src/main/java/dev/frogmc/frogloader/impl/mixin/AWProcessor.java b/src/main/java/dev/frogmc/frogloader/impl/mixin/AWProcessor.java
index 3a3a03b..c4452bb 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/mixin/AWProcessor.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/mixin/AWProcessor.java
@@ -14,12 +14,11 @@ import java.util.regex.Pattern;
import java.util.stream.Stream;
import dev.frogmc.frogloader.api.mod.ModProperties;
+import dev.frogmc.frogloader.impl.Constants;
import dev.frogmc.frogloader.impl.launch.transformer.AccessWidener;
-import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
public class AWProcessor {
- private static final String AW_EXTENSION_NAME = BuiltinExtensions.ACCESSWIDENER;
private static final Predicate HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate();
private static final String SEPARATOR = "[\\t ]+";
@@ -30,7 +29,7 @@ public class AWProcessor {
Map> mutations = new ConcurrentHashMap<>();
Set classNames = new ConcurrentSkipListSet<>();
- mods.stream().map(ModProperties::extensions).map(e -> e.get(AW_EXTENSION_NAME)).flatMap(o -> {
+ mods.stream().map(ModProperties::extensions).map(e -> e.get(Constants.MOD_ID, Constants.EXTENSION_ACCESSWIDENER)).flatMap(o -> {
if (o instanceof Collection> c) {
return c.stream().map(Object::toString);
}
diff --git a/src/main/java/dev/frogmc/frogloader/impl/mod/BuiltinExtensions.java b/src/main/java/dev/frogmc/frogloader/impl/mod/BuiltinExtensions.java
deleted file mode 100644
index 2bd6f47..0000000
--- a/src/main/java/dev/frogmc/frogloader/impl/mod/BuiltinExtensions.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package dev.frogmc.frogloader.impl.mod;
-
-import lombok.experimental.UtilityClass;
-
-@UtilityClass
-public class BuiltinExtensions {
- public final String MIXIN_CONFIG = "mixin";
- public final String INCLUDED_JARS = "included_jars";
- public final String PRE_LAUNCH = "prelaunch";
- public final String ACCESSWIDENER = "accesswidener";
- public final String LOADING_TYPE = "loading_type";
- public final String MOD_PROVIDER = "modprovider";
-}
diff --git a/src/main/java/dev/frogmc/frogloader/impl/mod/JavaModProperties.java b/src/main/java/dev/frogmc/frogloader/impl/mod/JavaModProperties.java
index e24467f..28443fa 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/mod/JavaModProperties.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/mod/JavaModProperties.java
@@ -25,7 +25,7 @@ public class JavaModProperties {
"",
Map.of(System.getProperty("java.vm.vendor"), Collections.singleton("Vendor")),
new ModDependencies(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()),
- ModExtensions.of(Collections.emptyMap()), Collections.emptySet());
+ new ModExtensionsImpl(Collections.emptyMap()), Collections.emptySet());
}
return INSTANCE;
}
diff --git a/src/main/java/dev/frogmc/frogloader/impl/mod/ModDependencyResolver.java b/src/main/java/dev/frogmc/frogloader/impl/mod/ModDependencyResolver.java
index e0be129..7131e45 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/mod/ModDependencyResolver.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/mod/ModDependencyResolver.java
@@ -11,6 +11,7 @@ import com.google.common.collect.MultimapBuilder;
import dev.frogmc.frogloader.api.mod.ModDependencies;
import dev.frogmc.frogloader.api.mod.ModProperties;
import dev.frogmc.frogloader.api.mod.SemVer;
+import dev.frogmc.frogloader.impl.Constants;
import dev.frogmc.frogloader.impl.SemVerParseException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -121,7 +122,7 @@ public class ModDependencyResolver {
DependencyEntry value = entry.getValue();
if (!(presentMods.containsKey(s) && value.range.versionMatches(presentOrProvided.get(s)))) { // The dependency isn't fulfilled by any present mods
if (!(provides.containsKey(s) && provides.get(s).stream().map(ProvidedMod::version).allMatch(value.range::versionMatches))) { // The dependency also isn't fulfilled by any provided mods
- if (value.origin.extensions().getOrDefault(BuiltinExtensions.LOADING_TYPE, "required").equals("required")) {
+ if (value.origin.extensions().getOrDefault(Constants.MOD_ID, Constants.EXTENSION_LOADING_TYPE, "required").equals("required")) {
unfulfilled.add(new UnfulfilledDependencyException.Entry(value.origin, s, value.range, presentOrProvided.get(s), value.link(), value.name()));
} else {
LOGGER.debug("Skipping optional mod: {} ({})", value.origin().id(), value.origin().name());
diff --git a/src/main/java/dev/frogmc/frogloader/impl/mod/ModExtensionsImpl.java b/src/main/java/dev/frogmc/frogloader/impl/mod/ModExtensionsImpl.java
new file mode 100644
index 0000000..bba8576
--- /dev/null
+++ b/src/main/java/dev/frogmc/frogloader/impl/mod/ModExtensionsImpl.java
@@ -0,0 +1,129 @@
+package dev.frogmc.frogloader.impl.mod;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandleProxies;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import dev.frogmc.frogloader.api.exception.ModExtensionResolutionException;
+import dev.frogmc.frogloader.api.mod.ModExtensions;
+
+public class ModExtensionsImpl implements ModExtensions {
+
+ private final Map map;
+
+ public ModExtensionsImpl(Map map) {
+ this.map = map;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T get(String modId, String key) {
+ return (T) map.get(new Key(modId, key));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getOrDefault(String modId, String key, T defaultValue) {
+ return (T) map.getOrDefault(new Key(modId, key), defaultValue);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void runIfPresent(String modId, String key, Consumer action) {
+ Object value = get(modId, key);
+
+ if (value == null)
+ return;
+
+ if (value instanceof Collection)
+ ((Collection) value).forEach(action);
+ else
+ action.accept((T) value);
+ }
+
+ @Override
+ public void runIfPresent(String modId, String key, Class type, Consumer action)
+ throws ModExtensionResolutionException {
+ String s = get(modId, key);
+
+ if (s == null)
+ return;
+
+ T object;
+ if (s.contains("::")) {
+ String[] parts = s.split("::", 2);
+ try {
+ Class> extension = Class.forName(parts[0]);
+ object = (T) handleReference(extension, type, parts[1]);
+ } catch (ModExtensionResolutionException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new ModExtensionResolutionException(e);
+ }
+ } else {
+ try {
+ Class> extension = Class.forName(s);
+
+ if (!type.isAssignableFrom(extension))
+ throw new ModExtensionResolutionException(extension + " does not inherit from " + type);
+
+ object = (T) MethodHandles.lookup().findConstructor(extension, MethodType.methodType(void.class))
+ .invoke();
+ } catch (ModExtensionResolutionException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new ModExtensionResolutionException(e);
+ }
+ }
+
+ if (object != null) {
+ action.accept(object);
+ }
+ }
+
+ private Object handleReference(Class> owner, Class> type, String name)
+ throws ModExtensionResolutionException, IllegalAccessException {
+ try {
+ Field found = owner.getDeclaredField(name);
+ found.setAccessible(true);
+ return found.get(null);
+ } catch (Exception ignored) {
+ }
+
+ Method found = null;
+ for (Method method : owner.getDeclaredMethods()) {
+ if (method.getName().equals(name)) {
+ if (found != null) {
+ throw new ModExtensionResolutionException("Ambiguous method reference: " + name + " in " + owner.getName());
+ }
+ found = method;
+ }
+ }
+
+ if (found != null) {
+ found.setAccessible(true);
+
+ MethodHandle method = MethodHandles.lookup().unreflect(found);
+ return MethodHandleProxies.asInterfaceInstance(type, method);
+ }
+
+ throw new IllegalArgumentException("Could not find either a static field or a method named '" + name + "' in class " + owner.getName() + "!");
+ }
+
+ public record Key(String modId, String key) {
+ }
+}
diff --git a/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java b/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java
index 2f85346..c004701 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/mod/ModPropertiesReader.java
@@ -153,16 +153,23 @@ public class ModPropertiesReader {
var suggests = parseDependencies.apply("suggests");
var provides = parseDependencies.apply("provides");
+ UnmodifiableConfig extensionsConfig = config.get("frog.extensions");
+ Map extensions = new HashMap<>();
+ if (extensionsConfig != null) {
+ for (var modEntry : extensionsConfig.entrySet()) {
+ UnmodifiableConfig modExtensionsConig = modEntry.getValue();
+
+ for (var entry : modExtensionsConig.entrySet())
+ extensions.put(new ModExtensionsImpl.Key(modEntry.getKey(), entry.getKey()), entry.getValue());
+ }
+ }
+
if (!badProperties.isEmpty())
throw new InvalidModPropertiesException(id, sources, badProperties);
- UnmodifiableConfig extensionsConfig = config.get("frog.extensions");
- Map extensions = new HashMap<>();
- if (extensionsConfig != null)
- extensionsConfig.entrySet().forEach(entry -> extensions.put(entry.getKey(), entry.getValue()));
return new ModPropertiesImpl(id, name, icon, semVer, license, Collections.unmodifiableMap(credits),
- new ModDependencies(depends, breaks, suggests, provides), ModExtensions.of(extensions), sources);
+ new ModDependencies(depends, breaks, suggests, provides), new ModExtensionsImpl(extensions), sources);
});
private static final Map versions = Arrays.stream(values()).collect(Collectors.toMap(v -> v.version, v -> v.parser));
diff --git a/src/main/java/dev/frogmc/frogloader/impl/mod/ModUtil.java b/src/main/java/dev/frogmc/frogloader/impl/mod/ModUtil.java
index 354c6cd..25574f0 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/mod/ModUtil.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/mod/ModUtil.java
@@ -6,6 +6,7 @@ import java.util.stream.Collectors;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dev.frogmc.frogloader.api.mod.ModProperties;
+import dev.frogmc.frogloader.impl.Constants;
public class ModUtil {
@@ -66,7 +67,7 @@ public class ModUtil {
private static Map> getParentMods(Collection mods) {
Map> children = new HashMap<>();
for (ModProperties mod : mods) {
- List entries = mod.extensions().get(BuiltinExtensions.INCLUDED_JARS);
+ List entries = mod.extensions().get(Constants.MOD_ID, Constants.EXTENSION_INCLUDED_JARS);
if (entries != null) {
for (var jar : entries) {
String id = jar.get("id");
diff --git a/src/main/java/dev/frogmc/frogloader/impl/plugin/game/minecraft/MinecraftGamePlugin.java b/src/main/java/dev/frogmc/frogloader/impl/plugin/game/minecraft/MinecraftGamePlugin.java
index 16f0bfe..c10eb87 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/plugin/game/minecraft/MinecraftGamePlugin.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/plugin/game/minecraft/MinecraftGamePlugin.java
@@ -21,6 +21,7 @@ import dev.frogmc.frogloader.api.mod.ModExtensions;
import dev.frogmc.frogloader.api.mod.ModProperties;
import dev.frogmc.frogloader.api.plugin.GamePlugin;
import dev.frogmc.frogloader.impl.FrogLoaderImpl;
+import dev.frogmc.frogloader.impl.mod.ModExtensionsImpl;
import dev.frogmc.frogloader.impl.mod.ModPropertiesImpl;
import dev.frogmc.frogloader.impl.util.SystemProperties;
import dev.frogmc.thyroxine.HttpHelper;
@@ -204,6 +205,6 @@ public class MinecraftGamePlugin implements GamePlugin {
MinecraftSemVerImpl.get(version), "MC-EULA",
Map.of("Mojang AB", Collections.singleton("Author")),
new ModDependencies(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()),
- ModExtensions.of(Collections.emptyMap()), Collections.emptySet());
+ new ModExtensionsImpl(Collections.emptyMap()), Collections.emptySet());
}
}
diff --git a/src/main/java/dev/frogmc/frogloader/impl/plugin/mod/FrogModProvider.java b/src/main/java/dev/frogmc/frogloader/impl/plugin/mod/FrogModProvider.java
index 6fc98bd..5129571 100644
--- a/src/main/java/dev/frogmc/frogloader/impl/plugin/mod/FrogModProvider.java
+++ b/src/main/java/dev/frogmc/frogloader/impl/plugin/mod/FrogModProvider.java
@@ -10,10 +10,11 @@ import java.util.function.Consumer;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dev.frogmc.frogloader.api.FrogLoader;
+import dev.frogmc.frogloader.api.exception.ModExtensionResolutionException;
import dev.frogmc.frogloader.api.extensions.PreLaunchExtension;
import dev.frogmc.frogloader.api.mod.ModProperties;
import dev.frogmc.frogloader.api.plugin.ModProvider;
-import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
+import dev.frogmc.frogloader.impl.Constants;
import dev.frogmc.frogloader.impl.mod.ModPropertiesReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -115,7 +116,7 @@ public class FrogModProvider implements ModProvider {
if (opt.isPresent()) {
ModProperties p = opt.get();
modPaths.put(mod, p);
- List entries = p.extensions().getOrDefault(BuiltinExtensions.INCLUDED_JARS, Collections.emptyList());
+ List entries = p.extensions().getOrDefault(Constants.MOD_ID, Constants.EXTENSION_INCLUDED_JARS, Collections.emptyList());
if (entries.isEmpty()) {
return;
}
@@ -135,7 +136,8 @@ public class FrogModProvider implements ModProvider {
}
@Override
- public void preLaunch(Collection mods) {
- mods.forEach(mod -> mod.extensions().runIfPresent(PreLaunchExtension.ID, PreLaunchExtension.class, PreLaunchExtension::onPreLaunch));
+ public void preLaunch(Collection mods) throws ModExtensionResolutionException {
+ for (ModProperties mod : mods)
+ mod.extensions().runIfPresent(Constants.MOD_ID, Constants.EXTENSION_PRE_LAUNCH, PreLaunchExtension.class, PreLaunchExtension::onPreLaunch);
}
}