diff options
Diffstat (limited to 'src/main/java/ftbsc/lll/mixin')
-rw-r--r-- | src/main/java/ftbsc/lll/mixin/LilleroMixin.java | 124 | ||||
-rw-r--r-- | src/main/java/ftbsc/lll/mixin/LilleroMixinPlugin.java | 152 |
2 files changed, 152 insertions, 124 deletions
diff --git a/src/main/java/ftbsc/lll/mixin/LilleroMixin.java b/src/main/java/ftbsc/lll/mixin/LilleroMixin.java deleted file mode 100644 index 835a77f..0000000 --- a/src/main/java/ftbsc/lll/mixin/LilleroMixin.java +++ /dev/null @@ -1,124 +0,0 @@ -package ftbsc.lll.mixin; - -import ftbsc.lll.IInjector; -import ftbsc.lll.exceptions.InjectionException; -import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; - -import java.util.*; - -public class LilleroMixin implements IMixinConfigPlugin { - - private final Map<String, List<IInjector>> injectorMap = new HashMap<>(); - - /** - * Called after the plugin is instantiated, do any setup here. - * @param mixinPackage The mixin root package from the config - */ - @Override - public void onLoad(String mixinPackage) { - for (IInjector inj : ServiceLoader.load(IInjector.class, this.getClass().getClassLoader())) { - //LOGGER.info(RESOURCE, "Registering injector {}", inj.name()); - List<IInjector> injectors = this.injectorMap.get(inj.targetClass()); - if(injectors == null) { - injectors = new ArrayList<>(); - injectorMap.put(inj.targetClass(), injectors); - } - injectors.add(inj); - } - } - - /** - * Called only if the "referenceMap" key in the config is <b>not</b> set. - * This allows the refmap file name to be supplied by the plugin - * programatically if desired. Returning <code>null</code> will revert to - * the default behaviour of using the default refmap json file. - * - * @return Path to the refmap resource or null to revert to the default - */ - @Override //TODO ? - public String getRefMapperConfig() { - return null; - } - - /** - * Called during mixin intialisation, allows this plugin to control whether - * a specific will be applied to the specified target. Returning false will - * remove the target from the mixin's target set, and if all targets are - * removed then the mixin will not be applied at all. - * - * @param targetClassName Fully qualified class name of the target class - * @param mixinClassName Fully qualified class name of the mixin - * @return True to allow the mixin to be applied, or false to remove it from - * target's mixin set - */ - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - return true; - } - - /** - * Called after all configurations are initialised, this allows this plugin - * to observe classes targetted by other mixin configs and optionally remove - * targets from its own set. The set myTargets is a direct view of the - * targets collection in this companion config and keys may be removed from - * this set to suppress mixins in this config which target the specified - * class. Adding keys to the set will have no effect. - * - * @param myTargets Target class set from the companion config - * @param otherTargets Target class set incorporating targets from all other - * configs, read-only - */ - @Override - public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { - - } - - /** - * After mixins specified in the configuration have been processed, this - * method is called to allow the plugin to add any additional mixins to - * load. It should return a list of mixin class names or return null if the - * plugin does not wish to append any mixins of its own. - * - * @return additional mixins to apply - */ - @Override - public List<String> getMixins() { - return null; - } - - /** - * Called immediately <b>before</b> a mixin is applied to a target class, - * allows any pre-application transformations to be applied. - * - * @param targetClassName Transformed name of the target class - * @param targetClass Target class tree - * @param mixinClassName Name of the mixin class - * @param mixinInfo Information about this mixin - */ - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - injectorMap.get(targetClassName).forEach((inj) -> targetClass.methods.stream() - .filter(m -> m.name.equals(inj.methodName()) && m.desc.equals(inj.methodDesc())) - .forEach(m -> { - try { - inj.inject(targetClass, m); - } catch (InjectionException ignored) {} //TODO log - })); - } - - /** - * Called immediately <b>after</b> a mixin is applied to a target class, - * allows any post-application transformations to be applied. - * - * @param targetClassName Transformed name of the target class - * @param targetClass Target class tree - * @param mixinClassName Name of the mixin class - * @param mixinInfo Information about this mixin - */ - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } -} diff --git a/src/main/java/ftbsc/lll/mixin/LilleroMixinPlugin.java b/src/main/java/ftbsc/lll/mixin/LilleroMixinPlugin.java new file mode 100644 index 0000000..8b23e4e --- /dev/null +++ b/src/main/java/ftbsc/lll/mixin/LilleroMixinPlugin.java @@ -0,0 +1,152 @@ +package ftbsc.lll.mixin; + +import ftbsc.lll.IInjector; +import ftbsc.lll.exceptions.InjectionException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.*; + +/** + * Allows you to load your mod's Lillero patches as a Mixin plugin. + * Extend this class and specify the child as a plugin in your mod's Mixin + * config. Refer to your mod loader's instructions for details on how this + * is done. + * Methods are left non-final in case you want to alter their behaviour in + * any way, but I can't really see any merit in doing so. + */ +public abstract class LilleroMixinPlugin implements IMixinConfigPlugin { + /** + * The {@link Logger} used by this library. + */ + protected static final Logger LOGGER = LogManager.getLogger(LilleroMixinPlugin.class); + + /** + * Maps each fully-qualified name to its associated class. + */ + private final Map<String, List<IInjector>> injectorMap = new HashMap<>(); + + /** + * Whether Lillero should take precedence over regular mixins. + */ + private final boolean precedence; + + /** + * The constructor. + * @param precedence whether Lillero should take precedence over regular mixins + */ + public LilleroMixinPlugin(boolean precedence) { + this.precedence = precedence; + } + + /** + * Called after the plugin is instantiated, do any setup here. + * @param mixinPackage The mixin root package from the config + */ + @Override + public void onLoad(String mixinPackage) { + for(IInjector inj : ServiceLoader.load(IInjector.class, this.getClass().getClassLoader())) { + LOGGER.info("Registering injector {}", inj.name()); + List<IInjector> injectors = this.injectorMap.get(inj.targetClass()); + if(injectors == null) { + injectors = new ArrayList<>(); + injectorMap.put(inj.targetClass(), injectors); + } + injectors.add(inj); + } + } + + /** + * Returns null, so it's effectively ignored. + * @return always null + */ + @Override + public String getRefMapperConfig() { + return null; + } + + /** + * Tells Mixin to always apply these patches. + * Lillero doesn't support conditional patches: any check should happen + * within the patch code itself, with the patch code's scope. + * @param targetClassName fully qualified class name of the target class + * @param mixinClassName fully qualified class name of the mixin + * @return always true + */ + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return true; + } + + /** + * Does nothing, as we don't need to alter the target class list. + * @param myTargets target class set from the companion config + * @param otherTargets target class set incorporating targets from all other + * configs, read-only + */ + @Override + public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {} + + /** + * This does not apply any additional mixins. + * @return always null + */ + @Override + public List<String> getMixins() { + return null; + } + + /** + * Called immediately before a mixin is applied to a target class. + * Will apply Lillero patches if {@link #precedence} is true. + * @param className transformed name of the target class + * @param clazz target class tree + * @param mixinClassName name of the mixin class + * @param mixinInfo information about this mixin + */ + @Override + public void preApply(String className, ClassNode clazz, String mixinClassName, IMixinInfo mixinInfo) { + if(precedence) this.applyLilleroPatches(className, clazz); + } + + /** + * Called immediately after a mixin is applied to a target class. + * Will apply Lillero patches if {@link #precedence} is false. + * @param className transformed name of the target class + * @param clazz target class tree + * @param mixinClassName name of the mixin class + * @param mixinInfo information about this mixin + */ + @Override + public void postApply(String className, ClassNode clazz, String mixinClassName, IMixinInfo mixinInfo) { + if(!precedence) this.applyLilleroPatches(className, clazz); + } + + /** + * Applies the appropriate Lillero patches given a node and a class name. + * @param className the class' fully qualified name + * @param clazz the target class + */ + protected void applyLilleroPatches(String className, ClassNode clazz) { + List<IInjector> injectors = this.injectorMap.remove(className); // remove so it's only once + if(injectors != null) { + injectors.forEach((inj) -> clazz.methods.stream() + .filter(m -> m.name.equals(inj.methodName()) && m.desc.equals(inj.methodDesc())) + .forEach(m -> { + try { + LOGGER.info( + "Patching {}.{} with {} ({})", + className, m.name, + inj.name(), + inj.reason()); + inj.inject(clazz, m); + } catch (InjectionException exception) { + LOGGER.error("Error applying patch '{}' : {}", inj.name(), exception); + } + })); + } + } +} |