I found the solution to my problem:
In net.minecraftforge.common.world.ForgeBiomeModifiers there is a description of a json format used to add features to any existing biomes. I created a add_my_ore.json file in data/MYMOD/forge/biome_modifier/:
{
"type": "forge:add_features",
"biomes": "#minecraft:is_overworld",
"features": ["examplemod:ore_mine","examplemod:ore_mine_large"],
"step": "underground_ores"
}
I then add jsons to describe my ore feature in data/MYMOD/worldgen/configured_feature/ and data/MYMOD/worldgen/placed_feature/
For example, in configured feature: ore_mine.json
{
"type": "minecraft:ore",
"config": {
"discard_chance_on_air_exposure": 0.0,
"size": 20,
"targets": [
{
"state": {
"Name": "MYMOD:my_ore"
},
"target": {
"predicate_type": "minecraft:tag_match",
"tag": "minecraft:stone_ore_replaceables"
}
},
{
"state": {
"Name": "MYMOD:my_ore"
},
"target": {
"predicate_type": "minecraft:tag_match",
"tag": "minecraft:deepslate_ore_replaceables"
}
}
]
}
}
And in placed_feature: ore_mine.json
{
"feature": "examplemod:ore_mine",
"placement": [
{
"type": "minecraft:count",
"count": 16
},
{
"type": "minecraft:in_square"
},
{
"type": "minecraft:height_range",
"height": {
"type": "minecraft:trapezoid",
"max_inclusive": {
"absolute": 112
},
"min_inclusive": {
"absolute": -16
}
}
},
{
"type": "minecraft:biome"
}
]
}
This combination of files causes forge to create a ConfiguredFeature to add my custom ore using the same built-in class that places built-in ores, and adds this feature to all Biomes. Of course, I still needed to write the code to define my custom ore, but this solved the world-gen part.