Events - morelandjo/Railways-Untold GitHub Wiki
Railways Untold supports custom events — structures that are placed randomly along generated tracks. Events are defined via data packs, allowing you to add your own structures, control how often they appear, and restrict them to specific biomes.
Events support two placement modes:
-
NBT events (default) — Place a single
.nbtschematic as-is, like/place template -
Jigsaw events — Use Minecraft's jigsaw structure system for procedural assembly, like
/place structure. The structure expands from a start piece using template pools, generating a different layout each time.
The mod loads event definitions from data packs at startup and on /reload. During track generation, when the event system triggers (based on chance and distance settings in the config), it:
- Collects all loaded event definitions
- Filters by the biome at the current track position (whitelist/blacklist)
- Filters by appearance limits — excludes events that have reached their
max_appearances - Filters by triggers — optional prerequisites that must all be met (see Triggers)
- Selects one event using weighted random selection
- Validates the terrain (ground level must be within 5 blocks of track height)
- Places the schematic, connecting it inline with the track
These settings remain in config/railways-untold/config.toml and act as global controls:
| Setting | Default | Range | Description |
|---|---|---|---|
customEvents |
false |
boolean | Master toggle for the event system. Must be true for any events to place. |
eventChance |
15 |
1-100 | Percentage chance per eligible position to attempt event placement. |
eventSeparationMinDistance |
500 |
100-5000 | Minimum blocks between consecutive events. |
Place your event definition JSON files at:
data/<namespace>/railwaysuntold/events/<name>.json
For NBT events, place the corresponding .nbt schematic files at:
data/<namespace>/structure/<path>.nbt
For jigsaw events, define the structure and template pools using vanilla worldgen JSON:
data/<namespace>/worldgen/structure/<name>.json
data/<namespace>/worldgen/template_pool/<name>.json
my_events/
pack.mcmeta
data/
my_events/
railwaysuntold/
events/
abandoned_minecart.json
broken_bridge.json
structure/
events/
abandoned_minecart.nbt
broken_bridge.nbt
my_events/
pack.mcmeta
data/
my_events/
railwaysuntold/
events/
lumber_camp.json
worldgen/
structure/
lumber_camp.json
template_pool/
lumber_camp/
center.json
houses.json
decorations.json
structure/
lumber_camp/
center.nbt
house_small.nbt
house_large.nbt
well.nbt
{
"schematic": "my_events:events/abandoned_minecart",
"weight": 10,
"biome_whitelist": [],
"biome_blacklist": [],
"max_appearances": 3,
"triggers": [
{ "type": "advancement", "advancement": "minecraft:story/mine_diamond" },
{ "type": "difficulty", "min_level": 2 }
],
"loot": {
"default": "my_events:events/common_loot",
"by_type": {
"minecraft:chest": "my_events:events/chest_loot",
"minecraft:barrel": "my_events:events/barrel_loot"
},
"by_tag": {
"rare_loot": "my_events:events/rare_loot",
"food_supply": "my_events:events/food"
}
}
}{
"type": "jigsaw",
"structure": "my_events:lumber_camp",
"weight": 10,
"biome_whitelist": ["#minecraft:is_forest"],
"biome_blacklist": [],
"max_appearances": 3,
"estimated_footprint": 48
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type |
string | No | "nbt" |
Event type: "nbt" for single schematic placement, "jigsaw" for procedural jigsaw structure assembly. |
schematic |
string | NBT only | — | Resource location of the .nbt schematic. Resolves to data/<namespace>/structure/<path>.nbt. For example, "my_events:events/abandoned_minecart" loads data/my_events/structure/events/abandoned_minecart.nbt. |
structure |
string | Jigsaw only | — | Resource location of a registered worldgen structure. Must point to a valid data/<namespace>/worldgen/structure/<name>.json definition (typically a minecraft:jigsaw type structure). |
estimated_footprint |
integer | No | 48 |
Jigsaw only. Estimated length in blocks used for terrain ground-level checks before placement. Since jigsaw structures vary in size per generation, this is an approximation. |
weight |
integer | No | 10 |
Relative weight for random selection. Higher values mean the event is chosen more often compared to other events. |
biome_whitelist |
string array | No | [] |
If non-empty, the event can only appear in these biomes. Supports biome IDs ("minecraft:plains") and biome tags ("#minecraft:is_forest"). An empty array means all biomes are allowed. |
biome_blacklist |
string array | No | [] |
The event will never appear in these biomes. Applied after the whitelist. Same syntax as whitelist. |
max_appearances |
integer | No | (unlimited) | Maximum number of times this event can be placed in a world. Once reached, the event is excluded from selection. Omit or set to -1 for unlimited. |
triggers |
array | No | [] |
List of prerequisite conditions that must all be met for this event to be eligible. See Triggers below. |
loot |
object | No | (none) | Loot table configuration for containers in this event's schematic. See Loot Tables below. |
NBT event schematics must meet these validation requirements:
- Edge-to-edge track, dead-end track, or start track: The schematic must contain Create track blocks on two opposite edges (entry and exit), OR track blocks on one edge and a Dead End or Start marker block on the opposite edge
- Flat track: All track, dead end, and start blocks must be at the same Y level within the schematic
- Straight alignment: Track must run in a straight line from one edge to the opposite edge
The mod automatically detects the track direction (North/South or East/West) and rotates the schematic to align with the current track expansion direction.
Jigsaw events use Minecraft's structure generation system. The structure is defined using standard vanilla worldgen/structure and worldgen/template_pool JSON files. When placed, the jigsaw assembler procedurally selects and connects pieces from template pools, producing a different layout each time.
Requirements for jigsaw event structures:
- Track on the bounding box edges: The assembled structure must have Create track blocks (or Dead End / Start marker blocks) on the outer edges of its total bounding box. Typically, the start piece of the jigsaw structure contains the track blocks, and other pieces expand outward from it.
- Non-square start piece: The start piece should be rectangular (not square) so the mod can determine which axis the track runs along. The track axis is inferred from the longer dimension of the start piece's bounding box.
-
Standard worldgen structure: The
structurefield must reference a valid structure registered viadata/<namespace>/worldgen/structure/<name>.json. This is typically aminecraft:jigsawtype structure withstart_pool,size(depth), and other standard jigsaw fields.
Jigsaw structures randomly rotate their start piece during generation. The mod handles this by generating the structure up to 4 times with different seeds until it finds a rotation where the track axis matches the current expansion direction. If no matching rotation is found after 4 attempts, the event is skipped and track generation continues normally.
To maximize placement success, design your start piece so it works well in multiple orientations. Rectangular start pieces with the track on the long axis will match 2 out of 4 possible rotations (50% match rate per attempt, ~94% chance of matching within 4 attempts).
data/my_events/worldgen/structure/lumber_camp.json
{
"type": "minecraft:jigsaw",
"biomes": "#minecraft:is_overworld",
"step": "surface_structures",
"start_pool": "my_events:lumber_camp/center",
"size": 3,
"max_distance_from_center": 80,
"terrain_adaptation": "beard_thin",
"start_height": {
"absolute": 0
},
"project_start_to_heightmap": "WORLD_SURFACE_WG",
"use_expansion_hack": false
}data/my_events/worldgen/template_pool/lumber_camp/center.json
{
"name": "my_events:lumber_camp/center",
"fallback": "minecraft:empty",
"elements": [
{
"weight": 1,
"element": {
"element_type": "minecraft:single_pool_element",
"location": "my_events:lumber_camp/center",
"projection": "rigid",
"processors": "minecraft:empty"
}
}
]
}A schematic can use the railwaysuntold:dead_end marker block on one edge instead of a track block. This creates a dead-end event — when placed, track connects from the entry edge to the dead end position, and the expansion head is terminated (no further track is generated from that head).
The Dead End block is a marker only. It is not placed in the world — it is skipped during schematic placement, just like structure_void. It exists so that schematic builders can visually mark where the track should terminate.
To create a dead-end schematic:
- Place Create track block(s) on one edge of your structure
- Place the Dead End block on the opposite edge, at the same Y level and same perpendicular offset as the track
- Build your structure around/between them
- Save as
.nbt
A schematic can use the railwaysuntold:start marker block on one edge instead of a track block. This creates a start event — when placed, track connects from the entry edge to the start position, the current expansion head is terminated, and a new expansion head is spawned at the start block's position.
The Start block is directional — it has a facing direction (shown by a black arrow on top). The new head will expand in whichever direction the arrow points. Place the block facing the direction you want the new track to generate.
The Start block is a marker only. It is not placed in the world — it is replaced with track during schematic placement.
To create a start schematic:
- Place Create track block(s) on one edge of your structure
- Place the Start block on the opposite edge, at the same Y level and same perpendicular offset as the track
- Rotate the Start block so the arrow points in the desired new head direction
- Build your structure around/between them
- Save as
.nbt
If your event schematic contains Create station blocks (track_station), any stations left with the default name ("My Station") will be automatically renamed using the mod's procedural naming system. Names are generated in the format "Adjective BiomeWord Station" (e.g., "Sunny Plains Station", "Mossy Forest Station") based on the biome at the station's position.
If you intentionally name a station something specific in your schematic, that custom name will be preserved — only the default "My Station" name is replaced.
- Build your event structure in-game with Create tracks running through it edge-to-edge (or edge-to-dead-end, or edge-to-start)
- Use a schematic tool (like Create's Schematic and Quill) to save the structure as a
.nbtfile - Place the
.nbtfile in your data pack'sstructure/directory
A simple abandoned minecart event that appears in any biome except oceans:
data/my_events/railwaysuntold/events/abandoned_minecart.json
{
"schematic": "my_events:events/abandoned_minecart",
"weight": 20,
"biome_whitelist": [],
"biome_blacklist": ["#minecraft:is_ocean"]
}A rare event that only appears in nether biomes:
data/my_events/railwaysuntold/events/nether_crossing.json
{
"schematic": "my_events:events/nether_crossing",
"weight": 5,
"biome_whitelist": ["#minecraft:is_nether"],
"biome_blacklist": []
}Weight controls relative frequency. With these two events, the common one appears roughly 4x as often:
{ "schematic": "my_events:events/signal_post", "weight": 40 }{ "schematic": "my_events:events/collapsed_tunnel", "weight": 10 }A procedurally-generated lumber camp that uses jigsaw assembly to create a different layout each time. Only appears in forest biomes:
data/my_events/railwaysuntold/events/lumber_camp.json
{
"type": "jigsaw",
"structure": "my_events:lumber_camp",
"weight": 10,
"biome_whitelist": ["#minecraft:is_forest"],
"max_appearances": 5,
"estimated_footprint": 64
}This requires a corresponding data/my_events/worldgen/structure/lumber_camp.json and template pool definitions. The start piece template (.nbt) must contain Create track blocks on its edges.
A bandit camp that only appears on Normal+ difficulty after the player has iron tools, and only far from spawn:
data/my_events/railwaysuntold/events/bandit_camp.json
{
"schematic": "my_events:events/bandit_camp",
"weight": 5,
"biome_blacklist": ["#minecraft:is_ocean"],
"triggers": [
{ "type": "difficulty", "min_level": 2 },
{ "type": "advancement", "advancement": "minecraft:story/iron_tools" },
{ "type": "distance_from_spawn", "min_distance": 2000 }
],
"loot": {
"default": "my_events:events/bandit_loot"
}
}Triggers are optional prerequisites that gate an event behind world state or player progression. When multiple triggers are specified, all must pass (AND logic). If no triggers are specified, the event is always eligible (subject to biome filtering and placement guards).
Player-specific triggers (advancement, item_in_inventory) check the nearest online player to the expansion head position, since track generation is triggered by player proximity.
Each trigger is a JSON object with a "type" field and type-specific parameters:
| Type | Fields | Description |
|---|---|---|
advancement |
"advancement": resource location |
The nearest player has completed this advancement. Example: "minecraft:story/mine_diamond"
|
item_in_inventory |
"item": resource location |
The nearest player has this item in their inventory. Example: "minecraft:diamond_pickaxe"
|
| Type | Fields | Description |
|---|---|---|
game_time |
"min_ticks": long |
The world's game time is at least this many ticks. 24000 ticks = 1 in-game day. |
dimension |
"dimension": resource location |
The track is in this dimension. Example: "minecraft:overworld"
|
difficulty |
"min_level": int (0-3) |
Server difficulty is at least this level. 0=Peaceful, 1=Easy, 2=Normal, 3=Hard. |
moon_phase |
"phase": int (0-7) |
The current moon phase matches. 0=Full moon, 4=New moon. |
weather |
"condition": string |
Current weather matches. Values: "clear", "rain", "thunder". |
| Type | Fields | Description |
|---|---|---|
min_active_heads |
"count": int |
At least this many expansion heads are currently active. |
train_count |
"min_count": int |
At least this many Create trains exist on the network. |
distance_from_spawn |
"min_distance": int |
The expansion head is at least this many blocks (Manhattan distance) from the world spawn point. |
Event only after the player has mined diamonds:
"triggers": [
{ "type": "advancement", "advancement": "minecraft:story/mine_diamond" }
]Event only on Hard difficulty, at least 3 days into the world:
"triggers": [
{ "type": "difficulty", "min_level": 3 },
{ "type": "game_time", "min_ticks": 72000 }
]Event only far from spawn, with at least 2 trains running:
"triggers": [
{ "type": "distance_from_spawn", "min_distance": 5000 },
{ "type": "train_count", "min_count": 2 }
]Event only if the player is carrying a compass:
"triggers": [
{ "type": "item_in_inventory", "item": "minecraft:compass" }
]- If a trigger references an advancement or item that doesn't exist, it logs a warning once and the trigger always fails (the event won't appear).
- If no players are online, player-specific triggers fail (the event won't appear).
- If the head manager is unavailable,
min_active_headspasses by default.
When multiple events are eligible (after biome filtering), one is selected using weighted random:
- Sum all eligible event weights
- Roll a random number in
[0, total) - The event whose weight range contains the roll is selected
For example, with three events of weight 10, 20, and 10 (total 40):
- Event A: 25% chance (10/40)
- Event B: 50% chance (20/40)
- Event C: 25% chance (10/40)
Even after an event is selected, placement can still be rejected by these guards:
-
Minimum distance — Must be at least
eventSeparationMinDistanceblocks since the last event - Village proximity — Must be at least 300 blocks from any targeted village center
- Branch proximity — Must be at least 50 blocks since the last branch placement
- Ground level — Terrain at the start, middle, and end of the event footprint must be within 5 blocks of track height
You can assign loot tables to containers (chests, barrels, etc.) in event schematics. When the event is placed, matching containers get a loot table assigned instead of keeping the items saved in the schematic. Loot is generated when a player first opens the container.
The loot block is optional. If omitted, containers keep whatever items were saved in the schematic.
The loot object supports three levels of specificity. When a container is placed, they are checked in this order:
-
by_tag— Matches the container's CustomName (most specific) -
by_type— Matches the container's block type (e.g."minecraft:chest") -
default— Fallback loot table for any container
If no rule matches, the container keeps its original schematic contents.
"loot": {
"default": "my_events:loot/common",
"by_type": {
"minecraft:chest": "my_events:loot/chest_supplies",
"minecraft:barrel": "my_events:loot/barrel_food"
},
"by_tag": {
"rare_loot": "my_events:loot/rare_treasure",
"common_loot": "my_events:loot/basic_supplies"
}
}| Sub-field | Type | Description |
|---|---|---|
default |
string | Loot table resource location applied to any container that doesn't match by_tag or by_type. |
by_type |
object | Map of block registry ID (e.g. "minecraft:chest") to loot table resource location. |
by_tag |
object | Map of container tag name to loot table resource location. See Container Tagging below. |
To assign different loot tables to individual containers within the same schematic, you can tag them using the container's custom name:
- In-game, rename the container on an anvil to the tag name (e.g.
rare_loot) - Save the schematic
- In the JSON definition, add the tag name to
by_tag
When the event is placed, the tag name is automatically stripped from the container so players see the default container name, not the tag.
Containers with custom names that don't match any by_tag entry keep their name as-is (they are treated as intentional display names, not tags).
Loot tables are standard vanilla Minecraft loot tables. You will want to understand them and do your own research to be sure you create proper valid loot tables or they will not work. Place them at:
data/<namespace>/loot_table/<path>.json
Reference them in the event definition using "<namespace>:<path>". For example, "my_events:events/rare_loot" resolves to data/my_events/loot_table/events/rare_loot.json.
{
"schematic": "my_events:events/supply_depot",
"weight": 15,
"loot": {
"default": "my_events:events/common_supplies",
"by_tag": {
"weapons": "my_events:events/weapon_cache",
"medical": "my_events:events/medical_supplies"
}
}
}In this example, any chest named weapons in the schematic gets the weapon cache loot table, any named medical gets medical supplies, and all other containers get common supplies.
Event definitions are loaded as part of Minecraft's data pack system. Changes take effect:
- When starting/loading a world
- After running the
/reloadcommand
No server restart is required.