decorate Mixin configs, fix extension invocation for references
All checks were successful
Publish to snapshot maven / build (push) Successful in 22s

- closes #13
This commit is contained in:
moehreag 2024-08-06 14:12:17 +02:00
parent a4ab17f5a8
commit 0715c7620d
3 changed files with 54 additions and 24 deletions

View file

@ -7,7 +7,7 @@ plugins {
} }
group = "dev.frogmc" group = "dev.frogmc"
version = "0.0.1-alpha.17" version = "0.0.1-alpha.18"
repositories { repositories {
maven { maven {

View file

@ -1,8 +1,12 @@
package dev.frogmc.frogloader.api.mod; package dev.frogmc.frogloader.api.mod;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; 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.Collection;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -73,9 +77,12 @@ public final class ModExtensions {
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public <T> void runIfPresent(String key, Consumer<T> action) { public <T> void runIfPresent(String key, Consumer<T> action) {
Object value = get(key); Object value = get(key);
if (value == null) {
return;
}
if (value instanceof Collection c) { if (value instanceof Collection c) {
((Collection<T>) c).forEach(action); ((Collection<T>) c).forEach(action);
} else if (value != null) { } else {
action.accept((T) value); action.accept((T) value);
} }
} }
@ -92,37 +99,52 @@ public final class ModExtensions {
* @param action The action to run on the newly retrieved instance of the provided class * @param action The action to run on the newly retrieved instance of the provided class
* @param <T> The type of the class * @param <T> The type of the class
*/ */
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"unchecked"})
public <T> void runIfPresent(String key, Class<T> type, Consumer<T> action) { public <T> void runIfPresent(String key, Class<T> type, Consumer<T> action) {
runIfPresent(key, (Consumer<String>) s -> {
Object value = get(key);
if (value == null) {
return;
}
Consumer<String> c = s -> {
try { try {
MethodHandle handle; T object;
if (s.contains("::")) { if (s.contains("::")) {
String[] parts = s.split("::"); String[] parts = s.split("::");
handle = MethodHandles.lookup().findVirtual(Class.forName(parts[0]), parts[1], MethodType.methodType(type)); Class<?> extension = Class.forName(parts[0]);
object = (T) handleReference(extension, type, parts[1]);
} else { } else {
handle = MethodHandles.lookup().findConstructor(Class.forName(s), MethodType.methodType(void.class)); object = (T) MethodHandles.lookup().findConstructor(Class.forName(s), MethodType.methodType(void.class)).invoke();
} }
T object = (T) handle.invoke();
if (object != null) { if (object != null) {
action.accept(object); action.accept(object);
} }
} catch (Throwable e) { } catch (Throwable e) {
LOGGER.warn("Failed to instantiate Extension: ", e); LOGGER.warn("Failed to instantiate Extension: ", e);
} }
}; });
}
if (value instanceof String s) { private Object handleReference(Class<?> clazz, Class<?> type, String ref) throws Throwable {
c.accept(s); try {
} else if (value instanceof Collection l) { Field found = clazz.getDeclaredField(ref);
((Collection<String>) l).forEach(c); 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() + "!");
}
} }

View file

@ -16,6 +16,7 @@ import dev.frogmc.frogloader.impl.mod.ModDependencyResolver;
import dev.frogmc.frogloader.impl.util.CrashReportGenerator; import dev.frogmc.frogloader.impl.util.CrashReportGenerator;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.FabricUtil;
import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.asm.mixin.Mixins;
public class PluginLoader { public class PluginLoader {
@ -75,14 +76,21 @@ public class PluginLoader {
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
private static void initializeModMixins(Collection<ModProperties> loadedMods) { private static void initializeModMixins(Collection<ModProperties> loadedMods) {
Map<String, ModProperties> configs = new HashMap<>();
loadedMods.forEach(props -> { loadedMods.forEach(props -> {
Object o = props.extensions().get(BuiltinExtensions.MIXIN_CONFIG); Object o = props.extensions().get(BuiltinExtensions.MIXIN_CONFIG);
if (o instanceof String name) { if (o instanceof String name) {
configs.put(name, props);
Mixins.addConfiguration(name); Mixins.addConfiguration(name);
} else if (o instanceof Collection l) { } else if (o instanceof Collection l) {
((Collection<String>) l).forEach(Mixins::addConfiguration); ((Collection<String>) l).forEach(configFile -> {
configs.put(configFile, props);
Mixins.addConfiguration(configFile);
});
} }
}); });
Mixins.getConfigs().forEach(c ->
c.getConfig().decorate(FabricUtil.KEY_MOD_ID, configs.get(c.getName()).id()));
} }
public static GamePlugin discoverGamePlugins() { public static GamePlugin discoverGamePlugins() {