minecraft:mod:mixin:start
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
minecraft:mod:mixin:start [2024/03/31 17:58] – removed - external edit (Unknown date) 127.0.0.1 | minecraft:mod:mixin:start [2024/03/31 17:58] (current) – ↷ Links adapted because of a move operation raccoon | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | SpongePowered Mixin allows you to modify compiled classes on load at runtime. Several mixins from different authors can be layered on top of the same targets, allowing for reasonable compatibility even between mods that alter the same vanilla code. | ||
+ | ====== Configuration ====== | ||
+ | |||
+ | This is an example configuration file for declaring your mixins: | ||
+ | |||
+ | <code javascript example.mixins.json> | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | ] | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | See also: [[https:// | ||
+ | |||
+ | ---- | ||
+ | |||
+ | For Fabric to see your mixins, you'll have to specify the location of this config file in your mod configuration too. | ||
+ | |||
+ | <code javascript fabric.mod.json> | ||
+ | { | ||
+ | /* ... */ | ||
+ | " | ||
+ | " | ||
+ | ], | ||
+ | /* ... */ | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Mixin classes ====== | ||
+ | |||
+ | A mixin class is annotated with '' | ||
+ | |||
+ | <code java MyFooMixin.java> | ||
+ | @Mixin(value = Foo.class) | ||
+ | public abstract class MyFooMixin | ||
+ | </ | ||
+ | |||
+ | A mixin' | ||
+ | The annotation also accepts the following optional elements: | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | ===== Mixin methods and their annotations ===== | ||
+ | |||
+ | Methods of a mixin are used to modify the target class. They can be annotated to signal how in particular they should be used to modify the target. | ||
+ | |||
+ | Conceptually, | ||
+ | |||
+ | ==== Overwriting a target method ==== | ||
+ | |||
+ | The default (but certainly not the preference) is to simply merge each method into the target, overwriting if a method already exists by the same name. This, of course, can break all lower-priority mixins that modify the same methods, so you should avoid it. | ||
+ | |||
+ | If you really do need the overwrite behavior for some reason, annotate your method with '' | ||
+ | |||
+ | ==== Modifying a target method' | ||
+ | |||
+ | === Overarching concepts === | ||
+ | |||
+ | For each annotation that modifies the body of a method, [[..: | ||
+ | |||
+ | ---- | ||
+ | |||
+ | Like the '' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | Since these annotations can match any number of different points in the method body, these common '' | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | === @Inject === | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code java Foo.java> | ||
+ | public class Foo { | ||
+ | public int bar(int x) { | ||
+ | System.out.println(" | ||
+ | return x; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code java FooMixin.java> | ||
+ | @Inject(method = " | ||
+ | private void injected(int x) { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code java Result> | ||
+ | public int bar(int x) { | ||
+ | System.out.println(" | ||
+ | System.out.println(" | ||
+ | return x; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The injected method is always '' | ||
+ | |||
+ | <code java FooMixin.java> | ||
+ | @Inject(method = " | ||
+ | private void injected(int x, CallbackInfoReturnable cir) { | ||
+ | if (x == 0) { | ||
+ | cir.setReturnValue(x+1); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code java Result> | ||
+ | public int bar(int x) { | ||
+ | if (x == 0) { // <-- | ||
+ | return x+1; // <-- | ||
+ | } // <-- | ||
+ | System.out.println(" | ||
+ | return x; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === @Redirect === | ||
+ | |||
+ | Instead of injecting //between// instructions, | ||
+ | |||
+ | == Redirecting a method call == | ||
+ | |||
+ | In the case of method calls, your method acts as if it's the method being called, and it receives all the necessary arguments to act as such. For '' | ||
+ | |||
+ | Additionally, | ||
+ | |||
+ | <code java Foo.java> | ||
+ | public class Foo { | ||
+ | public int x; | ||
+ | |||
+ | public int incrementX(int by) { | ||
+ | Crementer in = new Crementer(by); | ||
+ | this.x = in.crement(this.x); | ||
+ | return this.x; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code java FooMixin.java> | ||
+ | @Redirect(method = " | ||
+ | private int actuallyDecrement( | ||
+ | Crementer in, // the target' | ||
+ | int x, // the target' | ||
+ | int by // incrementX' | ||
+ | ) { | ||
+ | Crementer de = new Crementer(-by); | ||
+ | return de.crement(x); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code java Result> | ||
+ | public int incrementX(int by) { | ||
+ | Crementer in = new Crementer(by); | ||
+ | this.x = (() -> { // <-- | ||
+ | Crementer de = new Crementer(-by); | ||
+ | return de.crement(x); | ||
+ | })() // <-- | ||
+ | return this.x; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | == Redirecting field access == | ||
+ | |||
+ | If you inject at '' | ||
+ | |||
+ | ^ opcode | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | == ... == | ||
+ | |||
+ | '' | ||
+ | |||
+ | === @ModifyArg, @ModifyArgs, | ||
+ | |||
+ | These annotations allow you to modify the variable references and literals in a method body. | ||
+ | |||
+ | The method provided to these annotations accepts one argument and returns a value of the same type. That method has a special purpose for filtering the injection points: the type of its argument and the type of the item being selected must be the same to be considered. | ||
+ | |||
+ | ---- | ||
+ | |||
+ | '' | ||
+ | |||
+ | Like the '' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | '' | ||
+ | |||
+ | If you want to modify the arguments your target method receives before anything else happens, you can alternatively inject at the '' | ||
+ | |||
+ | ---- | ||
+ | |||
+ | '' | ||
+ | |||
+ | For additional context, the annotated method can optionally receive //all// of the arguments of the method, rather than just the one it intends to replace. | ||
+ | |||
+ | ---- | ||
+ | |||
+ | '' | ||
+ | |||
+ | ==== Proxying access to class members ==== | ||
+ | |||
+ | === @Shadow === | ||
+ | |||
+ | If you want to access a field or method in the target class while implementing your mixin methods, and you want to avoid the '' | ||
+ | |||
+ | === Re-exporting private members === | ||
+ | |||
+ | If you want to offer access to private fields to your non-mixin code, you can declare an //accessor mixin//, which is defined as an '' | ||
+ | |||
+ | Now, if some external code wants to access a private member on '' |