Custom Dungeon
The mod is the engine: worldgen, instanced dimensions, portals, keys, waves, bosses, rules, and rewards. You add your own dungeons as JSON (and optional SNBT rooms) without writing Java.
The jar ships three example dungeons so you can test the engine and copy the format. They are optional. Delete or disable them and ship only your content.
- Minecraft: 1.16.5
- Loader: Forge 36.1+
- Mod id:
customdungeon - License: MIT
Features
- JSON dungeons — one folder per dungeon under
data/<namespace>/dungeons/<name>/ - Two worldgen modes — surface shrine + portal into an instance, or a full dungeon embedded in the Overworld / Nether / End
- ASCII layouts — draw rooms with letters,
-and|; stack floors for 3D maps - Procedural or SNBT rooms — palettes, shapes, loot chests, or structure-block exports
- Waves and bosses — equipment, health, custom loot tables
- Portal activation — catalyst, bound key, both, or always open
- Run rules — break/place protection, PvP, timers, keep inventory, room constraints (mining fatigue, slowness, …)
- Rewards — loot tables, XP, commands, messages
- Server overrides — tweak dungeons from
config/customdungeon/dungeons/without repacking
Example dungeons (optional)
Three examples are included so you can play, copy, or delete them. The mod works with zero of them. Put your own folders under data/<namespace>/dungeons/ (or disable the examples below).
| Dungeon | Where | How you enter |
|---|---|---|
Ancient Crypt (example_crypt) |
Overworld forests, plains, taiga, hills | Surface shrine. Hold stone and a bound dungeon key, then use the portal. |
Ashen Bastion (example_nether) |
The Nether | Full fortress generated in the world. Walk in (protected rooms, mobs, entrance). Also /cd start. |
End Citadel (example_end) |
End highlands / midlands | Embedded SNBT citadel. Walk in, or /cd start. |
To remove the examples from a pack:
- Disable — in a datapack or
config/customdungeon/dungeons/<name>/dungeon.json, set"enabled": falseforexample_crypt,example_nether, andexample_end. - Override — ship a datapack that replaces those folders with your own dungeons (or empty stubs).
Already-generated chunks will not grow new shrines or embeds. Explore new terrain, start a new world, or use /cd place <id>.
Playing
Crypt shrine
- Craft a Dungeon Key (gold ingots + ender pearl).
- Find a shrine (
/cd locate example_cryptif you are op) or/cd place example_crypt. - Right-click the portal with the key to bind it.
- Hold stone and the bound key, then use the portal.
- Clear rooms. On victory you get a title countdown, then extract. Use a Dungeon Exit Token to leave early.
Embedded dungeons (Nether / End)
Walk into the structure. Rooms are protected, encounters spawn, and leaving unbinds you in place (you stay in that dimension).
Items
| Item | Recipe / use |
|---|---|
| Dungeon Key | Gold ingots around an ender pearl. Bind on a shrine, or NBT Dungeon:"namespace:id". |
| Dungeon Sigil | Stone + ender pearls + a key. Right-click to teleport to the bound shrine / world dungeon. |
| Dungeon Exit Token | Creative tab / loot. Leaves the current instance run. |
/give @p customdungeon:dungeon_key{Dungeon:"customdungeon:example_crypt"} 1
Commands
Alias: /cd. Ids can be example_crypt or customdungeon:example_crypt.
/customdungeon list
/customdungeon info <id>
/customdungeon start <id>
/customdungeon leave
/customdungeon locate [id] # op — clickable coords in front of the shrine
/customdungeon place <id> # op — shrine or embed at your feet
/customdungeon reload # op — config overlay only (use /reload for datapacks)
/customdungeon complete # op — force-clear the current run
Vanilla:
/locate customdungeon:dungeon_shrine
For datapack authors
Each dungeon is one folder:
data/<namespace>/dungeons/<name>/
dungeon.json required
portal.json optional — merged into dungeon.portal
rooms/<room>.json procedural (or SNBT) room templates
rooms/<room>.snbt structure-block export as a room
encounters/<id>.json waves / boss
Dungeon id is <namespace>:<name> unless dungeon.json sets "id".
Relative refs ("encounters": ["skeletons"]) resolve to <namespace>:<name>/skeletons.
Omitted keys use defaults. Only write what you change.
Vanilla /reload reloads datapacks. /cd reload reloads config/customdungeon/dungeons/ only.
The mod provides empty dimension customdungeon:dungeon for instanced runs. Point portal.dimension at it, or at your own datapack dimension.
Minimal shrine dungeon
{
"display": { "name": "My Crypt" },
"natural_spawn": {
"enabled": true,
"dimensions": ["minecraft:overworld"],
"biomes": ["#is_forest"],
"rarity": 48
},
"layout": {
"shape": ["E-H-B"],
"rooms": [
{ "id": "entrance", "encounters": ["skels"] },
{ "id": "hall", "encounters": ["skels"] },
{ "id": "boss", "encounters": ["boss"] }
]
}
}
{
"activation": "both",
"frame": "minecraft:stone_bricks",
"catalyst": "minecraft:stone"
}
Put the portal object in portal.json or under "portal" in dungeon.json.
Embedded dungeon (no shrine)
"natural_spawn": {
"enabled": true,
"embed": true,
"dimensions": ["minecraft:the_nether"],
"layer": "nether",
"min_y": 40,
"max_y": 90,
"rarity": 24
}
ASCII shape
Letters are rooms. - | + link them. A 2D list is one floor; a list of lists stacks floors on Y.
"shape": [
[" G ", " | ", "E-H-B"],
[" A ", " | ", " T "]
],
"cells": { "E": "entrance", "H": "hall", "B": "boss", "G": "graves", "A": "chapel", "T": "treasury" }
If cells is omitted, the letter matches the room id prefix. start_room defaults to the first room.
Procedural rooms get a corridor of gap blocks. SNBT rooms sit wall-to-wall (doors punched).
Other layouts: omit shape for a linear row; "type": "grid" with grid_x / grid_z; or set x / z / next for exact offsets.
JSON reference
Defaults apply when a key is missing.
dungeon.json
| Field | Default | Notes |
|---|---|---|
id |
folder id | Override registry name. |
enabled |
true |
false = loaded but never starts / generates. |
display.name |
path | Chat title on enter. |
display.description |
"" |
Sent after enter. |
natural_spawn |
off | Alias: placement (overlay, natural_spawn wins). |
portal |
off unless catalyst / frame / activation | Can live in portal.json. |
entry |
command + key + portal | Who can enter. |
instance |
customdungeon:dungeon |
Instance world + reset. |
layout |
one empty room | Room graph. |
rules |
see below | Combat / grief / death. |
rewards |
none | on_complete / on_fail / on_room_clear. |
natural_spawn
| Field | Default | Notes |
|---|---|---|
enabled |
false |
true = generate in the world. |
embed |
false |
true = place rooms in the world. false = shrine portal only. |
dimensions |
overworld | Dimension ids. |
biomes |
all | Exact id, category:jungle, or tags (#is_forest, #nether, #end, …). |
biome_blacklist |
[] |
Same syntax. Blacklist wins. |
layer |
surface |
surface (heightmap), nether (air above solid), sky (max_y). |
min_y / max_y |
5 / 80 |
Used for nether / sky embeds. Shrines sit on the surface. |
rarity |
80 |
1/N on the chunk grid. Higher = rarer. |
min_chunk_distance |
16 |
Grid spacing in chunks. |
spread_salt |
0 |
Keep two dungeons off the same chunks. |
avoid_water |
true |
Skip water at the stand position. |
portal / portal.json
| Field | Default | Notes |
|---|---|---|
activation |
catalyst if catalyst set, else always |
always / catalyst / key / both. |
frame |
minecraft:obsidian |
Shrine frame block. |
textures.portal |
minecraft:block/crying_obsidian |
Atlas sprite on the portal faces. |
color |
#5B21B6 |
Tint #RRGGBB. |
size.width / height |
4 / 5 |
Frame size. |
catalyst |
none | Item id string, or { "item", "count", "consume", "nbt" }. |
sound |
block.end_portal.spawn |
On enter. |
dimension |
customdungeon:dungeon |
Instance destination. |
return_portal |
true |
Exit portal inside the instance. |
Unbound portal: sneak-use a key with NBT Dungeon to bind it.
entry
| Field | Default | Notes |
|---|---|---|
methods |
command, key, portal |
Subset of those three. |
key_item |
customdungeon:dungeon_key |
|
consume_key |
true |
|
require_key_nbt |
false |
Key NBT Dungeon must match. |
max_players |
8 |
Per instance. |
cooldown_seconds |
0 |
Per player. |
permission_level |
0 |
Vanilla permission (2 = op). |
required_advancement |
none | |
required_scoreboard / required_score |
unused | |
creative_bypass |
true |
Creative skips method / catalyst / cooldown. |
shared |
false |
Reuse a running instance. |
instance
| Field | Default | Notes |
|---|---|---|
dimension |
portal dimension | Must exist. |
origin_y |
64 |
Build Y in the instance world. |
padding |
24 |
Bounds around rooms. |
reset_on_complete / empty / fail |
true |
Tear down the instance. |
idle_timeout_seconds |
600 |
Empty timeout. |
extract_seconds |
30 |
Victory countdown before teleport. 0 = immediate. |
layout
| Field | Default | Notes |
|---|---|---|
type |
inferred | shape / linear / grid / free / single. |
start_room |
first room | Spawn room. |
corridor_width / height |
3 / 3 |
Walkable air. |
gap |
5 |
Air between procedural rooms. |
floor_gap |
gap |
Vertical air between stacked floors. |
corridor_block |
minecraft:stone_bricks |
Also corridor_floor / wall / ceiling. |
rooms[].id |
Local id. | |
rooms[].template |
same as id |
Room file name. |
rooms[].encounters |
[] |
Encounter ids. |
rooms[].next |
[] |
Extra corridor targets. |
rooms[].x / y / z |
Exact offset (or offset: {x,y,z}). |
rooms/<id>.json
| Field | Default | Notes |
|---|---|---|
style |
none | Preset: crypt, nether, library, prison, sewer, end, jungle, ice, sand, … |
shape |
box |
box, circle, oval, cross, ring, diamond. |
size |
{x:13,y:7,z:13} |
Procedural only. SNBT size comes from the file. |
palette |
stone bricks | floor, wall, ceiling, accent, door, light, pillar. |
features |
style preset | See below. |
loot |
[] |
Chest loot table ids. |
constraints |
none | Effects while standing in this room. |
ladders |
true |
Vertical shafts between stacked floors. false on a room skips the whole link. |
snbt / snbt_file / structure |
none | Structure room instead of procedural. |
features: pillars, torches, windows, chest_count, carpet, fountain, cobwebs, skulls, bones, bookshelves, braziers, altar, hanging_lights, cages, vines, lava_moat, water_moat, floor_pattern (spots, checker, mosaic, path, rings, cracked, bones).
Constraints
On rules (whole run) or a room. Reapplied while inside, cleared on leave. Room effects stack.
Booleans: slowness, mining_fatigue, weakness, hunger, wither, poison, glowing, nausea, darkness (blindness on 1.16). Amplifiers: slowness_level, mining_level, wither_level. Extra list: "effects": [{ "id": "minecraft:wither", "amplifier": 0, "duration": 80, "particles": true }].
encounters/<id>.json
| Field | Default | Notes |
|---|---|---|
trigger |
enter |
Player steps into the room. |
trigger_delay_ticks |
20 |
Wait before the first wave. |
waves |
[] |
{ "delay_ticks", "mobs": [ … ] }. |
boss |
none | After waves; room waits until tagged mobs die. |
Mob spec: entity, count (number or {min,max}), name, health, damage, nbt, glowing, loot_table, equipment.helmet / chestplate / leggings / boots / mainhand.
rules
| Field | Default | Notes |
|---|---|---|
time_limit_seconds |
0 |
0 = none. |
keep_inventory |
true |
|
pvp |
false |
|
allow_break / allow_place |
false |
Else only the allow-lists. |
allow_interact |
true |
|
allowed_break_blocks / allowed_place_blocks |
[] |
|
mob_griefing |
false |
|
difficulty |
inherit |
Or peaceful / easy / normal / hard. |
mob_health_multiplier / mob_damage_multiplier / player_damage_multiplier |
1.0 |
|
respawn_in_dungeon |
true |
false = leave on death. |
max_deaths |
0 |
0 = unlimited. |
drop_mob_loot |
true |
|
prevent_sleep |
true |
|
constraints |
none | Dungeon-wide effects. |
rewards
on_complete / on_fail / on_room_clear: loot_table, xp, commands, broadcast, message, item_count.
Config
config/customdungeon-common.toml
| Key | Default | Notes |
|---|---|---|
general.world_gen |
true |
Natural shrines / embeds. |
general.debug_logs |
false |
|
general.max_active_instances |
32 |
0 = unlimited. |
general.instance_spacing |
512 |
Gap between instances. |
general.instance_origin_y |
64 |
Fallback Y. |
general.allow_config_overrides |
true |
Load config/customdungeon/dungeons/**. |
general.protect_dungeon_blocks |
true |
Enforce break / place rules. |
general.default_world_gen_chance |
80 |
Fallback rarity. |
Quick test
/cd start example_crypt
/cd start example_nether
/cd start example_end
/cd place example_crypt
Crypt portal: bound key + stone. Nether / End: explore new chunks in that dimension.

