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 [2023/08/18 09:19] – Removed apostrophe treeway7 | 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 | + | FIXME //This page is currently |
| ====== Mixin Tips (WIP) ====== | ====== Mixin Tips (WIP) ====== | ||
| - | This is a collection of different tips some might find useful. It's recommended to read the previous articles | + | This is a collection of different tips some might find useful. It's recommended to read [[tutorial: |
| - | ==== Why make a class abstract? | + | ===== Extend and implement the target |
| - | **1. Prevent instantiation** | + | When working with Mixins, you may want to use a method from the target' |
| + | < | ||
| + | public class class_3222 extends class_1657 { | ||
| + | //... | ||
| + | } | ||
| + | </ | ||
| - | It's fair to say that you should never instantiate a mixin class, mainly because | + | Should have the following Mixin class: |
| + | < | ||
| + | abstract class SomeMixin extends class_1657 { | ||
| + | //... | ||
| + | } | ||
| + | </ | ||
| - | Declaring a mixin class abstract doesn' | + | This page's other sections will showcase the different benefits of also making the Mixin class abstract. |
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Make abstract classes ===== | ||
| + | |||
| + | It's fair to say that you should never instantiate a mixin class, as Mixin classes exist to be merged. Along with this, it may both be obnoxious and lead to errors and unintended behavior to have to implement all of the methods from your target class' | ||
| + | |||
| + | 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> | ||
| Line 29: | Line 50: | ||
| </ | </ | ||
| - | But it' | + | But it' |
| <code java> | <code java> | ||
| @Shadow | @Shadow | ||
| Line 35: | Line 56: | ||
| </ | </ | ||
| - | Note: this doesn' | + | :!: This doesn' |
| + | If a method requires a return type, it is conventional to throw an '' | ||
| + | <code java> | ||
| + | @Shadow | ||
| + | protected Type hiddenMethod() { | ||
| + | throw new AssertionError(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ---- | ||
| - | **3. Access the '' | + | ==== 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 46: | 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 75: | 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 86: | Line 113: | ||
| <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 93: | 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.1692350376.txt.gz · Last modified: 2023/08/18 09:19 by treeway7