User Tools

Site Tools


tutorial:mixin_accessors

Differences

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

Link to this comparison view

Next revision
Previous revision
tutorial:mixin_accessors [2020/08/05 22:26] – created emmanuelmesstutorial:mixin_accessors [2025/11/30 20:08] (current) gauntrecluse
Line 1: Line 1:
-====== Mixin Accessors ======+====== Accessor Mixins ======
  
-===== 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.
  
-A type of mixin allows to you to access variables that are not visible (private) or final:+The ''@Accessor'' and ''@Invoker'' annotations allow you to access fields or invoke methods that are not visible (private) or mutable (final). 
 + 
 +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 non-static 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.\\ 
 +Prefixing accessor or invoker handlers is not necessary if they are static, as those may not conflict, and the stacktrace provides the Mixin class whose handlers are involved in errors, thus making it sufficient without needing to prefix with the modid. 
 + 
 +===== 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(StructuresConfig.class) +@Mixin(VanillaLayeredBiomeSource.class) 
-public interface StructuresConfigAccessor +public interface VanillaLayeredBiomeSourceAccessor 
-    @Accessor("DEFAULT_STRUCTURES") +  @Accessor("BIOMES") 
-    public static void setDefaultStructures(ImmutableMap<StructureFeature<?>, StructureConfigdefaultStructures) { +  static List<RegistryKey<Biome>> getBiomes() { 
-        throw new IllegalAccessError(); +    throw new AssertionError(); 
-    }+  }
 } }
 </code> </code>
-The error will never be thrown, you can use the function. 
  
 +Usage:
  
 +<code java>
 +List<RegistryKey<Biome>> biomes = VanillaLayeredBiomeSourceAccessor.getBiomes();
 +</code>
 +
 +==== Setting a value to the field ====
 +<code java>
 +@Mixin(VanillaLayeredBiomeSource.class)
 +public interface VanillaLayeredBiomeSourceAccessor {
 +  @Accessor("BIOMES")
 +  static void setBiomes(List<RegistryKey<Biome>> biomes) {
 +    throw new AssertionError();
 +  }
 +}
 +</code>
 +
 +Usage:
 +
 +<code java>
 +VanillaLayeredBiomeSourceAccessor.setBiomes(biomes);
 +</code>
 +
 +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 =====
 +''@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>
 +@Mixin(BrewingRecipeRegistry.class)
 +public interface BrewingRecipeRegistryAccessor {
 +  @Invoker("registerPotionType")
 +  static void invokeRegisterPotionType(Item item) {
 +    throw new AssertionError();
 +  }
 +}
 +</code>
 +
 +Usage:
 +
 +<code java>
 +BrewingRecipeRegistryAccessor.invokeRegisterPotionType(item);
 +</code>
 +
 +==== Invoker for constructors ====
 +
 +Invokers for constructors must be static, pass ''"<init>"'' as the ''@Invoker'' annotation attribute, and return the type of the target class.
 +
 +For example, we will use the ''Identifier'' class's private constructor. This is not something that you normally need to do for that class as it has accessible static constructors; it is however a good example for the syntax of a constructor invoker. Using 1.21.1 Yarn mappings:
 +<code java>
 +@Mixin(Identifier.class)
 +public interface IdentifierAccessor {
 +    @Invoker("<init>")
 +    static Identifier newIdentifier(String namespace, String path) {
 +        throw new AssertionError();
 +    }
 +}
 +</code>
 +
 +Usage:
 +<code java>
 +Identifier exampleVariable = IdentifierAccessor.newIdentifier("some_namespace", "some_path");
 +</code>
tutorial/mixin_accessors.1596666389.txt.gz · Last modified: 2020/08/05 22:26 by emmanuelmess