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/29 15:18] – Add how to add ores to the end biomes 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:
-====== Adding Ores to Worlds ====== +====== Accessor Mixins ====== 
-A lot of mods add their own ores, and you'll need a way to place them in existing biomes for players to find. In this tutorial, we'll look at adding ores to existing biomes. There are 2 steps that are required to add ores to biomes. +FIXME //This page is under revisonThis page may change suddenly and should be taken with a grain of salt.//
-  * Make a ConfiguredFeatures. This defines how your ore block is spawned. +
-  * Register your feature by using [[tutorial:mixin_introduction|mixin]] into Minecraft class where default features are listed+
  
-We'll assume you've already created your own ore block at this point. Quartz Ore will serve as our replacement throughout this tutorialReplace references to Quartz Ore with your ore when appropriate.+**Accessor Mixins** are special mixins defined as interfaces which must **only** contain ''@Accessor'' and ''@Invoker'' handlersUnlike normal mixins however, **Accessor Mixins** are accessible via user code directly.
  
-==== Adding to the overworld ==== +The ''@Accessor'' and ''@Invoker'' annotations allow you to access fields or invoke methods that are not visible (private) or mutable (final).
-In this section, our goal will be spawning the ore in the overworld.+
  
-=== Making a ConfiguredFeatures === +Unlike typical injectors, accessors do not prefix the merged methods with the modid of the mod that contains themAdditionally 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.
-First we need to create a ConfiguredFeaturesMake sure to register your ConfiguredFeature at ''onInitialize''. Feel free to change the values to suit your mod.+
  
-<code java [enable_line_numbers="true"]> +''@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 specifiedhowever, all handlers should be prefixed as so: ''modid$getFieldName'' insteadThis is because when the **Accessor Mixin** is merged the handler will remain unchanged and if there is a problem related to the handlerit's useful to know who owns itAdditionallyif 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.
-public class ExampleMod implements ModInitializer { +
-  public static ConfiguredFeature<??> ORE_QUARTZ_OVERWORLD = Feature.ORE +
-      .configure(new OreFeatureConfig( +
-        OreFeatureConfig.Rules.BASE_STONE_OVERWORLD +
-        Blocks.NETHER_QUARTZ_ORE.getDefaultState(), +
-        9)) // vein size +
-      .decorate(Decorator.RANGE.configure(new RangeDecoratorConfig( +
-        0// bottom offset +
-        0// min y level +
-        64))) // max y level +
-      .spreadHorizontally() +
-      .repeat(20); // number of veins per chunk+
  
-  @Override +===== Accessor ===== 
-  public void onInitialize() { +''@Accessor'' allows you to access or mutate fields. Suppose we want to access ''<yarn field_1752>'' field of ''<yarn class_310>'' class. 
-    Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, new Identifier("tutorial", "ore_quartz_overworld"), ORE_QUARTZ_OVERWORLD);+ 
 +==== 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.18this 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> 
 +@Mixin(VanillaLayeredBiomeSource.class) 
 +public interface VanillaLayeredBiomeSourceAccessor { 
 +  @Accessor("BIOMES") 
 +  static List<RegistryKey<Biome>> modid$getBiomes() { 
 +    throw new AssertionError();
   }   }
 } }
 </code> </code>
  
-=== Registering the feature === +Usage:
-Vanilla ore features that spawn in the overworld biomes are listed in ''DefaultBiomeFeature.addDefaultOres''. We modify this method to add our ore to the overworld via [[tutorial:mixin_introduction|mixin]].+
  
-<code java [enable_line_numbers="true"]+<code java
-@Mixin(DefaultBiomeFeatures.class) +List<RegistryKey<Biome>> biomes VanillaLayeredBiomeSourceAccessor.modid$getBiomes(); 
-public class DefaultBiomeFeaturesMixin +</code> 
-  @Inject(method = "addDefaultOres(Lnet/minecraft/world/biome/GenerationSettings$Builder;)V", at = @At("TAIL")+ 
-  private static void addDefaultOres(GenerationSettings.Builder builder, CallbackInfo ci) { +==== Setting a value to the field ==== 
-    builder.feature(GenerationStep.Feature.UNDERGROUND_ORES, ExampleMod.ORE_QUARTZ_OVERWORLD);+<code java
 +@Mixin(VanillaLayeredBiomeSource.class) 
 +public interface VanillaLayeredBiomeSourceAccessor 
 +  @Accessor("BIOMES") 
 +  static void modid$setBiomes(List<RegistryKey<Biome>> biomes) { 
 +    throw new AssertionError();
   }   }
 } }
 </code> </code>
  
