From a4f015fce6ede4af92acfe085ac99d726885eb11 Mon Sep 17 00:00:00 2001 From: IzzelAliz Date: Sun, 13 Jun 2021 22:06:33 +0800 Subject: [PATCH] Implement API --- arclight-common/build.gradle | 4 +- .../mixin/api/Arclight_ForgeEventMixin.java | 26 +++ .../common/mod/ArclightConnector.java | 3 +- .../arclight/common/mod/ArclightMod.java | 2 + .../common/mod/util/PluginEventHandler.java | 195 ++++++++++++++++++ .../remapper/ArclightRedirectAdapter.java | 6 +- .../mod/util/remapper/ClassLoaderRepo.java | 10 +- .../mod/util/remapper/GlobalClassRepo.java | 19 +- .../patcher/ArclightPluginPatcher.java | 64 +++--- .../main/resources/mixins.arclight.api.json | 14 ++ arclight-forge-1.16/build.gradle | 4 +- build.gradle | 2 + forge-installer/build.gradle | 2 +- 13 files changed, 310 insertions(+), 41 deletions(-) create mode 100644 arclight-common/src/main/java/io/izzel/arclight/common/mixin/api/Arclight_ForgeEventMixin.java create mode 100644 arclight-common/src/main/java/io/izzel/arclight/common/mod/util/PluginEventHandler.java create mode 100644 arclight-common/src/main/resources/mixins.arclight.api.json diff --git a/arclight-common/build.gradle b/arclight-common/build.gradle index 15d66ab9..554afab7 100644 --- a/arclight-common/build.gradle +++ b/arclight-common/build.gradle @@ -61,8 +61,8 @@ dependencies { implementation 'net.md-5:bungeecord-chat:1.16-R0.4' implementation 'mysql:mysql-connector-java:5.1.49' implementation 'org.yaml:snakeyaml:1.27' - implementation 'io.izzel:tools:1.1.+' - implementation 'io.izzel.arclight:arclight-api:1.0.+' + implementation "io.izzel:tools:$toolsVersion" + implementation "io.izzel.arclight:arclight-api:$apiVersion" implementation project(':i18n-config') } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/api/Arclight_ForgeEventMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/api/Arclight_ForgeEventMixin.java new file mode 100644 index 00000000..733cd0d9 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/api/Arclight_ForgeEventMixin.java @@ -0,0 +1,26 @@ +package io.izzel.arclight.common.mixin.api; + +import io.izzel.arclight.api.Arclight; +import io.izzel.arclight.common.mod.util.PluginEventHandler; +import net.minecraftforge.eventbus.EventBus; +import net.minecraftforge.eventbus.api.IEventBus; +import org.bukkit.plugin.Plugin; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(value = Arclight.class, remap = false) +public class Arclight_ForgeEventMixin { + + /** + * @author IzzelAliz + * @reason + */ + @Overwrite + public static void registerForgeEvent(Plugin plugin, IEventBus bus, Object target) throws Throwable { + if (bus instanceof EventBus) { + PluginEventHandler.register(plugin, (EventBus) bus, target); + } else { + bus.register(target); + } + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightConnector.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightConnector.java index 3baa263a..a61fddbc 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightConnector.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightConnector.java @@ -15,7 +15,7 @@ public class ArclightConnector implements IMixinConnector { public static final Logger LOGGER = ArclightI18nLogger.getLogger("Arclight"); private static final List FILTER_PACKAGE = Arrays.asList("com.google.common", "com.google.gson", "ninja.leaping.configurate", - "io.izzel.arclight.api", "io.izzel.arclight.i18n"); + "io.izzel.arclight.i18n"); @Override public void connect() { @@ -26,6 +26,7 @@ public class ArclightConnector implements IMixinConnector { Mixins.addConfiguration("mixins.arclight.core.json"); Mixins.addConfiguration("mixins.arclight.bukkit.json"); Mixins.addConfiguration("mixins.arclight.forge.json"); + Mixins.addConfiguration("mixins.arclight.api.json"); LOGGER.info("mixin-load.core"); } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMod.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMod.java index f319d441..13ef74f8 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMod.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMod.java @@ -1,5 +1,6 @@ package io.izzel.arclight.common.mod; +import io.izzel.arclight.api.ArclightVersion; import io.izzel.arclight.common.mod.server.ArclightPermissionHandler; import io.izzel.arclight.common.mod.server.event.ArclightEventDispatcherRegistry; import io.izzel.arclight.common.mod.util.log.ArclightI18nLogger; @@ -18,6 +19,7 @@ public class ArclightMod { public ArclightMod() { LOGGER.info("mod-load"); + ArclightVersion.setVersion(ArclightVersion.v1_16_4); ArclightEventDispatcherRegistry.registerAllEventDispatchers(); ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true)); PermissionAPI.setPermissionHandler(ArclightPermissionHandler.INSTANCE); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/PluginEventHandler.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/PluginEventHandler.java new file mode 100644 index 00000000..519502f6 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/PluginEventHandler.java @@ -0,0 +1,195 @@ +package io.izzel.arclight.common.mod.util; + +import io.izzel.arclight.api.Unsafe; +import io.izzel.arclight.common.mod.ArclightMod; +import net.minecraftforge.eventbus.ASMEventHandler; +import net.minecraftforge.eventbus.EventBus; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.IEventListener; +import net.minecraftforge.eventbus.api.IGenericEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import org.bukkit.plugin.Plugin; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static org.objectweb.asm.Opcodes.*; + +public class PluginEventHandler extends ASMEventHandler { + + private static final String HANDLER_DESC = Type.getInternalName(IEventListener.class); + private static final String HANDLER_FUNC_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Event.class)); + private static final MethodHandle MH_GET_LISTENERS; + private static final MethodHandle MH_ADD_LISTENERS; + private static final MethodHandle MH_UNIQUE_NAME; + + static { + try { + MH_GET_LISTENERS = Unsafe.lookup().findGetter(EventBus.class, "listeners", ConcurrentHashMap.class); + MH_ADD_LISTENERS = Unsafe.lookup().findVirtual(EventBus.class, "addToListeners", MethodType.methodType(void.class, + Object.class, Class.class, IEventListener.class, EventPriority.class)); + MH_UNIQUE_NAME = Unsafe.lookup().findVirtual(ASMEventHandler.class, "getUniqueName", MethodType.methodType(String.class, Method.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private final Plugin plugin; + + public PluginEventHandler(Plugin plugin, Object target, Method method, boolean isGeneric) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + super(target, method, isGeneric); + this.plugin = plugin; + } + + @SuppressWarnings("unchecked") + public static void register(Plugin plugin, EventBus bus, Object target) throws Throwable { + ConcurrentHashMap listeners = (ConcurrentHashMap) MH_GET_LISTENERS.invokeExact(bus); + if (!listeners.containsKey(target)) { + if (target.getClass() == Class.class) { + registerClass((Class) target, plugin, bus); + } else { + registerObject(target, plugin, bus); + } + } + } + + private static void registerClass(final Class clazz, Plugin plugin, EventBus bus) { + Arrays.stream(clazz.getMethods()). + filter(m -> Modifier.isStatic(m.getModifiers())). + filter(m -> m.isAnnotationPresent(SubscribeEvent.class)). + forEach(m -> registerListener(clazz, m, m, plugin, bus)); + } + + private static Optional getDeclMethod(final Class clz, final Method in) { + try { + return Optional.of(clz.getDeclaredMethod(in.getName(), in.getParameterTypes())); + } catch (NoSuchMethodException nse) { + return Optional.empty(); + } + + } + + private static void registerObject(final Object obj, Plugin plugin, EventBus bus) { + final HashSet> classes = new HashSet<>(); + typesFor(obj.getClass(), classes); + Arrays.stream(obj.getClass().getMethods()). + filter(m -> !Modifier.isStatic(m.getModifiers())). + forEach(m -> classes.stream(). + map(c -> getDeclMethod(c, m)). + filter(rm -> rm.isPresent() && rm.get().isAnnotationPresent(SubscribeEvent.class)). + findFirst(). + ifPresent(rm -> registerListener(obj, m, rm.get(), plugin, bus))); + } + + private static void typesFor(final Class clz, final Set> visited) { + if (clz.getSuperclass() == null) return; + typesFor(clz.getSuperclass(), visited); + Arrays.stream(clz.getInterfaces()).forEach(i -> typesFor(i, visited)); + visited.add(clz); + } + + private static void registerListener(final Object target, final Method method, final Method real, Plugin plugin, EventBus bus) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != 1) { + throw new IllegalArgumentException( + "Method " + method + " has @SubscribeEvent annotation. " + + "It has " + parameterTypes.length + " arguments, " + + "but event handler methods require a single argument only." + ); + } + + Class eventType = parameterTypes[0]; + + if (!Event.class.isAssignableFrom(eventType)) { + throw new IllegalArgumentException( + "Method " + method + " has @SubscribeEvent annotation, " + + "but takes an argument that is not an Event subtype : " + eventType); + } + + register(eventType, target, real, plugin, bus); + } + + private static void register(Class eventType, Object target, Method method, Plugin plugin, EventBus bus) { + try { + ASMEventHandler asm = new PluginEventHandler(plugin, target, method, IGenericEvent.class.isAssignableFrom(eventType)); + MH_ADD_LISTENERS.invokeExact(bus, target, eventType, (IEventListener) asm, asm.getPriority()); + } catch (Throwable e) { + ArclightMod.LOGGER.error("Error registering event handler: {} {}", eventType, method, e); + } + } + + @Override + public Class createWrapper(Method callback) { + + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + boolean isStatic = Modifier.isStatic(callback.getModifiers()); + String name; + try { + name = (String) MH_UNIQUE_NAME.invoke(this, callback); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + String desc = name.replace('.', '/'); + String instType = Type.getInternalName(callback.getDeclaringClass()); + String eventType = Type.getInternalName(callback.getParameterTypes()[0]); + + cw.visit(V1_6, ACC_PUBLIC | ACC_SUPER, desc, null, "java/lang/Object", new String[]{HANDLER_DESC}); + + cw.visitSource(".dynamic", null); + { + if (!isStatic) + cw.visitField(ACC_PUBLIC, "instance", "Ljava/lang/Object;", null, null).visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "", isStatic ? "()V" : "(Ljava/lang/Object;)V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + if (!isStatic) { + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, desc, "instance", "Ljava/lang/Object;"); + } + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "invoke", HANDLER_FUNC_DESC, null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + if (!isStatic) { + mv.visitFieldInsn(GETFIELD, desc, "instance", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, instType); + } + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, eventType); + mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, instType, callback.getName(), Type.getMethodDescriptor(callback), false); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + return Unsafe.defineClass(name, bytes, 0, bytes.length, plugin.getClass().getClassLoader(), plugin.getClass().getProtectionDomain()); + } + + @Override + public String toString() { + return "PL:" + plugin.getName() + " " + super.toString(); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java index 06be1450..c6227745 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java @@ -436,7 +436,7 @@ public class ArclightRedirectAdapter implements PluginTransformer { } @Override - public Object[] apply(ClassLoaderRemapper remapper, Method method, Object src, Object[] param) { + public Object[] apply4(ClassLoaderRemapper remapper, Method method, Object src, Object[] param) { try { Method handleMethod = remapper.getGeneratedHandlerClass().getMethod(handlerName, handlerArgs); if (method.getParameterCount() > 0) { @@ -467,7 +467,7 @@ public class ArclightRedirectAdapter implements PluginTransformer { } @Override - public Object[] apply(ClassLoaderRemapper remapper, Method method, Object src, Object[] param) { + public Object[] apply4(ClassLoaderRemapper remapper, Method method, Object src, Object[] param) { try { Method redirectMethod = remapper.getGeneratedHandlerClass().getMethod(handlerName, handlerArgs); return new Object[]{redirectMethod, null, Modifier.isStatic(method.getModifiers()) ? param : ArrayUtil.prepend(param, src)}; @@ -489,7 +489,7 @@ public class ArclightRedirectAdapter implements PluginTransformer { } @Override - public Object[] apply(ClassLoaderRemapper remapper, Method method, Object src, Object[] param) { + public Object[] apply4(ClassLoaderRemapper remapper, Method method, Object src, Object[] param) { boolean bridgeStatic = Modifier.isStatic(targetMethod.getModifiers()); if (bridgeStatic) { Object[] ret = bridge.apply(remapper, this.targetMethod, null, param); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRepo.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRepo.java index 2a434fef..c144a4ae 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRepo.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRepo.java @@ -1,5 +1,6 @@ package io.izzel.arclight.common.mod.util.remapper; +import io.izzel.arclight.api.PluginPatcher; import net.md_5.specialsource.repo.ClassRepo; import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode; @@ -10,7 +11,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; -public class ClassLoaderRepo implements ClassRepo { +public class ClassLoaderRepo implements ClassRepo, PluginPatcher.ClassRepo { private final ClassLoader classLoader; @@ -20,6 +21,11 @@ public class ClassLoaderRepo implements ClassRepo { @Override public ClassNode findClass(String internalName) { + return findClass(internalName, ClassReader.SKIP_CODE); + } + + @Override + public ClassNode findClass(String internalName, int parsingOptions) { URL url = classLoader instanceof URLClassLoader ? ((URLClassLoader) classLoader).findResource(internalName + ".class") // search local : classLoader.getResource(internalName + ".class"); @@ -29,7 +35,7 @@ public class ClassLoaderRepo implements ClassRepo { try (InputStream inputStream = connection.getInputStream()) { ClassReader reader = new ClassReader(inputStream); ClassNode classNode = new ClassNode(); - reader.accept(classNode, ClassReader.SKIP_CODE); + reader.accept(classNode, parsingOptions); return classNode; } } catch (IOException ignored) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/GlobalClassRepo.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/GlobalClassRepo.java index e215bccd..fec3f8e6 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/GlobalClassRepo.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/GlobalClassRepo.java @@ -3,7 +3,9 @@ package io.izzel.arclight.common.mod.util.remapper; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import io.izzel.arclight.api.PluginPatcher; import net.md_5.specialsource.repo.ClassRepo; +import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.service.MixinService; @@ -13,7 +15,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -public class GlobalClassRepo implements ClassRepo { +public class GlobalClassRepo implements ClassRepo, PluginPatcher.ClassRepo { public static final GlobalClassRepo INSTANCE = new GlobalClassRepo(); private static final PluginInheritanceProvider PROVIDER = new PluginInheritanceProvider(INSTANCE); @@ -35,6 +37,21 @@ public class GlobalClassRepo implements ClassRepo { } } + @Override + public ClassNode findClass(String internalName, int parsingOptions) { + if (parsingOptions == ClassReader.SKIP_CODE) { + return findClass(internalName); + } else { + return this.repos.parallelStream() + .filter(PluginPatcher.ClassRepo.class::isInstance) + .map(PluginPatcher.ClassRepo.class::cast) + .map(it -> it.findClass(internalName, parsingOptions)) + .filter(Objects::nonNull) + .findAny() + .orElseGet(() -> this.findMinecraft(internalName)); + } + } + private ClassNode findParallel(String internalName) { return this.repos.parallelStream() .map(it -> it.findClass(internalName)) diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java index 3befbcd4..f6eb00ab 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/patcher/ArclightPluginPatcher.java @@ -1,35 +1,37 @@ package io.izzel.arclight.common.mod.util.remapper.patcher; +import io.izzel.arclight.api.PluginPatcher; import io.izzel.arclight.common.mod.ArclightMod; import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper; +import io.izzel.arclight.common.mod.util.remapper.GlobalClassRepo; import io.izzel.arclight.common.mod.util.remapper.PluginTransformer; import org.bukkit.configuration.file.YamlConfiguration; import org.objectweb.asm.tree.ClassNode; import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.net.URLClassLoader; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; public class ArclightPluginPatcher implements PluginTransformer { - private final List list; + private final List list; - public ArclightPluginPatcher(List list) { + public ArclightPluginPatcher(List list) { this.list = list; } @Override public void handleClass(ClassNode node, ClassLoaderRemapper remapper) { - for (PluginTransformer transformer : list) { - transformer.handleClass(node, remapper); + for (PluginPatcher patcher : list) { + patcher.handleClass(node, GlobalClassRepo.INSTANCE); } } @@ -37,32 +39,36 @@ public class ArclightPluginPatcher implements PluginTransformer { File pluginFolder = new File("plugins"); if (pluginFolder.exists()) { ArclightMod.LOGGER.info("patcher.loading"); - ArrayList list = new ArrayList<>(); - for (File file : pluginFolder.listFiles()) { - if (file.isFile() && file.getName().endsWith(".jar")) { - loadFromJar(file.toPath()).ifPresent(list::add); + ArrayList list = new ArrayList<>(); + File[] files = pluginFolder.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile() && file.getName().endsWith(".jar")) { + loadFromJar(file).ifPresent(list::add); + } + } + if (!list.isEmpty()) { + list.sort(Comparator.comparing(PluginPatcher::priority)); + ArclightMod.LOGGER.info("patcher.loaded", list.size()); + transformerList.add(new ArclightPluginPatcher(list)); } - } - if (!list.isEmpty()) { - list.sort(Comparator.comparing(PluginTransformer::priority)); - ArclightMod.LOGGER.info("patcher.loaded", list.size()); - transformerList.add(new ArclightPluginPatcher(list)); } } } - private static Optional loadFromJar(Path path) { - try { - FileSystem fileSystem = FileSystems.newFileSystem(path, ArclightPluginPatcher.class.getClassLoader()); - Path pluginYml = fileSystem.getPath("plugin.yml"); - if (Files.exists(pluginYml)) { - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(Files.newBufferedReader(pluginYml)); - String name = configuration.getString("arclight.patcher"); - if (name != null) { - URLClassLoader loader = new URLClassLoader(new URL[]{path.toUri().toURL()}, ArclightPluginPatcher.class.getClassLoader()); - Class clazz = Class.forName(name, false, loader); - PluginTransformer transformer = clazz.asSubclass(PluginTransformer.class).newInstance(); - return Optional.of(transformer); + private static Optional loadFromJar(File file) { + try (JarFile jarFile = new JarFile(file)) { + JarEntry jarEntry = jarFile.getJarEntry("plugin.yml"); + if (jarEntry != null) { + try (InputStream stream = jarFile.getInputStream(jarEntry)) { + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream)); + String name = configuration.getString("arclight.patcher"); + if (name != null) { + URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()}, ArclightPluginPatcher.class.getClassLoader()); + Class clazz = Class.forName(name, false, loader); + PluginPatcher patcher = clazz.asSubclass(PluginPatcher.class).getConstructor().newInstance(); + return Optional.of(patcher); + } } } } catch (Throwable e) { diff --git a/arclight-common/src/main/resources/mixins.arclight.api.json b/arclight-common/src/main/resources/mixins.arclight.api.json new file mode 100644 index 00000000..4fe4ec0e --- /dev/null +++ b/arclight-common/src/main/resources/mixins.arclight.api.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "io.izzel.arclight.common.mixin.api", + "target": "@env(DEFAULT)", + "refmap": "mixins.arclight.refmap.json", + "setSourceFile": true, + "injectors": { + "defaultRequire": 1 + }, + "mixins": [ + "Arclight_ForgeEventMixin" + ] +} \ No newline at end of file diff --git a/arclight-forge-1.16/build.gradle b/arclight-forge-1.16/build.gradle index cd4deeee..d1d1518a 100644 --- a/arclight-forge-1.16/build.gradle +++ b/arclight-forge-1.16/build.gradle @@ -97,8 +97,8 @@ dependencies { embed 'net.md-5:bungeecord-chat:1.16-R0.4@jar' embed "org.spigotmc:spigot-api:$minecraftVersion-R0.1-SNAPSHOT@jar" embed 'com.github.ArclightTeam:mixin-tools:1.0.0@jar' - embed 'io.izzel:tools:1.1.+' - embed 'io.izzel.arclight:arclight-api:1.0.+' + embed "io.izzel:tools:$toolsVersion" + embed "io.izzel.arclight:arclight-api:$apiVersion" annotationProcessor 'org.spongepowered:mixin:0.8.2:processor' annotationProcessor 'com.github.ArclightTeam:mixin-tools:1.0.0' } diff --git a/build.gradle b/build.gradle index 4c2999bf..f34946d4 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,8 @@ allprojects { agpVersion = '1.17' minecraftVersion = '1.16.5' forgeVersion = '36.1.24' + apiVersion = '1.0.+' + toolsVersion = '1.3.+' } task cleanBuild { diff --git a/forge-installer/build.gradle b/forge-installer/build.gradle index 4f0d5b0e..1f5324ad 100644 --- a/forge-installer/build.gradle +++ b/forge-installer/build.gradle @@ -13,6 +13,6 @@ repositories { dependencies { compile 'com.google.code.gson:gson:2.8.0' - compile 'io.izzel.arclight:arclight-api:1.0.+' + compile "io.izzel.arclight:arclight-api:$apiVersion" compile project(':i18n-config') }