User Tools

Site Tools


tutorial:ores

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:ores [2021/05/21 15:34] themodderofalltutorial:ores [2023/12/18 01:03] (current) solidblock
Line 1: Line 1:
-====== Generating Custom Ores [1.16.3] ======+If you are looking for 1.19.3, ores should be done completely in jsons. A helpful tool to know is: [[https://misode.github.io/worldgen/feature/|Configured Features]] and [[https://misode.github.io/worldgen/placed-feature/|Placed Features]] 
 + 
 +====== Generating Custom Ores [1.19.3+] ====== 
 +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.  
 +  * Make worldgen Features in JSON that define how and where the ore block is spawned. 
 +  * Use [[https://github.com/FabricMC/fabric/pull/1097|Biome Modification API in Fabric API]] to add your feature to biomes. 
 + 
 +To simplify, we use vanilla end rod as ore, because it can be easily found and seen underground in spectator mode. 
 + 
 +==== Adding to the overworld biomes ==== 
 +In this section, our goal will be spawning the ore in the overworld. 
 + 
 +First, create two new JSON files in your mod's data directory: 
 + 
 +<code JavaScript src\main\resources\data\tutorial\worldgen\configured_feature\ore_custom.json> 
 +
 +  "type": "minecraft:ore", 
 +  "config":
 +    "discard_chance_on_air_exposure": 0.0, 
 +    "size": 12, 
 +    "targets":
 +      { 
 +        "state":
 +          "Name": "minecraft:end_rod" 
 +        }, 
 +        "target":
 +          "predicate_type": "minecraft:tag_match", 
 +          "tag": "minecraft:stone_ore_replaceables" 
 +        } 
 +      }, 
 +      { 
 +        "state":
 +          "Name": "minecraft:end_rod" 
 +        }, 
 +        "target":
 +          "predicate_type": "minecraft:tag_match", 
 +          "tag": "minecraft:deepslate_ore_replaceables" 
 +        } 
 +      } 
 +    ] 
 +  } 
 +
 +</code> 
 + 
 +This Configured Feature tells the game the size of the ore veins, what fraction of the ore blocks should be removed due to air exposure, and, importantly, which blocks the ore block should be allowed to replace. Notice how there are two ''target'' objects in the ''targets'' array: one for ore in stone, and another is the ore in deepslate. To simplify it, both we use end rod in vanilla games. 
 + 
 +<code JavaScript src\main\resources\data\tutorial\worldgen\placed_feature\ore_custom.json> 
 +
 +  "feature": "tutorial:ore_custom", 
 +  "placement":
 +    { 
 +      "type": "minecraft:count", 
 +      "count": 20 
 +    }, 
 +    { 
 +      "type": "minecraft:in_square" 
 +    }, 
 +    { 
 +      "type": "minecraft:height_range", 
 +      "height":
 +        "type": "minecraft:trapezoid", 
 +        "max_inclusive":
 +          "absolute": 70 
 +        }, 
 +        "min_inclusive":
 +          "absolute": -24 
 +        } 
 +      } 
 +    }, 
 +    { 
 +      "type": "minecraft:biome" 
 +    } 
 +  ] 
 +
 +</code> 
 + 
 +This Placed Feature tells the game how many ore veins to place in a chunk, what shape the ore veins are placed in, and at what y-levels the ore veins are placed in. Note that the first line of the Placed Feature references the Configured Feature that was created a moment ago.  
 +  
 +Writing these JSON files by hand is strenuous and inefficient. Note that the bulk of the work can be skipped by using tools like this [[https://misode.github.io/worldgen/feature/|Configured Features generator]] and this [[https://misode.github.io/worldgen/placed-feature/|Placed Features generator]]. Alternatively, use compressed file tools to open your vanilla Minecraft ''.jar'' and use the vanilla files as a reference.  
 + 
 +Now that our data is created, it's time for code! Thanks to the changes in Minecraft 1.19.3, adding ore to world generation requires much less Java code. All we need to do is register the feature, then use the Fabric Biome Modification API to tell Minecraft which dimension to generate the ore in (and during which stage of world generation to generate the ore). 
 + 
 +First, at the class level, let's create a new ''RegistryKey'' at the class-level to store our ore.  
 + 
 +<code Java src/main/java/net/fabricmc/example/ExampleMod.java> 
 +public class ExampleMod implements ModInitializer { 
 +  
 + public static final RegistryKey<PlacedFeature> CUSTOM_ORE_PLACED_KEY = RegistryKey.of(RegistryKeys.PLACED_FEATURE, new Identifier("tutorial","ore_custom")); 
 + 
 + @Override 
 + public void onInitialize() { 
 + 
 + //Your other code here... 
 + 
 +
 +
 +</code> 
 + 
 +Notice 2 things: 
 +  * ''RegistryKey'' is a generic. We give it the type of PlacedFeature. 
 +  * our identifier uses the name "ore_custom" which is the filename of our Placed Feature JSON file. We don't need to give it the Configured Feature, because the Placed Feature stores the name of its corresponding Configured Feature. 
 + 
 +Now to add the Feature to a biome: 
 + 
 +<code Java src/main/java/net/fabricmc/example/ExampleMod.java> 
 + 
 +@Override 
 + public void onInitialize() { 
 +  
 + //Your other code here... 
 + BiomeModifications.addFeature(BiomeSelectors.foundInOverworld(), GenerationStep.Feature.UNDERGROUND_ORES, CUSTOM_ORE_PLACED_KEY); 
 +
 + 
 +</code> 
 + 
 +==== Testing ==== 
 + 
 +To test your new ore, generate a new world. You can also open existing world but have to go to new chunks. In this example of end rod, the ores can be directly seen underground in spectator mode. 
 + 
 +==== Adding to the Nether or End ==== 
 + 
 +Adding your ore to the other dimensions is very similar to adding ore to the Overworld. This section assumes you have already created an ore block for the Nether and End. 
 + 
 +As with the Overworld, we begin with the JSON files. Using vanilla's nether gold ore as an example, our Placed Feature will look something like this: 
 + 
 +<code JavaScript src/main/resources/data/tutorial/worldgen/placed_feature/ore_custom_nether.json> 
 +
 +  "feature": "tutorial:ore_nether_custom", 
 +  "placement":
 +    { 
 +      "type": "minecraft:count", 
 +      "count": 20 
 +    }, 
 +    { 
 +      "type": "minecraft:in_square" 
 +    }, 
 +    { 
 +      "type": "minecraft:height_range", 
 +      "height":
 +        "type": "minecraft:uniform", 
 +        "max_inclusive":
 +          "below_top": 10 
 +        }, 
 +        "min_inclusive":
 +          "above_bottom": 10 
 +        } 
 +      } 
 +    }, 
 +    { 
 +      "type": "minecraft:biome" 
 +    } 
 +  ] 
 +
 +</code> 
 + 
 +Notice how the only real difference is that the height ranges are described differently, and the height type is ''uniform'' instead of ''trapezoid''. Trapezoid shapes and absolute height ranges are still valid in the Nether, we are simply following vanilla Minecraft's game design here. Your mod may use whichever you prefer. 
 + 
 +As before, we add a Configured Feature as well. 
 + 
 +<code JavaScript src/main/resources/data/tutorial/worldgen/configured_feature/ore_nether_custom.json> 
 +
 +  "type": "minecraft:ore", 
 +  "config":
 +    "discard_chance_on_air_exposure": 0.0, 
 +    "size": 20, 
 +    "targets":
 +      { 
 +        "state":
 +          "Name": "minecraft:end_rod" 
 +        }, 
 +        "target":
 +          "block": "minecraft:netherrack", 
 +          "predicate_type": "minecraft:block_match" 
 +        } 
 +      } 
 +    ] 
 +  } 
 +
 +</code> 
 + 
 +In the Configured Feature, remember that the ''Name'' key is the name of your ore block, not the name of your Placed Feature. 
 + 
 +Finally, back in our Java code, right after our other BiomeModification line, 
 + 
 +<code Java src/main/java/net/fabricmc/example/ExampleMod.java> 
 + BiomeModifications.addFeature(BiomeSelectors.foundInNether(), GenerationStep.Feature.UNDERGROUND_ORES, CUSTOM_ORE_PLACED_KEY); 
 +</code> 
 + 
 +Extrapolating this process to add ore to the End is left as an exercise for the reader. 
 + 
 +====== Generating Custom Ores [1.18.2 / 1.19.2] ======
 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. 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.
