User Tools

Site Tools


tutorial:mixin_accessors

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_accessors [2020/08/30 10:15] – Restore old revision siglongtutorial:mixin_accessors [2025/10/17 13:47] (current) – Add warnings for outdated examples, use yarncode plugin for most refs to vanilla code. gauntrecluse
Line 1: Line 1:
-====== Mixin Accessors ======+====== Accessor Mixins ====== 
 +FIXME //This page is under revison. This page may change suddenly and should be taken with a grain of salt.//
  
-===== Introduction ===== +**Accessor Mixins** are special mixins defined as interfaces which must **only** contain ''@Accessor'' and ''@Invoker'' handlers. Unlike normal mixins however, **Accessor Mixins** are accessible via user code directly.
-Mixin Accessors allow you to access fields and methods that are not visible (private) or final.+
  
-==== Accessor ==== +The ''@Accessor'' and ''@Invoker'' annotations allow you to access fields or invoke methods that are not visible (private) or mutable (final).
-''@Accessor'' allows you to access fields. Suppose we want to access ''itemUseCooldown'' field of ''MinecraftClient'' class.+
  
-=== Getting a value from the field ===+Unlike typical injectors, accessors do not prefix the merged methods with the modid of the mod that contains them. Additionally as the **Accessor Mixins** are used in user code, the names of the handlers are not mangled, these differences are important to keep in mind when writing **Accessor Mixins** for compatibility and debugging purposes. 
 + 
 +''@Accessor''s and ''@Invoker''s can infer the field/method name if they are called ''getFieldName'' / ''call/invokeMethodName'', omitting the requirement for the ''value'' attribute to be specified, however, all handlers should be prefixed as so: ''modid$getFieldName'' instead. This is because when the **Accessor Mixin** is merged the handler will remain unchanged and if there is a problem related to the handler, it's useful to know who owns it. Additionally, if another mod decides to create a Duck interface with a method called ''getFieldName'', when that is merged alongside your **Accessor Mixin** whichever one applies last will overwrite the other and you end up with a conflict that may crash or cause unintended behaviour. 
 + 
 +===== Accessor ===== 
 +''@Accessor'' allows you to access or mutate fields. Suppose we want to access ''<yarn field_1752>'' field of ''<yarn class_310>'' class. 
 + 
 +==== Getting a value from the field ==== 
 +<yarncode java> 
 +@Mixin(class_310.class) 
 +public interface class_310Accessor { 
 +    @Accessor("field_1752"
 +    int modid$getItemUseCooldown(); 
 +
 +</yarncode> 
 + 
 +Usage: 
 + 
 +<yarncode java> 
 +int field_1752 = ((class_310Accessor) class_310.method_1551()).modid$getItemUseCooldown(); 
 +</yarncode> 
 + 
 +==== Setting a value to the field ==== 
 +<yarncode java> 
 +@Mixin(class_310.class) 
 +public interface class_310Accessor { 
 +    @Accessor("field_1752"
 +    void modid$setItemUseCooldown(int field_1752); 
 +
 +</yarncode> 
 + 
 +Usage: 
 + 
 +<yarncode java> 
 +((class_310Accessor) class_310.method_1551()).modid$setItemUseCooldown(100); 
 +</yarncode> 
 + 
 +When the field is final and you need to set it, use ''@Mutable'' as well. Note that this may not work as expected if the field is set to a constant literal value in its initializer, such as ''42'' or ''"Hello World"''. This is because the compiler inlines constant fields. 
 + 
 +===== Accessor for static fields ===== 
 +FIXME //VanillaLayeredBiomeSource no longer exists since 1.18, this example cannot be directly replicated on recent versions.//\\ 
 +Suppose we want to access the ''BIOMES'' field of ''VanillaLayeredBiomeSource'' class. 
 + 
 +==== Getting a value from the field ====
 <code java> <code java>
-@Mixin(MinecraftClient.class) +@Mixin(VanillaLayeredBiomeSource.class) 
-public interface MinecraftClientAccessor +public interface VanillaLayeredBiomeSourceAccessor 
-    @Accessor("itemUseCooldown") +  @Accessor("BIOMES") 
-    public int getItemUseCooldown();+  static List<RegistryKey<Biome>> modid$getBiomes() { 
 +    throw new AssertionError(); 
 +  }
 } }
 </code> </code>
Line 19: Line 63:
  
 <code java> <code java>
-int itemUseCooldown ((MinecraftClientAccessor) MinecraftClient.getInstance()).getItemUseCooldown();+List<RegistryKey<Biome>> biomes VanillaLayeredBiomeSourceAccessor.modid$getBiomes();
 </code> </code>
  
-=== Setting a value to the field ===+==== Setting a value to the field ====
 <code java> <code java>
-@Mixin(MinecraftClient.class) +@Mixin(VanillaLayeredBiomeSource.class) 
-public interface MinecraftClientAccessor +public interface VanillaLayeredBiomeSourceAccessor 
-    @Accessor("itemUseCooldown") +  @Accessor("BIOMES") 
-    public void setItemUseCooldown(int itemUseCooldown);+  static void modid$setBiomes(List<RegistryKey<Biome>> biomes) { 
 +    throw new AssertionError(); 
 +  }
 } }
 </code> </code>
