User Tools

Site Tools


tutorial:mixin_tips

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:mixin_tips [2023/12/18 02:06] solidblocktutorial: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 being written, and may change at any time.//+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) ======
  
-This is a collection of different tips some might find useful. It's recommended to read the previous articles to understand the tips.+This is a collection of different tips some might find useful. It's recommended to read [[tutorial:mixin_introduction|the intro page]] to better understand this page. 
 + 
 +===== Extend and implement the target class's parents ===== 
 + 
 +When working with Mixins, you may want to use a method from the target's parent on a ''this'' instance. An easy way to make working with this relatively seamless is to directly extend the target class's parents. For instance for the target class: 
 +<yarncode java> 
 +public class class_3222 extends class_1657 { 
 +//... 
 +
 +</yarncode> 
 + 
 +Should have the following Mixin class: 
 +<yarncode java> 
 +abstract class SomeMixin extends class_1657 { 
 +//... 
 +
 +</yarncode> 
 + 
 +This page's other sections will showcase the different benefits of also making the Mixin class abstract. 
 + 
 +----
  
 ===== Make abstract classes ===== ===== Make abstract classes =====
  
-It's fair to say that you should never instantiate a mixin class, mainly because the class doesn't make sense to java if it isn't used in a mixin environment, and there are other ways to access methods declared in a mixin.+It's fair to say that you should never instantiate a mixin class, as Mixin classes exist to be merged. Along with thisit may both be obnoxious and lead to errors and unintended behavior to have to implement all of the methods from your target class's parent interfaces.
  
-Declaring a mixin class abstract doesn't affect its function, and it becomes protected against accidental instantiation.+Declaring a mixin class abstract doesn't affect its function, and it becomes protected against accidental instantiation and removes the burden of having to implement many of the parent's methods that you don't need:
  
 <code java> <code java>
-MixinClass foo = new MixinClass(); // can't do that with an abstract class+abstract class SomeMixin implements TargetsInterfaces { 
 +//Does not need to implement TargetsInterfacesmethods. 
 +}
 </code> </code>
  
-===== Make abstract shadow methods =====+---- 
 + 
 +==== Make abstract shadow methods ====
  
-If you want to access a unaccessible method or field from your target class into your mixin class, you need to use ''@Shadow'' to make that method/field visible.+If you want to access an inaccessible method or field from your target class into your mixin class, you need to use ''@Shadow'' to make that method/field visible. References to those "Shadowed" members will be turned into their target class equivalent when merged.
  
-You can perfectly do this in normal classes by using dummy body:+You can do this as you'd expect within standard class:
  
 <code java> <code java>
Line 26: Line 50:
 </code> </code>
  
-But it'much more //**elegant**// to use an abstract method (and hence an abstract class):+But it'arguably cleaner to use an abstract method (and hence an abstract class):
 <code java> <code java>
 @Shadow @Shadow
Line 32: Line 56:
 </code> </code>
  
-Notethis doesn't work with private methods, since you can't have private abstract methods, and hence you need to use the normal one.+:!: This doesn't work with private methods, since you can't have private abstract methods, and hence you need to implement a dummy body on those ones.
  
-===== Access the ''this'' instance more easily =====+If a method requires a return type, it is conventional to throw an ''AssertionError'' as the content is never executed.  
 +<code java> 
 +@Shadow 
 +protected Type hiddenMethod() { 
 +    throw new AssertionError(); 
 +
 +</code> 
 + 
 +---- 
 + 
 +==== Access the ''this'' instance of your Mixin ====
  
-In mixin, if you want to access the "''this''instance, you have to cast it into ''Object'' first.+In a Mixin class, if you want to access the ''this'' instance, you have to cast it into ''Object'' and ''TargetClass'' first:
  
 <code java> <code java>
Line 42: Line 76:
 </code> </code>
  
-But that only works if your mixin class extends/implements everything that your target class does, which can be a pain because if one of those classes/interfaces has a method you need to implement, you'll be in for some trouble.+But this can only work if your Mixin class extends and implements everything that the target class does. This may be an issue if one of those classes/interfaces has a method that requires implementation. However, by using an abstract Mixin class, you do not need to implement said methods.
  
-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 =====
  
-===== How to mixin inner classes =====+==== Non-static inaccessible inner classes and private outer classes ====
  
-==== 1Normal inaccessible inner classes ====+Since you cannot directly access (and hence mention) these classes from outside, you need to use the "targets" field of the ''@Mixin'' annotation to specify the nameThis is the same field that you would use to target a private or otherwise inaccessible outer class.
  
-Since this you can't directly access (and hence mention) these classes from outside, you need to use the "targets" field of the mixin annotation to specify the name. +For an inner class, you do this by using the complete name of the outer class, then a ''$'' and finally the name of the inner class, as shown below:
- +
-You do this by using the complete name of the outer class, then a ''$'' and finally the name of the inner class, as shown below:+
  
 Class: Class:
Line 70: Line 102:
 <code java> <code java>
 @Mixin(targets = "some.random.package.Outer$Inner") @Mixin(targets = "some.random.package.Outer$Inner")
-public class MyMixin {+abstract class MyMixin {
     @Inject(method = "someRandomMethod()V", at = @At("HEAD"))     @Inject(method = "someRandomMethod()V", at = @At("HEAD"))
     private void injected(CallbackInfo ci) {     private void injected(CallbackInfo ci) {
Line 88: 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't have the outer class first parameter (because in static inner classes only private static methods can be accessed from the inner class, and hence that parameter isn't needed). These are the same as above, the only difference is that the constructor doesn't have the outer class first parameter (because in static inner classes only private static methods can be accessed from the inner class, and hence that parameter isn't needed).
  
  
-==== 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 order is on source level).+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.
  
-===== How to mixin to lambdas =====+----
  
-Sometimes you want to mixin to lambdas. However, lambda do not have visible names. In this case, remember that **mixin is applied to bytecode instead of source**. Therefore, you can view the bytecode to view lambdas.+===== Mixing into lambdas ===== 
 + 
 +Sometimes you want to mix into lambdas. However, lambda do not have visible names. In this case, remember that **Mixin is applied to bytecode rather than the decompiled source code**. Therefore, you can view the bytecode to view the name of lambdas((See [[tutorial:reading_mc_code|Reading the Minecraft source]]'s relevant section on reading bytecode in your IDE)).
  
 For example, we want to inject the lambda in ''EntitySelectors''. The target code is seen as follows: For example, we want to inject the lambda in ''EntitySelectors''. The target code is seen as follows:
Line 117: Line 152:
 </code> </code>
  
-If you directly injects to ''register'' method, that does not workPlease view the bytecode, and you will find:+If you directly injects to ''register'' method, that will not include the lambda functionIf you view the bytecode, however, you will find:
 <code java> <code java>
   private static synthetic method_9982(Lnet/minecraft/command/EntitySelectorReader;)V throws com/mojang/brigadier/exceptions/CommandSyntaxException    private static synthetic method_9982(Lnet/minecraft/command/EntitySelectorReader;)V throws com/mojang/brigadier/exceptions/CommandSyntaxException 
Line 124: Line 159:
 </code> </code>
  
-Therefore, the method name of the lambda you want to mixin should be ''method_9982''.+Therefore, the method name of the lambda you want to mixin should be ''method_9982''. This is the common way lambdas are found in bytecode, but another format may be ''lambda$outerMethod$lambdaIndex''. It's best to try and see which synthetic method is invoked in your target method and try to target that.
  
-===== Mixin to those not remapped =====+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.
  
-If you want to mixin to classes, methods or fields that are not remapped in yarn, such as ''com.mojang.brigadier.StringReader'', you may add ''remap = false'' to avoid potential errors. For example:+---- 
 + 
 +===== 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 ''remap = false'' to avoid potential errors. For example:
 <code java> <code java>
 @Mixin(value = StringReader.class, remap = false) @Mixin(value = StringReader.class, remap = false)
-public abstract class StringReaderMixin { ... }+abstract class StringReaderMixin { ... }
 </code> </code>
  
Line 138: Line 177:
 <code java> <code java>
 @Mixin(EntitySelectorReader.class) @Mixin(EntitySelectorReader.class)
-public abstract class EntitySelectorReaderMixin {+abstract class EntitySelectorReaderMixin {
   @Inject(method = "readTagCharacter", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/StringReader;skipWhitespace()V", remap = false)   @Inject(method = "readTagCharacter", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/StringReader;skipWhitespace()V", remap = false)
   // your injection method ...   // your injection method ...
 } }
 </code> </code>
 +
 +:!: Disabling remapping on an annotation causes the internal remapper to skip the annotation entirely, thus, if the ''@Inject'' does not need remapping but the ''@At'' does, remapping must be explicitly re-enabled in the ''@At'' via ''remap=true''. Mixin can tell the difference between an annotation attribute being set or whether it uses the default.
 +
 +:!: 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.1702865187.txt.gz · Last modified: 2023/12/18 02:06 by solidblock