-  * Make a ConfiguredFeatures. This defines how your ore block is spawned.+  * Make a ConfiguredFeature. This defines how your ore block is spawned.
   * Use [[https://github.com/FabricMC/fabric/pull/1097|Biome Modification API in Fabric API]] to add your feature to biomes.   * Use [[https://github.com/FabricMC/fabric/pull/1097|Biome Modification API in Fabric API]] to add your feature to biomes.
  
Line 18: Line 208:
 <code java> <code java>
 public class ExampleMod implements ModInitializer { public class ExampleMod implements ModInitializer {
-  private static ConfiguredFeature<?, ?> ORE_WOOL_OVERWORLD = Feature.ORE +  private static ConfiguredFeature<?, ?> OVERWORLD_WOOL_ORE_CONFIGURED_FEATURE new ConfiguredFeature 
-    .configure(new OreFeatureConfig( +      (Feature.OREnew OreFeatureConfig( 
-      OreFeatureConfig.Rules.BASE_STONE_OVERWORLD+          OreConfiguredFeatures.STONE_ORE_REPLACEABLES
-      Blocks.WHITE_WOOL.getDefaultState(), +          Blocks.WHITE_WOOL.getDefaultState(), 
-      9)) // vein size +          9))// vein size 
-    .decorate(Decorator.RANGE.configure(new RangeDecoratorConfig+ 
-    UniformHeightProvider.create+  public static PlacedFeature OVERWORLD_WOOL_ORE_PLACED_FEATURE = new PlacedFeature( 
-    YOffset.fixed(0),  +      RegistryEntry.of(OVERWORLD_WOOL_ORE_CONFIGURED_FEATURE), 
-    YOffset.fixed(64))))) +      Arrays.asList( 
-    .spreadHorizontally() +          CountPlacementModifier.of(20), // number of veins per chunk 
-    .repeat(20); // number of veins per chunk+          SquarePlacementModifier.of(), // spreading horizontally 
 +          HeightRangePlacementModifier.uniform(YOffset.getBottom(), YOffset.fixed(64)) 
 +      )); // height
  
   @Override   @Override
   public void onInitialize() {   public void onInitialize() {
-    RegistryKey<ConfiguredFeature<?, ?>> oreWoolOverworld = RegistryKey.of(Registry.CONFIGURED_FEATURE_WORLDGEN+    Registry.register(BuiltinRegistries.CONFIGURED_FEATURE
-        new Identifier("tutorial", "ore_wool_overworld")); +        new Identifier("tutorial", "overworld_wool_ore"), OVERWORLD_WOOL_ORE_CONFIGURED_FEATURE); 
-    Registry.register(BuiltinRegistries.CONFIGURED_FEATUREoreWoolOverworld.getValue(), ORE_WOOL_OVERWORLD); +    Registry.register(BuiltinRegistries.PLACED_FEATUREnew Identifier("tutorial", "overworld_wool_ore"), 
-    BiomeModifications.addFeature(BiomeSelectors.foundInOverworld(), GenerationStep.Feature.UNDERGROUND_ORES, oreWoolOverworld);+        OVERWORLD_WOOL_ORE_PLACED_FEATURE); 
 +    BiomeModifications.addFeature(BiomeSelectors.foundInOverworld(), GenerationStep.Feature.UNDERGROUND_ORES, 
 +        RegistryKey.of(Registry.PLACED_FEATURE_KEY, 
 +            new Identifier("tutorial", "overworld_wool_ore")));
   }   }
 } }
Line 41: Line 236:
  
 === Result === === Result ===
-You should see wool spawning in the overworld. You can use below command to remove stone blocks surrounding you.+Always create a new world when you check the world generation result. You should see wool spawning in the overworld. You can use the command below to remove stone blocks surrounding you.
 <code> <code>
 /fill ~-8 0 ~-8 ~8 ~ ~8 minecraft:air replace minecraft:stone /fill ~-8 0 ~-8 ~8 ~ ~8 minecraft:air replace minecraft:stone
Line 51: Line 246:
 In this section, based on the code in the previous section, we will add the ore to the nether biomes. In this section, based on the code in the previous section, we will add the ore to the nether biomes.
  
-We have to replace ''OreFeatureConfig.Rules.BASE_STONE_OVERWORLD'' with ''OreFeatureConfig.Rules.BASE_STONE_NETHER'' +We need to replace ''OreConfiguredFeatures.STONE_ORE_REPLACEABLES'' with ''OreConfiguredFeatures.NETHERRACK'' 
-because blocks used as base block are different in the overall biomes and the nether biomes.+because the ore has to replace different blocks in the nether than in the overworld.
  
 <code java> <code java>
 public class ExampleMod implements ModInitializer { public class ExampleMod implements ModInitializer {
-  private static ConfiguredFeature<?, ?> ORE_WOOL_NETHER = Feature.ORE +  private static ConfiguredFeature<?, ?> NETHER_WOOL_ORE_CONFIGURED_FEATURE new ConfiguredFeature 
-    .configure(new OreFeatureConfig( +      (Feature.OREnew OreFeatureConfig( 
-      OreFeatureConfig.Rules.BASE_STONE_NETHER, // We use OreFeatureConfig.Rules.BASE_STONE_NETHER for nether +          OreConfiguredFeatures.NETHERRACK, // we use OreConfiguredFeatures.NETHERRACK here 
-      Blocks.WHITE_WOOL.getDefaultState(), +          Blocks.WHITE_WOOL.getDefaultState(), 
-      9)) +          9)); 
-    .decorate(Decorator.RANGE.configure(new RangeDecoratorConfig+ 
-      0+  public static PlacedFeature NETHER_WOOL_ORE_PLACED_FEATURE = new PlacedFeature
-      0, +      RegistryEntry.of(NETHER_WOOL_ORE_CONFIGURED_FEATURE)
-      64))+      Arrays.asList( 
-    .spreadHorizontally() +          CountPlacementModifier.of(20), 
-    .repeat(20);+          SquarePlacementModifier.of(), 
 +          HeightRangePlacementModifier.uniform(YOffset.getBottom(), YOffset.fixed(64))));
  
   @Override   @Override
   public void onInitialize() {   public void onInitialize() {
-    RegistryKey<ConfiguredFeature<?, ?>> oreWoolNether = RegistryKey.of(Registry.CONFIGURED_FEATURE_WORLDGEN+    Registry.register(BuiltinRegistries.CONFIGURED_FEATURE
-        new Identifier("tutorial", "ore_wool_nether")); +        new Identifier("tutorial", "nether_wool_ore"), NETHER_WOOL_ORE_CONFIGURED_FEATURE); 
-    Registry.register(BuiltinRegistries.CONFIGURED_FEATUREoreWoolNether.getValue(), ORE_WOOL_NETHER); +    Registry.register(BuiltinRegistries.PLACED_FEATUREnew Identifier("tutorial", "nether_wool_ore"), 
-    BiomeModifications.addFeature(BiomeSelectors.foundInTheNether(), GenerationStep.Feature.UNDERGROUND_ORES, oreWoolNether);+        NETHER_WOOL_ORE_PLACED_FEATURE); 
 +    BiomeModifications.addFeature(BiomeSelectors.foundInTheNether(), GenerationStep.Feature.UNDERGROUND_ORES, 
 +        RegistryKey.of(Registry.PLACED_FEATURE_KEY, 
 +            new Identifier("tutorial", "nether_wool_ore")));
   }   }
 } }
Line 81: Line 280:
 In this section, based on the code in the overworld section, we will add the ore to the end biomes. In this section, based on the code in the overworld section, we will add the ore to the end biomes.
  
-We replace ''OreFeatureConfig.Rules.BASE_STONE_OVERWORLD'' with ''new BlockMatchRuleTest(Blocks.END_STONE)'' +We replace ''OreConfiguredFeatures.STONE_ORE_REPLACEABLES'' with ''new BlockMatchRuleTest(Blocks.END_STONE)''.
-because endstone is used as a base block in the end biomes.+
  
 <code java> <code java>
 public class ExampleMod implements ModInitializer { public class ExampleMod implements ModInitializer {
-  private static ConfiguredFeature<?, ?> ORE_WOOL_END = Feature.ORE +  private static ConfiguredFeature<?, ?> END_WOOL_ORE_CONFIGURED_FEATURE new ConfiguredFeature 
-    .configure(new OreFeatureConfig( +      (Feature.OREnew OreFeatureConfig( 
-      new BlockMatchRuleTest(Blocks.END_STONE), // base block is endstone in the end biomes +          new BlockMatchRuleTest(Blocks.END_STONE), // we use new BlockMatchRuleTest(Blocks.END_STONE) here 
-      Blocks.WHITE_WOOL.getDefaultState(), +          Blocks.WHITE_WOOL.getDefaultState(), 
-      9)) +          9)); 
-    .decorate(Decorator.RANGE.configure(new RangeDecoratorConfig+ 
-      0+  public static PlacedFeature END_WOOL_ORE_PLACED_FEATURE = new PlacedFeature
-      0, +      RegistryEntry.of(END_WOOL_ORE_CONFIGURED_FEATURE)
-      64))+      Arrays.asList( 
-    .spreadHorizontally() +          CountPlacementModifier.of(20), 
-    .repeat(20);+          SquarePlacementModifier.of(), 
 +          HeightRangePlacementModifier.uniform(YOffset.getBottom(), YOffset.fixed(64))));
  
   @Override   @Override
   public void onInitialize() {   public void onInitialize() {
-    RegistryKey<ConfiguredFeature<?, ?>> oreWoolEnd = RegistryKey.of(Registry.CONFIGURED_FEATURE_WORLDGEN+    Registry.register(BuiltinRegistries.CONFIGURED_FEATURE
-        new Identifier("tutorial", "ore_wool_end")); +        new Identifier("tutorial", "end_wool_ore"), END_WOOL_ORE_CONFIGURED_FEATURE); 
-    Registry.register(BuiltinRegistries.CONFIGURED_FEATUREoreWoolEnd.getValue(), ORE_WOOL_END); +    Registry.register(BuiltinRegistries.PLACED_FEATUREnew Identifier("tutorial", "end_wool_ore"), 
-    BiomeModifications.addFeature(BiomeSelectors.foundInTheEnd(), GenerationStep.Feature.UNDERGROUND_ORES, oreWoolEnd);+        END_WOOL_ORE_PLACED_FEATURE); 
 +    BiomeModifications.addFeature(BiomeSelectors.foundInTheEnd(), GenerationStep.Feature.UNDERGROUND_ORES, 
 +        RegistryKey.of(Registry.PLACED_FEATURE_KEY, 
 +            new Identifier("tutorial", "end_wool_ore")));
   }   }
 } }
 </code> </code>
  
tutorial/ores.1621611298.txt.gz · Last modified: 2021/05/21 15:34 by themodderofall