-=== Result === +Usage:
-You should see quartz ore spawning in the overworld. You can use fill command to remove stone blocks surrounding you like this''/fill ~-4 0 ~-4 ~4 ~ ~4 minecraft:air replace minecraft:stone''.+
  
-{{tutorial:ores.png?800}}+<code java> 
 +VanillaLayeredBiomeSourceAccessor.modid$setBiomes(biomes); 
 +</code>
  
-==== Adding to the end ==== +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 initializersuch as ''42'' or ''"Hello World"''. This is because the compiler inlines constant fields.
-In this section, based on the code in the previous sectionwe will add the ore to the end biomes.+
  
-=== Making a ConfiguredFeatures === +===== Invoker ===== 
-We replace '' OreFeatureConfig.Rules.BASE_STONE_OVERWORLD'' with ''new BlockMatchRuleTest(Blocks.END_STONE)'' because endstone is used as a base block in the end biomes.+''@Invoker'' allows you to access methodsSuppose we want to invoke ''<yarn method_7024>'' method of ''<yarn class_1560>'' class.
  
-<code java [enable_line_numbers="true"]+<yarncode java> 
-public class ExampleMod implements ModInitializer { +@Mixin(class_1560.class
-  public static ConfiguredFeature<?, ?> ORE_QUARTZ_END = Feature.ORE +public interface class_1560Invoker { 
-      .configure(new OreFeatureConfig( +  @Invoker("method_7024"
-        new BlockMatchRuleTest(Blocks.END_STONE), /* We use endstone! */ +  boolean modid$invokeTeleportTo(double xdouble ydouble z);
-        Blocks.NETHER_QUARTZ_ORE.getDefaultState(), +
-        9)) +
-      .decorate(Decorator.RANGE.configure(new RangeDecoratorConfig( +
-        0, +
-        0, +
-        64))) +
-      .spreadHorizontally() +
-      .repeat(20); +
- +
-  @Override +
-  public void onInitialize() { +
-    Registry.register(BuiltinRegistries.CONFIGURED_FEATUREnew Identifier("tutorial""ore_quartz_end"), ORE_QUARTZ_END); +
-  }+
 } }
-</code>+</yarncode>
  
-=== Registering the feature === +Usage:
-Considering that no ore is generated in the end biomes on vanilla minecraft, ''DefaultBiomeFeature.addDefaultOres'' can't be used for adding an ore to the end biomes. Instead, we inject into ''DefaultBiomeCreator.method_31065'' because this method is used to create every end biomes.+
  
-<code java [enable_line_numbers="true"]+<yarncode java
-@Mixin(DefaultBiomeCreator.class) +class_1560 enderman ...; 
-public class DefaultBiomeCreatorMixin +((class_1560Invoker) enderman).modid$invokeTeleportTo(0.0D, 70.0D, 0.0D); 
-  @Inject(method = "method_31065(Lnet/minecraft/world/biome/GenerationSettings$Builder;)Lnet/minecraft/world/biome/Biome;", at = @At("HEAD")+</yarncode> 
-  private static void addEndOres(GenerationSettings.Builder builder, CallbackInfoReturnable<Biome> cir) { + 
-    builder.feature(GenerationStep.Feature.UNDERGROUND_ORES, ExampleMod.ORE_QUARTZ_END);+===== 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 BrewingRecipeRegistryInvoker 
 +  @Invoker("registerPotionType") 
 +  static void modid$invokeRegisterPotionType(Item item) { 
 +    throw new AssertionError();
   }   }
 } }
 </code> </code>
  
 +Usage:
 +
 +<code java>
 +BrewingRecipeRegistryInvoker.modid$invokeRegisterPotionType(item);
 +</code>
tutorial/mixin_accessors.1598714307.txt.gz · Last modified: 2020/08/29 15:18 by siglong