Feature Advancements - bh679/dungeon-train-mc GitHub Wiki

Advancements

Since: v0.236.0 (PR #292) β€” 93 advancements as of v0.501.1.

Dungeon Train ships two custom advancement tabs, shown on the vanilla advancement screen (press L in any Dungeon Train world):

  • Dungeon Train (78) β€” player-facing milestones: riding, exploring, chest-hunting, reading the train's books, and befriending (or murdering) your fellow passengers.
  • Editor (15) β€” content-authoring milestones that walk you through the build pipeline.

Both tabs persist across worlds by player UUID β€” earning an advancement in one world unlocks it on every later world. Since v0.310.0 this extends to every advancement β€” vanilla Minecraft and other mods included, not just the two Dungeon Train tabs (see Cross-world persistence below). Icons below are framed by tier: ⬛ Task (plain), 🟑 Goal (rounded), 🟣 Challenge (spiked). The Hint column mirrors the in-game tooltip shown for an advancement you haven't earned yet (??? where no hint is written).

Dungeon Train tab

Dungeon Train advancement tree

Icon Advancement Description Hint Type Unlocks from
Dungeon Train Dungeon Train All aboard. β€” Task β€” (root)
Dungeon Train Explorer Dungeon Train Explorer Traverse 100 carriages in a single life. How far through the train can you make it? Task Dungeon Train
Cover me in ...leather? Cover me in ...leather? Wear a full set of leather armor with better combined stats than full diamond. Some of this loot rolls better than it looks. Challenge Dungeon Train Explorer
Enjoying the Ride Enjoying the Ride Spend two hours on board the train. Time accumulates across every world. Lets log some hours. Dying is fine. Task Dungeon Train Explorer
One Long Ride One Long Ride Spend an unbroken hour aboard the train in a single life. The platform is a rumour now. Dying resets it. How long can you stay? Goal Enjoying the Ride
Reality Escaped Reality Escaped Spend two unbroken hours aboard the train in a single life β€” no death, no dismount. Just you and the rails. Double it. Challenge One Long Ride
Settling In Settling In Spend ten hours aboard the train β€” long enough to settle in. Time accumulates across every world. You'll likely need more then one life to hit this timeframe. Goal Enjoying the Ride
I Live Here Now I Live Here Now A full day on the rails β€” twenty-four hours aboard. The platform has long since gone. The horizon is just track. The train is home. Investing crazy hours into the game Challenge Settling In
Help, I Can't Get Off Help, I Can't Get Off Three days on the rails. You've tried the doors. They don't open. They never did. Please tell me you took breaks. Challenge I Live Here Now
Escaping Spawn Escaping Spawn Ride 10,000 metres aboard the train in a single life. Just stay aboard and dont die. Task Dungeon Train Explorer
Covering ground Covering ground Ride 100,000 metres aboard the train across all your runs. Keep riding. Task Escaping Spawn
Cross-Country Cross-Country Ride 100,000 metres aboard the train in a single life. Thats a long ride without dying. Goal Covering ground
The Long Run The Long Run Ride 250,000 metres aboard the train in a single life β€” without dying once. Wow. In one life? Challenge Cross-Country
I got so far I got so far One million metres. You tried so hard and got so far β€” but in the end, does it even matter? Deaths don't matter, distance does. Challenge Covering ground
Inter-Dimensional Passenger Inter-Dimensional Passenger Ride the train into the Nether, where the overworld gives way to netherrack and fire. Can the train take you there? Goal Dungeon Train Explorer
Voided Warranty Voided Warranty Ride the train into the void, where the world has crumbled away to nothing beneath the rails. Stay aboard past the point where the ground gives out. Goal Inter-Dimensional Passenger
End of the Line End of the Line Reach the floating End islands adrift in the disintegration band. Ride on through the dark β€” pale islands drift out there. Goal Voided Warranty
The Upside Down The Upside Down Ride the train into the upside-down, where the sky mirrors the ground and the world hangs the wrong way round. Some stretches of track don't just look wrong. They are wrong. Goal End of the Line
Reassembly Required Reassembly Required Ride out through the scattered islands as the upside-down disperses and the real overworld reassembles beneath you. The world's stitching itself back together. Goal The Upside Down
Re-Over-World Re-Over-World Cross all the way through the void and the End, and ride back onto solid overworld ground. All the way through the dark, and out the far side. Goal Reassembly Required
Nether Return Again Nether Return Again Loop all the way around β€” the Nether, the void, the End islands, back to solid ground β€” then ride the train into the Nether a second time. ??? Goal Re-Over-World
Long-Haul Passenger Long-Haul Passenger Traverse 1,000 carriages in a single life. Make it really far. Goal Dungeon Train Explorer
Nearing Infinity Nearing Infinity Traverse 10,000 carriages in a single life. Absurdly far. Challenge Long-Haul Passenger
Over the Top Over the Top Run across the roof of a carriage group without dropping inside. Skip a carriage Task Dungeon Train Explorer
Skip the Grind Skip the Grind Run the length of 3 carriage groups along the train roof β€” in one go, without dropping inside. Dont touch the ground, dont leave the train. Challenge Over the Top
Pacifist Pacifist Traverse 100 carriages in a single life without dealing any damage. Not every journey needs a fight. Task Dungeon Train Explorer
Turn the Other Cheek Turn the Other Cheek Traverse 250 carriages in a single life without dealing any damage. Keep the peace a little longer. Goal Pacifist
Pure of Heart Pure of Heart Traverse 1,000 carriages in a single life without dealing any damage. How far can you go without lifting a finger? Challenge Turn the Other Cheek
The Far Start The Far Start Carry your starting book beyond 150 carriages. How far can you take that first book? Challenge Dungeon Train Explorer
The Long Way Back The Long Way Back Traverse 100 carriages forward and 100 carriages back in a single life. Sunken Cost Fallacy on going back? Goal Dungeon Train Explorer
Everything Burrito Everything Burrito Earn every other Dungeon Train advancement. The whole train, all wrapped up. The final advancement. Challenge Dungeon Train
Far Afield Far Afield Ride through 10 different biomes in a single life. the biomes are counting. Task Dungeon Train
Many Lands Many Lands Ride through 17 different biomes in a single life. the biomes are counting further. Goal Far Afield
World Without End World Without End Ride through 25 different biomes in a single life. thats a lot of biomes Challenge Many Lands
Terra Omnia Terra Omnia The whole world, and worlds beyond. Stand in every biome the Overworld, the Nether, and the End have to offer β€” all in a single life. There's still ground you haven't stood on. Challenge World Without End
Others? Others? Come within 4 blocks of another on the train. Am I alone? Goal Dungeon Train
Come Along With Me Come Along With Me Join a multiplayer world, or have someone join yours. The game is better with friends Goal Others?
Hello, Old Soul Hello, Old Soul Come within two blocks of your own echo β€” a passenger reincarnated from one of your past lives. You'll know it when you see it. Task Others?
A Stranger's Ghost A Stranger's Ghost Come within two blocks of someone else's echo β€” a passenger reincarnated from another player's past life. Not every echo out here is yours. Task Hello, Old Soul
Befriending the Dead Befriending the Dead Befriend someone else's echo. Trade gifts both ways with a stranger's past life. Be kind to a stranger you'll never meet. Goal A Stranger's Ghost
Erased Erased Shove someone else's echo off the train and into the void. Off the train, into the nothing β€” they won't climb back. Goal A Stranger's Ghost
Not Your Ghost to Keep Not Your Ghost to Keep Kill someone else's echo. Someone, somewhere, will feel that. Goal A Stranger's Ghost
Assassination Attempt Assassination Attempt Kill a Death Note echo before it kills you. Someone wanted you gone. Goal Not Your Ghost to Keep
Lay to Rest Lay to Rest Kill your own echo. Some pasts are better left behind. To become new, you must kill the old. Goal Hello, Old Soul
Old Friends, New Life Old Friends, New Life Befriend your own echo. Make peace with who you were. Be kind to yourself. Goal Hello, Old Soul
Repressed Memory Repressed Memory Shove your own echo off the train and into the void. Meet your old self out over the abyss, and let go. Goal Hello, Old Soul
Reboarder Reboarder Help a passenger off the moving train. If they come back, they won't talk about it. Most are lying. Not everyone needs to be onboard. Goal Others?
Murderous Intent Murderous Intent Kill a fellow passenger β€” a PlayerMob, or another player. Nobody saw anything. Do you have it in you? Goal Reboarder
Simon's Desperation Simon's Desperation Give an item to a passenger and get nothing in return. Perhaps you'll get better luck next time. Give a little. Task Others?
Come On, Grab Your Friends Come On, Grab Your Friends Trade gifts both ways with the same passenger. You give, they give. On this train, that's everything. Get a little. Goal Simon's Desperation
That's What Friends Are For That's What Friends Are For Kill a monster that was attacking a passenger. Dying together sucks a little less than dying alone. Lend a helping hand Goal Come On, Grab Your Friends
So Many Potential Friends! So Many Potential Friends! Encounter 1,000 players across all your runs. You have many lifetimes to meet a lot of people. Challenge That's What Friends Are For
The Denamed The Denamed Give a passenger a name. Most of them have forgotten their own. Take it from them. Task Others?
The Enchiridion The Enchiridion So you have read a book. There are more… Can you read? Task Dungeon Train
Collecting Stories Collecting Stories Read a single story from start to finish for the first time. Where do the lecterns go? Task The Enchiridion
A Library In Motion A Library In Motion Read every letter of every narrative story carried by the train. How many stories are there? Challenge Collecting Stories
Every Reality, Every Word Every Reality, Every Word Read every variant of every letter of every narrative story. The stories change, re-reading is key. Challenge A Library In Motion
Don't know, Don't wana know Don't know, Don't wana know Burn 10 books without reading them They burnt regardless of how read they are Task The Enchiridion
Nothing But Books Nothing But Books Fill your entire inventory with story books. Empty books don't count. A whole inventory of stories. Why need anything else? Challenge The Enchiridion
Taking Notes Taking Notes Open ten random books. Re-reads count. Its in the books Task The Enchiridion
A Stranger's Story A Stranger's Story Open a chest book credited to a real passenger from another train. Someone out there wrote this for you. Not every book in these chests was written by the game. Task Taking Notes
Ghostwriter Ghostwriter Sign a book and send it into the void β€” a stranger, somewhere, will read your words. Try writing something down. Task A Stranger's Story
A Death Note A Death Note Sign a Death Note. When you fall, your echo will hunt the name you wrote. Some books only work once you're gone. Task Ghostwriter
Self-Fulfilling Prophecy Self-Fulfilling Prophecy Write your own name in a Death Note. Some names come full circle. Task A Death Note
I Must Know I Must Know Read at least one variant of every Faulthurst volume. Then keep reading. Goal Taking Notes
Faulthurst's Favourite Faulthurst's Favourite Read every variant of every Faulthurst book. The margins are full now. ??? Challenge I Must Know
Know The Rules Know The Rules Read every Quiet Rule of the train. You know them all now β€” keeping them is another matter. then read them Goal Taking Notes
Respect The Rules Respect The Rules Open 100 distinct chests in a single life. Open the same chest twice and the streak resets. That one about opening chests x 100 Challenge Know The Rules
The Same But Different The Same But Different Open ten starting books. They're not quite the same. They're not quite different. Is that what the starting book said last time? Task The Enchiridion
Welcome Back Welcome Back Read at least one variant of every starting book the train hands out. How many ways to start this are there? Goal The Same But Different
The Great Beyond The Great Beyond Complete the feedback survey after death. The afterlife is just a feedback survey. Goal Dungeon Train
Summon The Creator Summon The Creator Tag @dev or @brennanhatton in chat He is in chat if you call Goal The Great Beyond
The Creator Answers The Creator Answers Receive a message from the Developer He doesn't always reply... but sometimes he does Goal Summon The Creator
Track Record Track Record Drop off the train and stand on the rails it rides. The ground moves differently down here. Its not on the train. Task Dungeon Train
Off the Rails Off the Rails Leave the train and the tracks behind. The world is bigger than the corridor β€” and quieter. Escaping the train? Task Track Record
Beneath the Candy Kingdom Beneath the Candy Kingdom Use a tunnel stairwell β€” down from the surface, or up from the dark. Take the stairs deep underground. Task Off the Rails
Taking the Stairs Taking the Stairs Climb the stairs beside a bridge pillar. Somebody built these. Nobody knows who. Thats one way to get back board. Task Off the Rails
Back on Track Back on Track Get off the train, then climb back aboard. From the rails or from a running leap β€” the train doesn't ask. Why'd you leave in the first place? Task Taking the Stairs

Editor tab

Editor advancement tree

Icon Advancement Description Hint Type Unlocks from
Editor Editor Build the train you ride. β€” Task β€” (root)
Architect Architect Create a new carriage from scratch in the editor. β€” Task Editor
Block Variants Block Variants Apply a block-variant entry to a cell. β€” Task Editor
Locked In Locked In Use a block-variant lock id to keep cells in sync. β€” Task Block Variants
Loot Master Loot Master Define a new contents pool. β€” Task Editor
More Variety More Variety Add a new entry to a contents pool. β€” Task Loot Master
Modular Thinking Modular Thinking Save a new carriage part template. β€” Task Editor
Packaged Packaged Save your authored content as a package. β€” Task Editor
Ship It Ship It Export a package zip ready to share. β€” Goal Packaged
Pillar of the Community Pillar of the Community Save a new pillar section variant. β€” Task Editor
Step By Step Step By Step Save a new stair (pillar adjunct) variant. β€” Task Pillar of the Community
Roll With It Roll With It Edit a container's contents pool. β€” Task Editor
Curator Curator Save a contents template to disk. β€” Task Roll With It
Trackmaker Trackmaker Save a new track tile variant. β€” Task Editor
Tunnel Vision Tunnel Vision Save a new tunnel variant. β€” Task Editor

The Long Way Back (v0.245.0, PR #306)

New side-branch advancement off Dungeon Train Explorer. Awarded when the player has traversed β‰₯100 carriages forward AND β‰₯100 carriages backward in a single life β€” proves you actually paced the train rather than just being carried by it. Frame: goal, icon: minecraft:recovery_compass.

Shipped alongside a semantic change to the underlying counter:

  • Before v0.245.0: the per-life carriage counter recorded net forward movement only β€” walking backward was silently dropped. A player who walked 5 forward then 5 back was credited 0.
  • From v0.245.0: the counter records total absolute carriages traversed β€” both directions count. A player who walks 5 forward then 5 back is credited 10. Explorer (100), Long-Haul (1,000), and Nearing Infinity (10,000) all fire on absolute traversal.

Existing achievement holders are unaffected (they already met the threshold). Mid-run players in older saves load cleanly: their stored "forward" value becomes the new "absolute" value with backward subtotal 0 β€” consistent with how they actually moved.

Dimension advancements + folder-driven sets (v0.268.0, PR #331)

Four new Dungeon Train–tab advancements tied to the Nether and the End, plus an engine change that makes future book-set advancements zero-code.

The chain. The five link into one lineage (display-only β€” the parent link organises the tree, it does not gate earning):

Dungeon Train (root) β†’ Inter-Dimensional Passenger β†’ Void Passenger β†’ Nether Return Again β†’ End of the Line β†’ Inter-Reality Passenger

  • Inter-Dimensional Passenger / Void Passenger reuse the existing editor_action boarded trigger with a vanilla player β†’ location.dimension predicate (minecraft:the_nether / minecraft:the_end) β€” no Java. Earnable once the train is boarded in a run that started in that dimension (see World Type, Login spawn).
  • Nether Return Again / End of the Line complete the per-dimension starting-book sets (nether_starting_books / end_starting_books). Completion is delivery-tracked β€” credited once every welcome-book variant for that dimension has been handed to you across that many dimension-start runs (per-installation cycling). These two dimension sets are the delivery-tracked half of Inter-Reality Passenger; its lifecycle half is read-tracked (see below).

Folder = set (zero engine code). Set membership derives entirely from the starting-book folders. StartingBookContext.achievementSetId() is the single place a folder is declared an achievement set (nether β†’ nether_starting_books, end β†’ end_starting_books); AchievementEvents.notifyStoryProgress loops the folders and fires whatever it declares. Adding books, variants, or a whole new dimension folder auto-extends the relevant sets with no achievement-code change.

Inter-Reality Passenger β†’ grand slam. allStartingBooksSeen was reworked to a per-context check (dimension folders β†’ per-install delivery store; lifecycle folders β†’ world snapshot), so it now requires the Nether/End books too and auto-includes any future folder. Players who already earned it keep it (vanilla never revokes).

v0.269.0 β€” lifecycle half made earnable (PR #332). The world-snapshot store for the lifecycle folders (DEFAULT / new_world / joined_world) was never written on a normal install β€” only the (empty) RESPAWN cycling path could write it β€” so the non-dimension half could never complete. It is now marked on read: every delivered starting book carries a (basename, variant) identity on its StartingBookTag, and reading it (the close-and-burn flow) records the variant via NarrativeProgressData.markStartingBookVariantSeen, matching the "Read every starting book" wording. Single-player caveat: joined_world is only delivered in multiplayer, so the grand slam is completable in multiplayer (or via creative / the give command), not in pure single-player.

Superseded by the v0.400.1 rework below β€” the paragraph above describes the original book-set design; "Nether Return Again" and "End of the Line" no longer work this way.

Advancement rework: Nether Return Again, End of the Line, Inter-Reality Passenger (v0.400.1)

This reshuffles the tail of the dimension chain described above. The chain is now:

... Hell on Rails β†’ Voided Warranty β†’ End of the Line β†’ Re-Over-World β†’ Nether Return Again (zone-traversal chain, Dungeon Train tab), with Inter-Reality Passenger moved to hang off Welcome Back instead.

  • "End of the Line" is now the title of the advancement for reaching the floating End islands (previously "Tastes like Cheese?", id reached_end_islands) β€” a gameplay_action zone-crossing trigger, not a book-set. The old book-set advancement that used to hold this title (read_all_end_starting_books, "read every End starting book") has been removed.
  • "Nether Return Again" (id read_all_nether_starting_books, kept unchanged so existing holders don't lose it) no longer tracks Nether book-reading. It now fires as a gameplay_action once the player re-enters the End islands zone after already having earned Re-Over-World β€” i.e. complete one full loop (void β†’ End islands β†’ back to solid Overworld ground), then ride back into the End a second time. See ZoneProgressEvents.java.
  • "Re-Over-World" and "Nether Return Again" are both "hidden": true β€” per vanilla advancement behaviour, a hidden advancement doesn't show as a locked node in the tree at all; it only appears once earned. So neither shows up until the player actually reaches it.
  • "Inter-Reality Passenger" has been pulled out of the loaded advancement set entirely β€” its file was renamed inter_reality_passenger.json.disabled (JSON has no comment syntax, so this is the closest equivalent to "commenting it out"), so it doesn't show anywhere in the tree or count toward the advancement total. The file (still parented under Welcome Back, criteria swapped to vanilla minecraft:impossible) stays in the repo β€” restore it by dropping the .disabled suffix once new Overworld-reality-specific starting books exist to gate it on (a future StartingBookContext, mirroring NETHER/END).
  • The underlying nether_starting_books / end_starting_books / all_starting_books story-set-completed triggers in AchievementEvents.java / StartingBookContext.java still fire (harmless, no advancement currently listens to them) β€” left in place rather than ripped out, since the book-tracking machinery is expected to be reused when the Overworld-reality content lands.

Welcome Back fix: dropped the unreachable Nether/End requirement (v0.448.0, PR #738)

PR #639 restricted new-world creation to the Overworld-start preset β€” the Nether/End Dungeon Train world types were dropped from the #minecraft:normal world-preset tag, so the Create World screen's World Type cycle can no longer select them (see World Type). That silently broke Welcome Back (starting_books_all_titles): its "read at least one variant of every starting book" check spanned every starting-book folder, including the nether/end dimension-routed pools β€” and a run can no longer be dimension-routed into either, so those two titles could never be delivered to a fresh player again. Welcome Back β€” and transitively Everything Burrito, which requires every other advancement β€” was permanently unobtainable for anyone creating a new world after #639.

Fix: AchievementEvents.allStartingBookTitlesSeen() now sources its basename list via a new StartingBookRegistry.basenames(Predicate<StartingBookContext>) overload, filtered to ctx.achievementSetId().isEmpty() β€” i.e. only the lifecycle folders (default/new_world/joined_world/respawn). This mirrors the dimension-routed-vs-lifecycle split allStartingBooksSeen (the Inter-Reality Passenger grand-slam check, itself disabled β€” see above) already used. Players who already earned Welcome Back keep it; legacy Nether/End-start saves are untouched. PR #639's Overworld-only restriction itself was left as-is β€” out of scope for this fix.

Multiplayer advancement (v0.271.0, PR #335)

Come Along With Me β€” the first advancement that recognises playing with others. Granted on PlayerLoggedInEvent when the joined session is multiplayer:

  • Dedicated server (or any host the player doesn't own) β†’ multiplayer on its own (MinecraftServer.isSingleplayerOwner is always false on a dedicated server).
  • Integrated LAN host β†’ multiplayer once a second player connects.

When detected, every currently-online player is awarded β€” so the LAN host earns "someone joined yours" at the same moment the guest earns "joined a multiplayer world". Opening a single-player world to LAN does not re-login the host, so awarding all online players when the guest connects is what retroactively grants the host. Idempotent: vanilla advancement dedupe makes the repeated firing a no-op once earned.

Backed by a new marker trigger dungeontrain:multiplayer_join (MultiplayerJoinTrigger.java) β€” carries only the optional player predicate, no extra conditions. Persists cross-world via the existing sidecar like every other dungeontrain:* advancement. The title is an Adventure Time "Dungeon Train" (S5E36) reference.

So Many Potential Friends! (v0.288.0, PR #356)

Encounter 1,000 distinct players (PlayerMobs) across your whole Dungeon Train career. An "encounter" is recorded the first time a given PlayerMob comes within ~16 blocks during a run β€” a 10-tick proximity scan in RunStatsEvents feeds both the death-screen MET cell (per-run) and an all-time cumulative counter. Frame: challenge, icon: minecraft:player_head. The title is an Adventure Time reference, on theme with its parent Come On, Grab Your Friends.

It reuses the same all-time pattern as the train-time milestones: a new tier trigger EncounteredPlayersTrigger (dungeontrain:encountered_players, matches cumulative count β‰₯ threshold) backed by a fourth cumulative field playersEncountered on GlobalPlayerStats. Each new per-run encounter bumps the counter once and fires the trigger via AchievementEvents.notifyEncounter.

Tree placement. It extends the social branch as a child of That's What Friends Are For:

Simon's Desperation β†’ Come On, Grab Your Friends β†’ That's What Friends Are For β†’ So Many Potential Friends!

The same PR also re-parents Murderous Intent under Reboarder (helped a passenger off the train β†’ killed one). The per-run encounter / kill / befriend / damage figures it shares are shown on the death-screen combat strip.

Advancements keybind hint (v0.301.0, PR #381)

A first-run nudge that points new players at this screen. The first time you earn a Dungeon Train gameplay advancement before you have ever opened the advancements screen, a short, train-flavoured line appears in chat about a second later β€” e.g. "All aboard! Press L to see all your Dungeon Train advancements." It reappears on each earn until you open the advancements screen once, then never shows again.

  • Live keybind, never hardcoded β€” the key shown is read from your controls (options.keyAdvancements), so it reflects your actual binding and updates if you rebind "Advancements".
  • Random wording β€” one of twelve conductor-style lines per showing.
  • Grey, no sender β€” a plain grey system line (no [Dungeon Train] prefix), shown one second after the advancement so it lands just below the vanilla "… has made the advancement" message.
  • Quietly self-terminating β€” opening the advancements screen flips a per-install client flag (advancementsHint.openedBefore in dungeontrain-client.toml); the hint never returns once you've seen the tab.

How it's wired. The earn is detected server-side in AchievementEvents (any dungeontrain: advancement not under editor/), which sends a marker AdvancementsHintPacket to that player. The "have you opened it" flag and the keybind are client-only, so the client decides whether to show the hint and renders it with the live key (~1 s later, via a client-tick countdown). The login-replay that re-fires every stored advancement is guarded, so returning players are not spammed; creative-mode earns produce no hint (gameplay advancements are gated there already).

Train-traversal advancements (v0.301.0, PR #382)

Five new Dungeon Train–tab advancements that recognise where you are relative to the train as it rides the world β€” the rails beneath it, the world beside it, and the staircases and tunnel stairwells along the route.

Track Record β†’ Off the Rails β†’ { Taking the Stairs β†’ Back on Track, Beneath the Candy Kingdom }

  • Track Record β€” stand on the rail bed below the train (off the carriages, on the tracks it rides).
  • Off the Rails β€” off every carriage and off the corridor: stepped off the side, out into the world.
  • Back on Track β€” board a carriage again after getting off, whether you dropped to the tracks or leapt off entirely.
  • Taking the Stairs β€” climb a bridge-pillar side staircase.
  • Beneath the Candy Kingdom β€” use a tunnel stairwell (the surface entrance pavilion or its descending shaft). An Adventure Time reference β€” the Candy Kingdom tunnels of S5's "Beneath the Candy Kingdom".

How it's wired. All five fire one parameterised marker trigger dungeontrain:gameplay_action (GameplayActionTrigger, the gameplay analogue of editor_action); like every dungeon_train/* advancement they are gated to survival/adventure by the game-mode mixin.

  • Landing / leaving / returning β€” TrackPresenceEvents scans every 10 ticks. The corridor is static world geometry (the train only moves +X), read from the live train's TrackGeometry; "on a carriage" uses the carriage worldAABB with strict horizontal bounds (so standing or towering up beside the train does not count) plus a short off-grace, so coupling-seam crossings and roof-running don't misfire.
  • Stairs β€” TrackGenerator records each placed pillar-stairs and tunnel entrance+shaft footprint into a per-dimension StairsLocationData SavedData at worldgen; StairsUsageEvents fires the marker when a player stands inside one. Indexing is metadata only β€” no placed-block changes β€” and covers chunks generated from this version onward.

The same PR also reworded Simon's Desperation ("…Perhaps you'll get better luck next time.").

Over the Top (v0.321.1, PR #459)

New stepping-stone advancement that sits between Dungeon Train Explorer and Skip the Grind:

Dungeon Train Explorer (100 carts) β†’ Over the Top β†’ Skip the Grind

  • Over the Top β€” traverse the roof of one enclosed carriage group in a single continuous run, landing on the coupling pad on the far side, without ever dropping inside. Frame: task, icon: minecraft:stone_brick_stairs.

What counts. A run is the player's uninterrupted time above roof height β€” scanning every 10 ticks. "Above roof height" includes: standing on the glass roof, standing on the edge-stair blocks at the roof ends, and towering up immediately adjacent to the carriage sides. Only dropping inside an enclosed carriage (below the interior ceiling) or falling off the train entirely ends the streak.

How it's wired. RoofRunEvents classifies each player as ON_ROOF, ON_TOP_OTHER (coupling pad / airborne over the top), BELOW_ROOF, or OFF. When the player transitions from ON_ROOF to ON_TOP_OTHER (reaching the pad after a roof run), the trigger fires with groups = 1; the existing dungeontrain:roof_run_groups trigger (threshold β‰₯ 1) awards this advancement. Skip the Grind uses threshold β‰₯ 3 and fires when the player has covered three distinct group roofs. Reuses HORIZONTAL_PADDING = 2.0 and ENCLOSED_END_PADDING = 1.0 constants for edge-case detection.

Summon The Creator β†’ The Creator Answers (v0.411.0 / v0.417.0)

A small hidden two-node chain off The Great Beyond, breaking the fourth wall a little:

The Great Beyond β†’ Summon The Creator β†’ The Creator Answers

  • Summon The Creator (v0.411.0, PR #678) β€” tag @dev or @brennanhatton in chat. Fires the dungeontrain:gameplay_action marker trigger (action id tagged_creator) from AchievementEvents.notifyTaggedCreator, called wherever chat-mention detection lives. Earns regardless of whether Brennan is actually online. Icon: minecraft:bell.
  • The Creator Answers (v0.417.0, PR #690) β€” receive an actual relayed Developer message in your in-game chat. Fires the same gameplay_action trigger (action id creator_answered) from the new AchievementEvents.notifyCreatorAnswered, called from both delivery sites in DevMessageConsent β€” immediate delivery when consent is already valid, and the held-message flush once the player types @Dev to accept the consent prompt. Icon: minecraft:written_book.

Both are "hidden": true, so β€” like the rest of this tree β€” neither shows as a locked node until its parent is earned.

Advancement tabs (v0.1195.0, PR #1883)

Dungeon Train's advancements are spread across themed tabs. Like the rest of Dungeon Train's advancements, most entries stay hidden until you get close, so this section describes how the tabs behave rather than what is on them.

The tabs

  • The player tabs are Dungeon Train, Train Explorer, Challenges, Others, The Enchiridion and The Darkroom, plus one more you find late in the game.
  • Editor holds the content-authoring advancements.
  • They always appear in that order, in vanilla and in Better Advancements. This is done by mixin/AdvancementTreeOrderMixin and advancement/TabOrder.

Unlocking and copies

  • A tab opens when you earn its gateway advancement.
  • Some advancements show up in two tabs. The second one is a mirror ("copy") that is granted along with the original.

Earned progress

  • Every advancement kept its id when the tabs were split, so progress you already earned carries over.
  • ids_before_tab_split.txt in the tests pins this.

Finishing a tab

  • Every player tab has a final advancement. It is earned once everything else on that tab is earned.
  • The Dungeon Train tab's capstone counts the Dungeon Train tab and the other tabs' final advancements.

Click to pin

  • Clicking a final advancement pins it. Every advancement it still needs is ringed in aqua, and so is each tab that holds one.
  • The pin stays while you change tabs. Clicking any other advancement clears it.

Gold tabs

  • A finished tab is drawn gold.
  • While that tab is selected, the window's border is gold too.

Other mods' tabs

  • Minecraft's and other mods' advancement tabs are hidden by default. Turn them back on with Options β†’ Dungeon Train… β†’ General β†’ Other Advancement Tabs.
  • Their advancements are still earned either way.

Layout data

  • All of this comes from data/dungeontrain/advancement_tabs.json: tab names and order, gateways, copies, each tab's final advancement, and which advancements count toward the capstone and the reset.
  • The in-repo editor at scripts/advancements/editor edits that file and the advancement JSON. Its README covers how to use it.

Code

  • advancement/TabGateways
  • TabCompleteAdvancements
  • CompletionistAdvancement
  • client/CapstoneNeeds (the pin)
  • client/TabCompletion (gold)
  • the client/Advancement*Mixin tab and window hooks, plus their betteradvancements/ twins

Cross-world persistence

GlobalAchievementStore mirrors earned advancements to a per-UUID JSON sidecar at <minecraft>/config/dungeontrain-achievements/<uuid>.json, outside any individual world save. On PlayerLoggedInEvent the sidecar is replayed onto the world's PlayerAdvancements β€” silent (vanilla's "first packet" reset suppresses toast spam, and the gamerule announceAdvancements is temporarily disabled during replay). On PlayerLoggedOutEvent the sidecar reconciles against the world's state, so /advancement revoke sticks across worlds.

Every advancement, not just Dungeon Train's (v0.310.0, PR #413)

Originally only dungeontrain:* advancements were stored. As of v0.310.0 the store keeps every advancement that appears in the advancements screen β€” vanilla Minecraft (Story / Nether / The End / Adventure / Husbandry), Dungeon Train, and any other mod β€” so your full vanilla and modded progress now follows you into every new world, exactly as the Dungeon Train tabs always have.

  • What carries: any advancement with a display. One policy β€” shouldPersist(holder) = holder.value().display().isPresent() β€” is shared by the earn-append, login back-fill, and logout revoke-reconcile sites in AchievementEvents.
  • What doesn't: the hidden, display-less recipes/* tree (the hundreds of recipe-unlock advancements). Recipe-book unlocks stay per-world β€” a fresh world still discovers recipes through normal play. This is the same display-less partition the Everything Burrito capstone uses to skip recipe advancements.
  • Existing saves are back-filled: on login the world's already-earned advancements are swept into the store before replay, so progress earned before this update β€” which never re-fires AdvancementEarnEvent β€” still carries forward to your next world.
  • No migration: the sidecar JSON schema is unchanged; existing files just start accumulating vanilla/mod ids.

Free Play never writes to the profile (v0.831.0, PR #1350)

The sidecar is a record of what you earned, so every write to it goes through RunIntegrity.persistsAdvancement: a Free Play run (cheats available, creative, a cheat mod, retuned config, keepInventory, …) still earns advancements live β€” toast, chat line, the lot β€” but nothing from that run is banked to the cross-world profile. dungeontrain:editor/* authoring advancements are the one exemption (authoring legitimately happens in creative).

That includes advancements Dungeon Train grants from code rather than from a criterion trigger. As of v0.831.0 the last remaining unconditional write β€” one code-granted advancement that had been banking regardless of Free Play β€” goes through the same gate, and any advancement whose prerequisite is another advancement now verifies that prerequisite against the banked profile, not just the live tree: /advancement grant can put an advancement in front of you, but it can't make the profile believe you earned it. Two consequences worth knowing:

  • Command-granted advancements are never recorded, even if the tree's ordering is satisfied live.
  • Operators can't bank anything: with cheats available the run is Free Play by definition (OperatorIntegrity).

The relay's Advancements page (dt.brennan.games) counts Discord announcement posts, not profile records, so a command grant in Free Play still shows there β€” that page is a what was announced feed, not a what was earned ledger.

Time-on-train (Settling In / Enjoying the Ride / I Live Here Now) and the player-encounter total (So Many Potential Friends!) are similarly cross-world via GlobalPlayerStats, flushed on logout + server stop.

Requirement values: one home, relay-editable (v0.848.0, PR #1366)

Every milestone advancement β€” carriages traversed, metres ridden, biomes visited, time aboard, books read, chests opened, players encountered β€” keeps its number in one place: criteria.<name>.conditions.<field> in its datapack JSON, where <field> is one of threshold, thresholdReads, thresholdMeters, thresholdTicks (RequirementField). The advancements that Java code grants directly (the Pacifist tiers, The Far Start, Not My Chest / Look Don't Touch tiers, Contained Loop) carry theirs the same way β€” a typed dungeontrain:code_granted criterion, or an optional threshold on gameplay_action β€” and read it back through AdvancementRequirements instead of a constant.

The description shows the live number. Lang lines say Traverse %s carriages in a single life.; at datapack load ServerAdvancementManagerRequirementsMixin turns each description into a translatable whose argument is the requirement β€” counts and metres as grouped digits (1,000), ticks as dungeontrain.requirement.hours/days.{single,some,plural} (2 hours, 1 day, 3 days, localised per client). The generated advancement-tree.js on the relay and the wiki gallery substitute the same value.

Editable on the relay. The explorer's Advancements page (dt.brennan.games/#/advancements) has a Requirement editor at the top of each milestone's drilldown row (tick fields edit in hours). A saved value:

  • is served anonymously at GET /<CAP>/advancement-requirements (edge-cached 5 min) and fetched once per launch by every server and client (AdvancementRequirementOverrides), cached under <gameDir>/dungeontrain/advancement-requirements.json;
  • is applied at the next datapack load (world open, /reload) β€” the log line Advancement requirements: 35 loaded, N overridden by relay: … is the proof. A fetch that lands after the world loaded, with a different set, triggers one automatic data-pack reload; a failed fetch is retried at server start;
  • is pulled back into the datapack JSON by scripts/advancements/import-requirement-edits.py, the second step of the Monday import-english-edits.yml PR (defer-not-clobber when git has moved on, same policy as the text importer).

Revert to jar value deletes the row; players fall back to the jar at their next launch. Values affect what the game checks and what the tooltip says, in every language.

Enabled / Burrito switches: relay-settable per advancement (v0.885.0, PR #1416, dp-relay #576)

The same page has two checkbox columns per Dungeon Train advancement, Enabled and Burrito (also at the top of each row's drilldown). Ticked is the jar's baseline; the relay stores only the exceptions (PUT|DELETE /<ADMIN_CAP>/advancement-flags/<id>/<flag>), which ride the same public advancement-requirements payload as flags β€” one fetch per launch, same disk cache.

  • Enabled off (disabled): AdvancementDisabler drops the advancement from the datapack map inside the same mixin, before vanilla parses it, and re-parents its children to the nearest surviving ancestor (vanilla would otherwise orphan them). Absent from the registry means not earnable, not on the L screen, and not a prerequisite of the capstone. Log line per datapack load: Advancement flags: N disabled by relay: […] (re-parented childβ†’parent, …). The root cannot be disabled.
  • Burrito off (notRequired): the advancement stays in the game but the capstone's registry walk skips it β€” read live on every check (AdvancementRequirementOverrides.notRequired()), so it needs a fetch but no datapack reload. The capstone itself and its successor can't carry this flag (the code skips them anyway).

A machine picks flags up once per launch; a player made eligible for the capstone only by a new notRequired receives it at their next login or next earn, like any backfill. There is nothing to import back into the repo β€” the jar has no per-advancement flag, the baseline is always "enabled, required".

Life-disqualified advancements: fade, red hint line, tracking toasts (v0.895.0, PR #1429)

Some advancements can only be earned within one life, and a single action rules them out until the next one (the Pacifist chain β€” any damage dealt; Contained Loop β€” an ender chest opened; The Far Start β€” the starting book burned). The advancements screen now shows that state, on both the vanilla screen and Better Advancements.

  • Ruled out this life: the tile fades to 50 % (frame and icon), the connector into it stops at the frame edge, and the hover hint gains a red "Lost for this life." line in place of the usual footer. Hovering shows the tile at full opacity β€” the red line carries the message. Everything clears on respawn.
  • Tracking: click a trackable tile to track it (footer "Click to track" / "Tracking β€” click to stop."). A tracked tile gets a yellow halo β€” its own task/goal/challenge frame, 2 px larger, tinted yellow, drawn behind it (faded with the tile when lost). Closing the screen after a change prints Tracking A, B β€” you will be told if it gets ruled out. (already-lost tiles can still be toggled but are left out of that line).
  • Toasts: a tracked advancement that is lost raises a red-bordered "Lost this life" toast; a tracked streak advancement whose count restarts (Not My Chest / Still Not My Chest β€” chest or barrel opened; Look Don't Touch / Museum Rules β€” any block broken; Chests Γ—100 β€” the same chest opened twice) raises an amber "Streak reset" toast, only when there was a streak to lose. Streak tiles never fade β€” they are not lost for the life. Roof Run / Roof Runner are deliberately not trackable (a roof run is seconds long; a toast per drop would be spam).

Server side, nothing new is stored for the "lost" set: LifeDisqualification re-derives it from persisted per-life state (PlayerRunState.damageDealt(), OPENED_ENDER_CHEST_THIS_LIFE, the new STARTING_BOOK_BURNED_THIS_LIFE attachment), so a relog agrees with the pre-relog screen. Earned advancements are never reported. It pushes LifeDisqualifiedPacket (full set + newly-lost + streak-reset lists) on login, respawn and every flip; LifeDisqualificationClient mirrors it and raises DisqualifiedAdvancementToast β€” its own Toast class, so Advancement Plaques (which intercepts vanilla AdvancementToast) leaves it alone. The tracked set is a per-install choice in <gameDir>/dungeontrain/user/tracked-advancements.json (TrackedAdvancements).

Presentation lives in one place for both screens β€” AdvancementTileDecor β€” called from AdvancementWidgetHideDescMixin and its BA twin. Three things it learned the hard way: vanilla's blitSprite does not enable GL blending (so a half-alpha shader colour is ignored in the tree until you enableBlend() yourself); the hover tooltip redraws the frame and icon over the tree, so any tree-only treatment vanishes on hover; and BA calls RenderUtil.setColor(rgb) immediately before its frame blit and icon, which has to be wrapped to keep the fade. The incoming connector is clipped with a scissor in drawConnectivity (screen-space in 1.21.1 β€” transform the edge through the pose first), popped before the children draw their own lines. Clicks are resolved without touching either tab's private geometry: drawHover records the hovered id (HoveredAdvancement) and the screens' mouseClicked mixins read it back.

All at Once / The Challenge (v0.1270.0, PR #1982)

Two Challenges-tab advancements for keeping every "do without" challenge at once, for a whole life: All at Once (100 carriages, totem, goal) and its child The Challenge (1,000, bedrock, challenge). The rules: no apple or edible backpack in the inventory, eat nothing but melon, wear no armor (an elytra is fine), break no block (vases count), open or break no chest or barrel, deal no damage, open no ender chest. Fail one, fail all. Respect The Rules, Cover me in ...leather? and Challenge Complete are not part of it.

The individual challenges are unchanged. Breaking a block still only restarts the Look, Don't Touch streak, but it rules these two out for the rest of the life. Every rule-break site calls AllAtOnceAdvancement.markLost, which sets one per-life attachment (ALL_AT_ONCE_LOST_THIS_LIFE, serialized, cleared on respawn) and greys both tiers out through LifeDisqualification. The grant also re-reads the older per-life flags and the damage tally. A save whose life was already underway when this shipped is closed until respawn (LifeChallengeAdvancements.closeUntrackedChallenges). Thresholds sit in each tier's code_granted criterion, so the editor and relay can rebalance them. Both count toward Challenge Complete and the Everything Burrito.

Selecting either one highlights the rules it combines, in game and in the advancement editor. The list is the needs table in advancement_tabs.json (advancement β†’ the challenges it combines). In game, CapstoneNeeds pins it like a capstone and haloes each rule in aqua. Earned rules are haloed too, since every rule still applies. The editor's neededBy outlines the same set.

Milestone announcements in #passenger-log (v0.1115.0, PR #1778)

Three advancements are rare enough that the whole community hears about them: Everything Burrito (dungeon_train/completionist), It's Not That Simple (dungeon_train/start_again) and The Long Run (dungeon_train/the_long_run, 250,000 m in one life). Earning one on a clean run posts a top-level report to the public #passenger-log feed (the same channel as the death manifest and echo stories) that @-mentions Brennan.

  • What it says: "πŸ† player earned advancement", then the advancement's hint β€” never its description, which other players are meant to discover β€” and the carriage/difficulty line.
  • The photo: the ride gallery's framed third-person capture around the player (RideSnapshotCapture.requestEchoCapture on the player themself), with the vanilla "Challenge Complete!" toast painted on afterwards offscreen (client/snapshot/ToastOverlayPainter), since the snapshot pass draws the level only. The server holds the post up to 10 s for it (discord/MilestonePostBuffer, like DeathReportBuffer) and posts text-only otherwise. Two packets carry the round trip: CaptureAdvancementPacket (Sβ†’C) / AdvancementPhotoPacket (Cβ†’S, ≀1 MB). PROTOCOL_VERSION 111.
  • When it stays quiet: Free Play (any RunIntegrity.isCheated cause β€” creative, cheats, a console grant, an unapproved mod) and cross-world replays on login (the hook sits in AchievementEvents.onAdvancementEarn's !replaying block). Dev builds route to the dev channel, as the echo stories do.
  • The ping uses Discord Presence 0.64.0's postReportTopLevel(…, pingUserIds) β€” a trusted allowed_mentions.users allow-list, so no player text can ever mention anyone. It reaches Discord while the relay's hook guard is in its default warn mode; in enforce mode Brennan's id must be in HOOK_PING_ALLOW_IDS.

Entry point: MilestoneAdvancementReporter.

First passenger ever (v0.1120.0, PR #1781)

The same announcement fires for any dungeontrain:* advancement (the Secrete Menu tab included, editor/* excluded) the first time anyone, ever earns it on a clean run: the player gets a gold chat line β€” "You're the first passenger ever to earn X. The whole train heard." β€” and #passenger-log gets the post under a "πŸ₯‡ player β€” first passenger ever to earn X" title. A milestone that is also a first posts once (the pending post is retitled).

  • The relay decides. FirstEverAdvancements asks POST /advancements/first-claim (AdvancementFirstClient); the relay records the first clean claimant atomically in adv_firsts (dp-relay advancement-firsts.js). All-time, never per era. Only a fresh record is a first β€” the holder re-earning after a Start Again wipe or in a new world is not celebrated again.
  • Pre-seeded. Every advancement anyone had earned before this shipped was recorded with its earliest earner (114 of them at launch, from the relay's retention-proof adv_alltime tally), so only never-earned advancements can be claimed; the relay's daily fold keeps the record book current for clients that never claim.
  • Fail-closed. Relay unreachable, slow, or unseeded β†’ nothing. Free Play β†’ nothing. Cross-world replays β†’ nothing. The dev cap answers without recording, so dev builds never take a real record.

How the carts counter works

BoardingProgressEvents scans every 10 ticks for boarded players, finds the leader's current carriage AABB, and computes delta = current - data.lastLeaderCarriage() β€” naturally signed. The delta is passed to AchievementEvents.notifyCartAdvance, which calls PlayerRunState.recordCartMovement(signedDelta):

  • cartsSinceDeath += |signedDelta| (absolute total)
  • if signedDelta < 0: cartsBackwardSinceDeath += |signedDelta|

The forward subtotal is derived as cartsSinceDeath βˆ’ cartsBackwardSinceDeath. Two custom triggers fire per advance: dungeontrain:carts_in_run (matches absolute total β‰₯ threshold) and dungeontrain:carts_both_directions (matches when both forward and backward subtotals β‰₯ threshold).

Implementation entry points

Related

  • Death screen recap β€” its combat strip shows the per-run encounter / kill / befriend / damage stats; So Many Potential Friends! counts the same encounters all-time.
  • Narrative lectern β€” drives Collecting Stories and A Library In Motion.
  • Random books β€” drives Taking Notes.
  • Starting books β€” drives Welcome Back; no longer drives Inter-Reality Passenger (disabled), Nether Return Again, or End of the Line (both now zone-traversal advancements β€” see the v0.400.1 rework above).
  • Carriage editor β€” most Editor-tab achievements trigger here.
⚠️ **GitHub.com Fallback** ⚠️