Line 34: Line 80:
  
 <code java> <code java>
-((MinecraftClientAccessor) MinecraftClient.getInstance()).setItemUseCooldown(100);+VanillaLayeredBiomeSourceAccessor.modid$setBiomes(biomes);
 </code> </code>
  
-==== Invoker ==== +When the field is final and you need to set it, use ''@Mutable'' as well. Note that this may not work as expected if the field is set to a constant literal value in its initializer, such as ''42'' or ''"Hello World"''. This is because the compiler inlines constant fields. 
-''@Invoker'' allows you to access methods. Suppose we want to invoke ''teleportTo'' method of ''EndermanEntity'' class.+ 
 +===== Invoker ===== 
 +''@Invoker'' allows you to access methods. Suppose we want to invoke ''<yarn method_7024>'' method of ''<yarn class_1560>'' class. 
 + 
 +<yarncode java> 
 +@Mixin(class_1560.class) 
 +public interface class_1560Invoker { 
 +  @Invoker("method_7024"
 +  boolean modid$invokeTeleportTo(double x, double y, double z); 
 +
 +</yarncode> 
 + 
 +Usage: 
 + 
 +<yarncode java> 
 +class_1560 enderman = ...; 
 +((class_1560Invoker) enderman).modid$invokeTeleportTo(0.0D, 70.0D, 0.0D); 
 +</yarncode> 
 + 
 +===== Invoker for static methods ===== 
 +FIXME //This example is not fully accurate to latest versions, ''registerPotionType'' exists now within the ''Builder'' nested class within ''BrewingRecipeRegistry''//\\ 
 +Suppose we want to invoke ''registerPotionType'' method of ''BrewingRecipeRegistry'' class.
  
 <code java> <code java>
-@Mixin(EndermanEntity.class) +@Mixin(BrewingRecipeRegistry.class) 
-public interface EndermanEntityInvoker +public interface BrewingRecipeRegistryInvoker 
-  @Invoker("teleportTo") +  @Invoker("registerPotionType") 
-  public boolean invokeTeleportTo(double x, double y, double z);+  static void modid$invokeRegisterPotionType(Item item) { 
 +    throw new AssertionError(); 
 +  }
 } }
 </code> </code>
Line 51: Line 120:
  
 <code java> <code java>
-EndermanEntity enderman = ...; +BrewingRecipeRegistryInvoker.modid$invokeRegisterPotionType(item);
-((EndermanEntityInvoker) enderman).invokeTeleportTo(0.0D, 70.0D, 0.0D);+
 </code> </code>
- 
tutorial/mixin_accessors.1598782524.txt.gz · Last modified: 2020/08/30 10:15 by siglong