Ore generation is driven by the world generation data pack system: a ConfiguredFeature describes what generates and where it can replace, and a PlacedFeature describes frequency and vertical placement. NeoForge datagen writes these as JSON; Fabric applies them at runtime through the Fabric API biome modifications API.
Feature Resource Keys
Declare the resource keys in common so both loaders and the datagen system can reference them without duplicating strings. Create a ExampleWorldGen class in your common project in aworld package:
NeoForge Datagen Setup
NeoForge generates world gen JSON through a DatapackBuiltinEntriesProvider backed by a RegistrySetBuilder. Create a helper class ExampleWorldGenProvider in your NeoForge data package and register it in gatherData:
Configured Feature
The configured feature defines the ore block, vein size, and which blocks it can replace. Add a configuredFeatures static method to ExampleWorldGenProvider:
TagMatchTest wraps a block tag into a RuleTest, which is what OreConfiguration.target expects. Using BlockTags.STONE_ORE_REPLACEABLES and BlockTags.DEEPSLATE_ORE_REPLACEABLES makes the ore generate in both stone and deepslate layers, matching the behaviour of vanilla iron ore. The vein size of 9 is comparable to iron. Adjust both values to taste.
Placed Feature
The placed feature references the configured feature and layers on placement modifiers: how many veins per chunk, the height range, and any surface/terrain filters:
The vanilla OrePlacements helper has private methods in 1.21, so the four modifiers are listed explicitly here. CountPlacement sets the number of vein attempts per chunk. InSquarePlacement scatters each attempt randomly within the 16×16 column. HeightRangePlacement.triangle picks a Y level with a triangular distribution that peaks at the midpoint of the range - matching how vanilla ores like iron concentrate at certain Y levels. BiomeFilter discards attempts in biomes that haven't been configured to include this feature.
Biome Modifier
A NeoForge biome modifier injects the placed feature into matching biomes. The built-in BiomeModifiers.AddFeaturesBiomeModifier adds a placed feature to all biomes matching a tag:
Fabric World Generation
On Fabric, biome modification is done at runtime in the mod initialiser using the Fabric API. The placed feature JSON generated by NeoForge datagen is shared, so only the registration call differs:
BiomeModifications reads the placed feature from the world gen registry at world load, so the JSON files produced by NeoForge datagen are picked up automatically on Fabric too. You do not need separate Fabric datagen for the feature files themselves.Testing
Run datagen first to produce the JSON files, then launch either client. Create a new world (the ore placement only applies to newly generated chunks). Use /locate biome minecraft:plains to teleport to an open area and dig down, or enable spectator mode and fly underground. You should find New Dirt veins scattered through stone and deepslate layers.
Use /place feature examplemod:new_dirt_ore at any underground position to force a single vein to generate immediately for quick verification.
You can find the source for this tutorial here:
View Source on GitHubIntroduction to Mixins (MultiLoader 1.21+)
Write your first @Inject mixin against a vanilla class, use CallbackInfoReturnable to override return values, expose private fields with @Shadow, and understand the common pitfalls that cause startup crashes.
Continue →