From 12f9d823b4e160ab2241ebcd01b4f95c1933e5e2 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 1 Sep 2024 14:09:22 +0100 Subject: [PATCH] Require extensions to be namespaced --- minecraft/src/main/resources/frog.mod.toml | 4 +- minecraft/src/test/resources/frog.mod.toml | 2 +- .../ModExtensionResolutionException.java | 17 ++ .../api/extensions/PreLaunchExtension.java | 4 +- .../frogloader/api/mod/ModExtensions.java | 152 +++++------------- .../frogloader/api/plugin/ModProvider.java | 3 +- .../dev/frogmc/frogloader/impl/Constants.java | 14 ++ .../frogloader/impl/FrogLoaderImpl.java | 3 +- .../frogmc/frogloader/impl/PluginLoader.java | 13 +- .../frogloader/impl/mixin/AWProcessor.java | 5 +- .../impl/mod/BuiltinExtensions.java | 13 -- .../impl/mod/JavaModProperties.java | 2 +- .../impl/mod/ModDependencyResolver.java | 3 +- .../impl/mod/ModExtensionsImpl.java | 129 +++++++++++++++ .../impl/mod/ModPropertiesReader.java | 17 +- .../frogmc/frogloader/impl/mod/ModUtil.java | 3 +- .../game/minecraft/MinecraftGamePlugin.java | 3 +- .../impl/plugin/mod/FrogModProvider.java | 10 +- 18 files changed, 241 insertions(+), 156 deletions(-) create mode 100644 src/main/java/dev/frogmc/frogloader/api/exception/ModExtensionResolutionException.java create mode 100644 src/main/java/dev/frogmc/frogloader/impl/Constants.java delete mode 100644 src/main/java/dev/frogmc/frogloader/impl/mod/BuiltinExtensions.java create mode 100644 src/main/java/dev/frogmc/frogloader/impl/mod/ModExtensionsImpl.java 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); } }