Events - morelandjo/Railways-Untold GitHub Wiki

Events

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 .nbt schematic 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.

How It Works

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:

  1. Collects all loaded event definitions
  2. Filters by the biome at the current track position (whitelist/blacklist)
  3. Filters by appearance limits — excludes events that have reached their max_appearances
  4. Filters by triggers — optional prerequisites that must all be met (see Triggers)
  5. Selects one event using weighted random selection
  6. Validates the terrain (ground level must be within 5 blocks of track height)
  7. Places the schematic, connecting it inline with the track

Config Settings

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.

File Location

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

NBT Event Data Pack Example

my_events/
  pack.mcmeta
  data/
    my_events/
      railwaysuntold/
        events/
          abandoned_minecart.json
          broken_bridge.json
      structure/
        events/
          abandoned_minecart.nbt
          broken_bridge.nbt

Jigsaw Event Data Pack Example

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

JSON Format

NBT Event (default)

{
  "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"
    }
  }
}

Jigsaw Event

{
  "type": "jigsaw",
  "structure": "my_events:lumber_camp",
  "weight": 10,
  "biome_whitelist": ["#minecraft:is_forest"],
  "biome_blacklist": [],
  "max_appearances": 3,
  "estimated_footprint": 48
}

Field Reference

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.

Schematic Requirements

NBT Events

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

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 structure field must reference a valid structure registered via data/<namespace>/worldgen/structure/<name>.json. This is typically a minecraft:jigsaw type structure with start_pool, size (depth), and other standard jigsaw fields.

Rotation Handling

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).

Example Worldgen Structure JSON

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"
      }
    }
  ]
}

Dead-End Events

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:

  1. Place Create track block(s) on one edge of your structure
  2. Place the Dead End block on the opposite edge, at the same Y level and same perpendicular offset as the track
  3. Build your structure around/between them
  4. Save as .nbt

Start Events

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:

  1. Place Create track block(s) on one edge of your structure
  2. Place the Start block on the opposite edge, at the same Y level and same perpendicular offset as the track
  3. Rotate the Start block so the arrow points in the desired new head direction
  4. Build your structure around/between them
  5. Save as .nbt

Station Naming

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.

Creating Event Schematics

  1. Build your event structure in-game with Create tracks running through it edge-to-edge (or edge-to-dead-end, or edge-to-start)
  2. Use a schematic tool (like Create's Schematic and Quill) to save the structure as a .nbt file
  3. Place the .nbt file in your data pack's structure/ directory

Examples

Abandoned Minecart Scene

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"]
}

Rare Nether Crossing

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": []
}

Common vs Rare Events

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 }

Jigsaw Lumber Camp

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.

Progression-Gated Bandit Camp

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

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.

Trigger Types

Each trigger is a JSON object with a "type" field and type-specific parameters:

Player Triggers

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"

World State Triggers

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".

Create / Railways Untold Triggers

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.

Trigger Examples

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" }
]

Graceful Failure

  • 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_heads passes by default.

Weighted Selection

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)

Placement Guards

Even after an event is selected, placement can still be rejected by these guards:

  1. Minimum distance — Must be at least eventSeparationMinDistance blocks since the last event
  2. Village proximity — Must be at least 300 blocks from any targeted village center
  3. Branch proximity — Must be at least 50 blocks since the last branch placement
  4. Ground level — Terrain at the start, middle, and end of the event footprint must be within 5 blocks of track height

Loot Tables

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.

Loot Configuration

The loot object supports three levels of specificity. When a container is placed, they are checked in this order:

  1. by_tag — Matches the container's CustomName (most specific)
  2. by_type — Matches the container's block type (e.g. "minecraft:chest")
  3. 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.

Container Tagging

To assign different loot tables to individual containers within the same schematic, you can tag them using the container's custom name:

  1. In-game, rename the container on an anvil to the tag name (e.g. rare_loot)
  2. Save the schematic
  3. 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 Table Files

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.

Example: Event with Mixed Loot

{
  "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.

Reloading

Event definitions are loaded as part of Minecraft's data pack system. Changes take effect:

  • When starting/loading a world
  • After running the /reload command

No server restart is required.

⚠️ **GitHub.com Fallback** ⚠️