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 [2022/08/17 21:51] – Added WIP warning clomclemtutorial:mixin_tips [2023/12/18 02:06] (current) solidblock
Line 1: Line 1:
-====== Mixin Tips (WIP) ====== 
- 
 FIXME //This page is currently being written, and may change at any time.// FIXME //This page is currently being written, and may change at any time.//
  
 +====== Mixin Tips (WIP) ======
  
-==== Why make class abstract? ====+This is collection of different tips some might find useful. It's recommended to read the previous articles to understand the tips.
  
-**1. Prevent instantiation**+===== 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, 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.
  
-Declaring a mixin class abstract doesn't affect it'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.
  
 <code java> <code java>
Line 16: Line 15:
 </code> </code>
  
- +===== Make abstract shadow methods =====
-**2. Make more elegant 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 a unaccessible method or field from your target class into your mixin class, you need to use ''@Shadow'' to make that method/field visible.
Line 25: Line 23:
 <code java> <code java>
 @Shadow @Shadow
-private void hiddenMethod() {/*dummy body*/}+protected void hiddenMethod() {/*dummy body*/}
 </code> </code>
  
Line 31: Line 29:
 <code java> <code java>
 @Shadow @Shadow
-private abstract void hiddenMethod(); // no dummy body necessary+protected abstract void hiddenMethod(); // no dummy body necessary
 </code> </code>
  
 +Note: this doesn't work with private methods, since you can't have private abstract methods, and hence you need to use the normal one.
  
-**3. Access the "thisinstance more easily**+===== Access the ''this'' instance more easily =====
  
-In mixin, if you want to access the "this" instance, you have to do a cast inside your mixin class:+In mixin, if you want to access the "''this''" instance, you have to cast it into ''Object'' first.
  
 <code java> <code java>
Line 47: Line 46:
 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. 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 mixin inner classes =====
  
-==== How to mixin inner classes ==== +==== 1. Normal inaccessible inner classes ====
- +
-**1. Normal inaccessible inner classes **+
  
 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. 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.
Line 73: Line 71:
 @Mixin(targets = "some.random.package.Outer$Inner") @Mixin(targets = "some.random.package.Outer$Inner")
 public class MyMixin { public 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) {
         // your code here         // your code here
Line 80: Line 78:
 </code> </code>
  
-The only caveat is that if you want to mixin into the inner class constructor, the first parameter must be of the type of the outer class (this is implicitly added by the compiler to allow the inner class to access private methods of the outer class):+The only caveat is that if you want to mixin into the inner class constructor, the first parameter must be of the type of the outer class (this is implicitly added by the compiler to allow the inner class to access instance methods of the outer class):
  
 <code java> <code java>
-@Inject(method = "<init>(Lsome/random/package/Outer;)V", at = @At("HEAD")+@Inject(method = "<init>(Lsome/random/package/Outer;)V", at = @At("HEAD"))
 private void injected(CallbackInfo ci) { private void injected(CallbackInfo ci) {
     // your code here     // your code here
Line 90: Line 88:
  
  
-**2. Static inaccessible inner classes**+==== 2. 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**+==== 3. 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 (the declaration order is on source level).
  
-** UNDER CONSTRUCTION **+===== 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.
 +
 +For example, we want to inject the lambda in ''EntitySelectors''. The target code is seen as follows:
 +<code java>
 +public class EntitySelectorOptions {
 +  public static void register() {
 +    if (...) {
 +      putOption("name", reader -> {
 +        // Assume that you want to mixin to here
 +        int i = reader.getReader().getCursor();
 +        // ...
 +      }
 +      // ...
 +    }
 +  }
 +}
 +</code>
 +
 +If you directly injects to ''register'' method, that does not work. Please view the bytecode, and you will find:
 +<code java>
 +  private static synthetic method_9982(Lnet/minecraft/command/EntitySelectorReader;)V throws com/mojang/brigadier/exceptions/CommandSyntaxException 
 +   L0
 +   // ...
 +</code>
 +
 +Therefore, the method name of the lambda you want to mixin should be ''method_9982''.
 +
 +===== Mixin to those not remapped =====
 +
 +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:
 +<code java>
 +@Mixin(value = StringReader.class, remap = false)
 +public abstract class StringReaderMixin { ... }
 +</code>
 +
 +Another example is:
 +
 +<code java>
 +@Mixin(EntitySelectorReader.class)
 +public abstract class EntitySelectorReaderMixin {
 +  @Inject(method = "readTagCharacter", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/StringReader;skipWhitespace()V", remap = false)
 +  // your injection method ...
 +}
 +</code>
tutorial/mixin_tips.1660773076.txt.gz · Last modified: 2022/08/17 21:51 by clomclem