tutorial:mixin_tips
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| tutorial:mixin_tips [2022/08/17 21:51] – Added WIP warning clomclem | tutorial:mixin_tips [2025/10/20 00:27] (current) – [Mixing into inner classes] Make example mixin class go from public class -> abstract class gauntrecluse | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | FIXME //This page is currently incomplete and still contains relatively very old edits, it may change at any time or contain incomplete or outdated advice!// | ||
| + | |||
| ====== Mixin Tips (WIP) ====== | ====== Mixin Tips (WIP) ====== | ||
| - | FIXME //This page is currently being written, | + | This is a collection of different tips some might find useful. It's recommended to read [[tutorial: |
| + | |||
| + | ===== Extend | ||
| + | |||
| + | When working with Mixins, you may want to use a method from the target' | ||
| + | < | ||
| + | public class class_3222 extends class_1657 { | ||
| + | //... | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Should have the following Mixin class: | ||
| + | < | ||
| + | abstract class SomeMixin extends class_1657 { | ||
| + | //... | ||
| + | } | ||
| + | </ | ||
| + | This page's other sections will showcase the different benefits of also making the Mixin class abstract. | ||
| - | ==== Why make a class abstract? ==== | + | ---- |
| - | **1. Prevent instantiation** | + | ===== Make abstract classes ===== |
| - | It's fair to say that you should never instantiate a mixin class, | + | It's fair to say that you should never instantiate a mixin class, |
| - | Declaring a mixin class abstract doesn' | + | Declaring a mixin class abstract doesn' |
| <code java> | <code java> | ||
| - | MixinClass foo = new MixinClass(); | + | abstract class SomeMixin implements TargetsInterfaces { |
| + | //Does not need to implement TargetsInterfaces' | ||
| + | } | ||
| </ | </ | ||
| + | ---- | ||
| - | **2. Make more elegant | + | ==== Make abstract |
| - | If you want to access | + | If you want to access |
| - | You can perfectly | + | You can do this as you'd expect within |
| <code java> | <code java> | ||
| @Shadow | @Shadow | ||
| - | private | + | protected |
| </ | </ | ||
| - | But it' | + | But it' |
| <code java> | <code java> | ||
| @Shadow | @Shadow | ||
| - | private | + | protected |
| </ | </ | ||
| + | :!: This doesn' | ||
| - | **3. Access the "this" | + | If a method requires a return type, it is conventional to throw an '' |
| + | <code java> | ||
| + | @Shadow | ||
| + | protected Type hiddenMethod() { | ||
| + | throw new AssertionError(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Access the '' | ||
| - | In mixin, if you want to access the "this" | + | In a Mixin class, if you want to access the '' |
| <code java> | <code java> | ||
| Line 43: | Line 76: | ||
| </ | </ | ||
| - | But that only works if your mixin class extends/implements everything that your target class does, which can be a pain because | + | But this can only work if your Mixin class extends |
| - | Luckily, this can all be avoided by using an abstract class, in which case you don't have to implement methods and all problems are avoided. | + | ===== How to mix into inner classes ===== |
| + | ==== Non-static inaccessible inner classes and private outer classes ==== | ||
| - | ==== How to mixin inner classes | + | Since you cannot directly access (and hence mention) these classes |
| - | **1. Normal inaccessible | + | For an inner class, you do this by using the complete name of the outer class, then a '' |
| - | + | ||
| - | Since this you can't directly access (and hence mention) these classes from outside, you need to use the " | + | |
| - | + | ||
| - | You do this by using the complete name of the outer class, then a '' | + | |
| Class: | Class: | ||
| Line 72: | Line 102: | ||
| <code java> | <code java> | ||
| @Mixin(targets = " | @Mixin(targets = " | ||
| - | public | + | abstract |
| - | @Inject(method = " | + | @Inject(method = " |
| private void injected(CallbackInfo ci) { | private void injected(CallbackInfo ci) { | ||
| // your code here | // your code here | ||
| Line 80: | Line 110: | ||
| </ | </ | ||
| - | The only caveat is that if you want to mixin into the inner class constructor, | + | The only caveat is that if you want to mixin into the inner class constructor, |
| <code java> | <code java> | ||
| - | @Inject(method = "< | + | @Inject(method = "< |
| private void injected(CallbackInfo ci) { | private void injected(CallbackInfo ci) { | ||
| // your code here | // your code here | ||
| Line 90: | Line 120: | ||
| - | **2. Static inaccessible inner classes** | + | |
| + | ==== Static inaccessible inner classes | ||
| These are the same as above, the only difference is that the constructor doesn' | These are the same as above, the only difference is that the constructor doesn' | ||
| - | **3. Anonymous inner classes** | + | ==== Anonymous inner classes |
| - | These are the same as the static inaccessible inner classes, the only difference is that since they don't have a name, they are declared by appearance order, for example: the anonymous inner class if declared in our previous example class first would be named Outer$1, a second one would be named Outer$2, a third one Outer$3 and so on (the declaration | + | These are the same as the static inaccessible inner classes, the only difference is that since they don't have a name, they are declared by appearance order, for example: the anonymous inner class if declared in our previous example class first would be named Outer$1, a second one would be named Outer$2, a third one Outer$3 and so on. It is best to check the Bytecode to be certain of the order of the Anonymous classes if any issues are encountered. |
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Mixing into lambdas ===== | ||
| + | |||
| + | Sometimes you want to mix into lambdas. However, lambda do not have visible names. In this case, remember that **Mixin | ||
| + | |||
| + | For example, we want to inject the lambda in '' | ||
| + | <code java> | ||
| + | public class EntitySelectorOptions { | ||
| + | public static void register() { | ||
| + | if (...) { | ||
| + | putOption(" | ||
| + | // Assume that you want to mixin to here | ||
| + | int i = reader.getReader().getCursor(); | ||
| + | // ... | ||
| + | } | ||
| + | // ... | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | If you directly injects to '' | ||
| + | <code java> | ||
| + | private static synthetic method_9982(Lnet/ | ||
| + | L0 | ||
| + | // ... | ||
| + | </ | ||
| + | |||
| + | Therefore, the method name of the lambda you want to mixin should be '' | ||
| + | |||
| + | If you are using the MCDev plugin, its autocomplete for target methods will also include lambdas, and the autocomplete widget will also show parameters for each lambda, which may be leveraged to help confirm which one to target. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Mixing into those not remapped ===== | ||
| + | |||
| + | If you want to mix into classes, methods or fields that are not remapped in yarn, you may try to add '' | ||
| + | <code java> | ||
| + | @Mixin(value = StringReader.class, | ||
| + | abstract class StringReaderMixin { ... } | ||
| + | </ | ||
| + | |||
| + | Another example is: | ||
| + | |||
| + | <code java> | ||
| + | @Mixin(EntitySelectorReader.class) | ||
| + | abstract class EntitySelectorReaderMixin { | ||
| + | @Inject(method = " | ||
| + | // your injection method ... | ||
| + | } | ||
| + | </ | ||
| - | ** UNDER CONSTRUCTION ** | + | :!: Disabling remapping on an annotation causes the internal remapper to skip the annotation entirely, thus, if the '' |
| + | :!: Changing the remap setting may not fix an issue that would apparently seem to be remapping related, however, so it is best to seek direct support for your specific case. | ||
tutorial/mixin_tips.1660773076.txt.gz · Last modified: 2022/08/17 21:51 by clomclem