mirror of
https://github.com/zaaarf/lillero-mixin.git
synced 2024-11-22 03:34:48 +01:00
feat: reworked to make it extension-based, done?
might just work, but it's untested for now so take care
This commit is contained in:
parent
e50d61850b
commit
a6aa21a985
5 changed files with 162 additions and 130 deletions
|
@ -1,4 +1,4 @@
|
||||||
# WIP, IT DOES NOT WORK YET!
|
# UNTESTED FOR NOW, TAKE CARE!
|
||||||
|
|
||||||
# Lillero-mixin
|
# Lillero-mixin
|
||||||
Lillero-mixin is a Mixin plugin capable of applying [Lillero](https://github.com/zaaarf/lillero) ASM patches without
|
Lillero-mixin is a Mixin plugin capable of applying [Lillero](https://github.com/zaaarf/lillero) ASM patches without
|
||||||
|
@ -6,6 +6,10 @@ needing to inject itself as a JAR library. While slightly dirtier code-wise, thi
|
||||||
[Lillero-loader](https://github.com/zaaarf/lillero-loader) of being compatible with both Forge and Fabric - and, barring
|
[Lillero-loader](https://github.com/zaaarf/lillero-loader) of being compatible with both Forge and Fabric - and, barring
|
||||||
major API changes, with any other future mod loader that will try to force Mixin on you.
|
major API changes, with any other future mod loader that will try to force Mixin on you.
|
||||||
|
|
||||||
|
To use this, write a class extending `LilleroMixinPlugin`. Then, register it as a Mixin plugin; it usually involves editing
|
||||||
|
or writing your mod's configuration, but the exact steps vary depending on your mod loader.
|
||||||
|
Specific instructions for Fabric and Forge coming as soon as I have time for this.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
This time there's one other project that must be mentioned. I would've never thought of this had I not stumbled on
|
This time there's one other project that must be mentioned. I would've never thought of this had I not stumbled on
|
||||||
[Manningham Mills](https://github.com/Chocohead/Fabric-ASM). So, thanks to Chocohead for showing that it was indeed
|
[Manningham Mills](https://github.com/Chocohead/Fabric-ASM). So, thanks to Chocohead for showing that it was indeed
|
||||||
|
|
|
@ -25,9 +25,9 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.apache.logging.log4j:log4j-api:2.17.1'
|
implementation 'org.apache.logging.log4j:log4j-api:2.22.1'
|
||||||
implementation 'org.apache.logging.log4j:log4j-core:2.17.1'
|
implementation 'org.apache.logging.log4j:log4j-core:2.22.1'
|
||||||
implementation 'org.ow2.asm:asm-commons:9.4'
|
implementation 'org.ow2.asm:asm-commons:9.6'
|
||||||
implementation 'ftbsc:lll:0.5.0'
|
implementation 'ftbsc:lll:0.5.0'
|
||||||
implementation 'org.spongepowered:mixin:0.8.3'
|
implementation 'org.spongepowered:mixin:0.8.5'
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
152
src/main/java/ftbsc/lll/mixin/LilleroMixinPlugin.java
Normal file
152
src/main/java/ftbsc/lll/mixin/LilleroMixinPlugin.java
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
ftbsc.lll.mixin.LilleroMixin
|
ftbsc.lll.mixin.LilleroMixinPlugin
|
||||||
|
|
Loading…
Reference in a new issue