Description
Ore Generation Configuration
Ore Generation Configuration is a server-side-behavior NeoForge mod for Minecraft 1.21.1 that controls ore block generation in newly generated chunks.
It can deny or reduce vanilla and modded ores. It does not add blocks, items, entities, recipes, or regenerating ore veins.
The mod contains no client gameplay, assets, networking, blocks, items, or entities. It can run on a dedicated server, and it can also run in singleplayer because singleplayer uses an integrated server. Multiplayer clients should not need to install it to join a server that has it, because the server sends the resulting normal chunk data.
Technical Approach
The mod uses NeoForge ChunkEvent.Load and only queues chunks where isNewChunk() is true. On later server ticks it scans those newly generated chunks once, finds configured ore block ids, and replaces matching blocks according to ores.json.
This was chosen over biome modifiers, placed-feature mutation, or mixins because it gives reliable block-level frontier gating across vanilla and modded ores without fragile worldgen hooks.
Limitations:
- Existing chunks are not modified.
- Loaded chunks are not scanned continuously.
- Player-placed blocks are not targeted by normal operation.
- Ores not listed in
ores.jsonare left alone. - If Regenerating Ore Veins is installed, OGC skips positions already tracked by that mod so managed regenerating veins are not reduced or removed by OGC.
Config Files
The mod creates:
config/ore_generation_configuration/global.jsonconfig/ore_generation_configuration/areas.jsonconfig/ore_generation_configuration/ores.json
If a config file fails to parse, the mod logs the problem to latest.log and tells players on login or /ogc reload that the configuration has warnings or errors.
global.json
Example:
{
"enabled": true,
"debug_logging": false,
"dry_run": false,
"fail_on_unknown_blocks": false,
"fail_on_unknown_biomes": false,
"default_keep_chance": 1.0,
"print_startup_summary": true
}
Fields:
enabled: disables all processing when false.debug_logging: logs every processed new chunk.dry_run: counts matching blocks but does not replace them.fail_on_unknown_blocks: reports unknown target blocks as errors instead of warnings.fail_on_unknown_biomes: reserved for stricter future biome validation.default_keep_chance: fallback chance for a matching ore block to remain.print_startup_summary: logs loaded area and rule counts.
areas.json
Areas are named spatial filters using In Control!-style area semantics.
Supported area types:
box: rectangular prism.dimx,dimy, anddimzare half-extents from the center.sphere: ellipsoid.dimx,dimy, anddimzare the X/Y/Z radii from the center.cylinder: vertical elliptical cylinder.dimxanddimzare horizontal radii, anddimyis vertical half-height.
Example:
[
{
"dimension": "minecraft:overworld",
"name": "frontier_0",
"type": "box",
"x": 0,
"y": 128,
"z": 0,
"dimx": 128,
"dimy": 256,
"dimz": 128
}
]
Area fields:
name: unique area id.dimension: optional dimension id. If omitted, the area can match any dimension.type:box,sphere, orcylinder.x,y,z: center of the area.dimx,dimy,dimz: In Control-style half-size/radius values, not full dimensions.
Examples:
- A
boxat0,64,0withdimx: 10,dimy: 20,dimz: 30coversx=-10..10,y=44..84, andz=-30..30. - A
spherewithdimx: 16,dimy: 8,dimz: 16is an ellipsoid centered onx,y,z. - A
cylinderwithdimx: 32,dimy: 128,dimz: 32is a vertical cylinder with radius 32 and height 257 blocks.
The default areas.json creates frontier boxes centered at 0, 128, 0 with these X/Z half-widths:
| Area | Half-width |
|---|---|
frontier_0 |
128 |
frontier_1 |
224 |
frontier_2 |
448 |
frontier_3 |
768 |
frontier_4 |
1152 |
frontier_5 |
1600 |
frontier_6 |
2304 |
frontier_7 |
3072 |
frontier_8 |
4096 |
ores.json
Each rule targets ore blocks and decides whether matching generated blocks stay or are replaced.
Example:
{
"id": "deny_iron_before_frontier_2",
"blocks": [
"minecraft:iron_ore",
"minecraft:deepslate_iron_ore"
],
"dimension_whitelist": [
"minecraft:overworld"
],
"area_whitelist": [
"frontier_1"
],
"keep_chance": 0.0,
"replacement": "auto",
"priority": 20
}
Rule fields:
id: unique rule id.enabled: optional, defaults totrue.blocks: block ids to target. Works with vanilla and modded ore blocks.dimension_whitelist: optional dimensions where the rule can apply.dimension_blacklist: dimensions where the rule cannot apply.biome_whitelist: exact biome ids or tags such as#c:is_jungle.biome_blacklist: exact biome ids or tags where the rule cannot apply.area_whitelist: areas where the rule can apply.area_blacklist: areas where the rule cannot apply.min_yandmax_y: optional vertical range. If omitted, all Y levels are considered. When an area is used, that area'syanddimystill constrain the rule.keep_chance:0.0..1.0chance for a matching ore block to remain.generation_multiplier: accepted as an alias forkeep_chance.replacement:auto,nothing, a block id, or a list of block ids.weight: optional list of replacement weights whenreplacementis a list.priority: lower values run first. If priorities tie, the matching rule with the lowerkeep_chancetakes precedence. If both priority andkeep_chancetie, JSON file order wins.
Filter behavior:
- If neither whitelist nor blacklist is provided for a filter type, that filter does not restrict the rule.
- If only a whitelist is provided, the rule applies only inside that whitelist.
- If only a blacklist is provided, the rule applies everywhere except that blacklist.
- If both are provided and overlap, the blacklist takes precedence.
- Empty lists such as
"biome_whitelist": []are treated the same as omitting the field. - All filters combine as constraints. For example,
area_whitelist: ["frontier_0"]plusmin_y: -64andmax_y: 64applies only insidefrontier_0and only between Y -64 and 64.
Dimension and biome entries:
- Exact ids are supported, such as
"minecraft:overworld"or"minecraft:plains". - Biome tags are supported with
#, such as"#c:is_jungle". - Namespace wildcards are supported with
modid:*, such as"biomeswevegone:*"for all biomes from that mod or"mydimensions:*"for all dimensions from that mod.
Conflict behavior:
- Rules are sorted by
priority, then by lowerkeep_chance, then by the order they appear inores.json. - For a given generated ore block, the first matching rule is applied.
- Later matching rules for that same block position are ignored.
Example: a global 50% ore reduction can use priority 100 and keep_chance: 0.5. A frontier-specific rule for the same ore blocks can also use priority 100 with keep_chance: 0.0 and area_whitelist: ["frontier_0"]. Inside frontier_0, the 0% rule wins for its listed blocks. Outside frontier_0, it does not match, so the global 50% rule applies.
Replacement examples:
"replacement": "auto"
auto tries nearby base stone first, then falls back to stone, deepslate, netherrack, or end stone based on context.
"replacement": "nothing"
nothing replaces the ore with air.
"replacement": "minecraft:stone"
A single block id replaces every denied ore with that block.
{
"replacement": [
"minecraft:coal_ore",
"minecraft:deepslate_coal_ore"
],
"weight": [
1,
1
]
}
A replacement list chooses one replacement block with weighted random selection. If weight is omitted, every replacement has weight 1.
Commands
Reload configuration without restarting:
/ogc reload
/ore_generation_configuration reload
Reloading affects newly generated chunks after the reload. It does not revisit chunks already generated before the reload.
Testing
Suggested test flow:
- Start a new world or travel into terrain that has never generated.
- Set
dry_runtotrueanddebug_loggingtotrue. - Run
/ogc reload. - Generate new chunks and check
latest.logfor dry-run match counts. - Set
dry_runtofalse, reload, and generate fresh chunks. - Inspect ores in denied frontiers.
License
MIT


