Ore features
Ore features are used to generate ores in the world. They are configured using the ore
or scattered_ore
feature types
(ore
is recommended for most cases).
Configuration
The following options are available for ore configurations:
Option | Type | Description |
---|---|---|
targets |
A list of TargetBlockState s |
A list which determines what block to use for specific targets. Needs a target and a state option. See below for more details. |
size |
An int . (Range limit in Json is \([0;64]\)) |
Determines the volume size of the ore. |
discard_chance_on_air_exposure |
A float . (Range limit in Json is \([0;1]\)) |
Determines the chance that the ore will be discarded if it is exposed to air. 1 means that the ore will never be exposed to air. |
In code, the OreConfiguration
class is used to configure the feature.
Targets
As mentioned above, the targets
option is a list of targets. The target
option is a so calledRuleTest
. A RuleTest
is
pretty much the same thing as Predicate<BlockState>
in Java. The state
option is a BlockStateProvider
which determines what block to use for the specific target.
The following RuleTests
are available:
Name | Description | Example |
---|---|---|
minecraft:always_true |
Matches every block | |
minecraft:block_match |
Matches a specific block | |
minecraft:blockstate_match |
Matches a specific blockstate | |
minecraft:tag_match |
Matches a specific block tag | |
minecraft:random_block_match |
Matches the given block with a probability. The probability should be a value between 0.0 and 1.0. | |
minecraft:random_blockstate_match |
Matches the given blockstate with a probability. The probability should be a value between 0.0 and 1.0. | |
nova:material_match |
Matches a specific nova material |
Custom RuleTests
You can also implement your own custom RuleTests
by implementing the Minecraft RuleTest
interface or extending
Nova's NovaRuleTest
/NovaMaterialTest
classes. Nova's classes provide a bit more parameters such as the Level
and
BlockPos
(or even the NovaMaterial
via NovaMaterialTest
).
You will also need to provide a RuleTestType
, which specifies how your RuleTest implementation is (de)serialized.
This can either be done by implementing the RuleTestType
interface or creating it inline by just registering the Codec
in the FeatureRegistry
. Check out the Codecs page for more information on Mojang's serialization system.
Here's the code for Nova's MaterialMatchTest
implementation as an example:
object RuleTests : MinecraftUtilTypeRegistry by ExampleAddon.registry {
@OptIn(ExperimentalWorldGen::class)
val MATERIAL_MATCH_TEST_TYPE = registerRuleTestType("material_match", MaterialMatchTest.CODEC)
}
class MaterialMatchTest(val material: BlockNovaMaterial) : NovaMaterialTest() {
override fun test(material: BlockNovaMaterial, level: Level, pos: BlockPos, state: BlockState, random: RandomSource): Boolean {
return material == this.material
}
override fun getType(): RuleTestType<*> = RuleTests.MATERIAL_MATCH_TEST_TYPE
companion object {
@JvmField // (1)!
val CODEC: Codec<MaterialMatchTest> =
BlockNovaMaterial.CODEC
.fieldOf("material")
.xmap(::MaterialMatchTest, MaterialMatchTest::material)
.codec()
.stable()
}
}
- This allows
CODEC
to be accessed as a field from Java code instead of having to callgetCODEC()
object MaterialMatchTestType : RuleTestType<MaterialMatchTest> {
private val CODEC: Codec<MaterialMatchTest> =
BlockNovaMaterial.CODEC
.fieldOf("material")
.xmap(::MaterialMatchTest, MaterialMatchTest::material)
.codec()
.stable()
override fun codec() = CODEC
}
object RuleTests : MinecraftUtilTypeRegistry by ExampleAddon.registry {
@OptIn(ExperimentalWorldGen::class)
val MATERIAL_MATCH_TEST_TYPE = registerRuleTestType("material_match", MaterialMatchTestType)
}
class MaterialMatchTest(val material: BlockNovaMaterial) : NovaMaterialTest() {
override fun test(material: BlockNovaMaterial, level: Level, pos: BlockPos, state: BlockState, random: RandomSource): Boolean {
return material == this.material
}
override fun getType(): RuleTestType<*> = MaterialMatchTestType
}
Example
As an example, here's the configured- and placed feature of star shards ore from the machines addon.
@OptIn(ExperimentalWorldGen::class)
@Init(stage = InitStage.POST_PACK_PRE_WORLD)
object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry {
val ORE_STAR_SHARDS = registerConfiguredFeature(
"ore_star_shards",
Feature.ORE,
OreConfiguration(
listOf( // (1)!
OreConfiguration.target(
TagMatchTest(BlockTags.STONE_ORE_REPLACEABLES),
WrapperBlockState(Blocks.STAR_SHARDS_ORE) // (2)!
),
OreConfiguration.target(
TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES),
WrapperBlockState(Blocks.DEEPSLATE_STAR_SHARDS_ORE)
)
),
4, // (3)!
0.0f // (4)!
)
)
}
- Specify that
star_shards_ore
should be used to replace normal stone anddeepslate_star_shards_ore
should be used to replace deepslate. - Since blocks registered via Nova aren't registered in the vanilla blocks registry and thus don't have a blockstate, we need to wrap an block material in a
WrapperBlockState
.
These objects are later detected before the block is placed and are passed to Nova for correct placement. - The size of the ore vein.
- The chance that the ore will be discarded if it's exposed to air.
For placements, it's pretty useful to define a few util functions to create the PlacementModifier
list.
private fun orePlacement(firstModifier: PlacementModifier, lastModifier: PlacementModifier) =
listOf(firstModifier, InSquarePlacement.spread(), lastModifier, BiomeFilter.biome())
/**
* @param count The amount of ores per chunk
*/
private fun commonOrePlacement(count: Int, lastModifier: PlacementModifier) =
orePlacement(CountPlacement.of(count), lastModifier)
/**
* @param chance The chance of an ore to spawn per chunk. 7 = 1 in 7 chunks
*/
private fun rareOrePlacement(chance: Int, lastModifier: PlacementModifier) =
orePlacement(RarityFilter.onAverageOnceEvery(chance), lastModifier)
Vanilla ore placement examples
// ore_iron_upper
commonOrePlacement(90, HeightRangePlacement.triangle(VerticalAnchor.absolute(80), VerticalAnchor.absolute(384)))
// ore_iron_middle
commonOrePlacement(10, HeightRangePlacement.triangle(VerticalAnchor.absolute(-24), VerticalAnchor.absolute(56)))
// ore_iron_small
commonOrePlacement(10, HeightRangePlacement.uniform(VerticalAnchor.bottom(), VerticalAnchor.absolute(72)))
// ore_diamond
commonOrePlacement(7, HeightRangePlacement.triangle(VerticalAnchor.aboveBottom(-80), VerticalAnchor.aboveBottom(80)))
// ore_diamond_large
rareOrePlacement(9, HeightRangePlacement.triangle(VerticalAnchor.aboveBottom(-80), VerticalAnchor.aboveBottom(80)))
// ore_diamond_buried
commonOrePlacement(4, HeightRangePlacement.triangle(VerticalAnchor.aboveBottom(-80), VerticalAnchor.aboveBottom(80)))
@OptIn(ExperimentalWorldGen::class)
@Init(stage = InitStage.POST_PACK_PRE_WORLD)
object PlacedFeatures : FeatureRegistry by ExampleAddon.registry {
val ORE_STAR_SHARDS = placedFeature("ore_star_shards", ConfiguredFeatures.ORE_STAR_SHARDS)
.modifiers(commonOrePlacement(30, HeightRangePlacement.uniform(VerticalAnchor.absolute(120), VerticalAnchor.top()))) // (1)!
.register()
/* ... util placement functions ... */
}
- 30 tries per chunk and only place the ore at a high altitude (y>120).
{
"type": "minecraft:ore",
"config": {
"discard_chance_on_air_exposure": 0.0,
"size": 4,
"targets": [ // (1)!
{
"state": {
"Name": "machines:star_shards_ore"
},
"target": {
"predicate_type": "minecraft:tag_match",
"tag": "minecraft:stone_ore_replaceables"
}
},
{
"state": {
"Name": "machines:deepslate_star_shards_ore"
},
"target": {
"predicate_type": "minecraft:tag_match",
"tag": "minecraft:deepslate_ore_replaceables"
}
}
]
}
- Specify that
star_shards_ore
should be used to replace normal stone anddeepslate_star_shards_ore
should be used to replace deepslate.
{
"feature": "machines:ore_star_shards",
"placement": [
{
"type": "minecraft:count",
"count": 30 // (1)!
},
{
"type": "minecraft:in_square" // (2)!
},
{
"type": "minecraft:height_range", // (3)!
"height": {
"type": "minecraft:uniform",
"max_inclusive": {
"below_top": 0
},
"min_inclusive": {
"absolute": 120
}
}
},
{
"type": "minecraft:biome" // (4)!
}
]
}
- 30 tries per chunk.
- Spread the tries in a square.
- Place the ore above y-level 119.
- Discard the try if we moved into a biome that doesn't generate star shards.