Feature Shared Carriages - bh679/dungeon-train-mc GitHub Wiki

Home > Features > Shared Carriages

Shared Carriages β€” community-built carriages that travel between worlds

A shared carriage is a carriage-template slot marked as a community-authored space. Instead of spawning a fixed built-in interior, a shared slot either leases an existing player-built carriage from the mod's relay pool (its blocks placed back verbatim, chests exactly as they were left) or drops a fresh blank shared template that only enters the pool once someone actually builds in it. A leased carriage is locked to one world until it's returned or the holder goes quiet for ~1 hour β€” so a build is only ever edited by one player at a time, then travels on to the next world.

See also: Build Profiles β€” how a player's own Train Builder creations reach this pool, and the profile they live in until they're submitted.

Status: in testing β€” off by default. The master switch sharedCarriagesEnabled (dungeontrain-server.toml) stays off until text-scan moderation and the report/takedown tools are proven. Distinct from Community Books: books are handed out as loot, shared carriages hand out an editable space that carries your changes back to the pool.

What a player experiences

  • Some carriages are subtly different β€” stepping onto one shows a faint dark-grey hint: a fresh carriage invites you to change it; a leased-from-the-pool one hints that someone has been here before you; and one you built yourself says so outright.
  • Your own builds come back to you. Roughly a third of shared slots hand back a room you made β€” travelled out to other worlds and returned, exactly as you last left it. A carriage you've already met never turns up twice in the same world.
  • Anything you build, place, or store inside a shared carriage is captured and travels with it β€” the next world to lease that carriage sees your changes, verbatim.
  • Furnishings travel too. Armor stands, item frames, paintings, end crystals and the animals or villagers standing in a shared carriage are saved with the build and placed back when another world receives it β€” and they ride the moving train with it, rather than being left on the tracks. Players are never captured, and neither are echoes (they carry a real person's name and skin) or bosses.
  • What you leave shapes your echo. Leaving items of worth in storage in someone else's drifting carriage makes your echo a little friendlier, scaled by the items' value (LootValue.stackScore, 0.05 kindness per point, capped at 1.0 per chest visit). Only deposits beyond the most you have ever left in that container count, so putting the same item back in earns nothing, and a carriage you authored doesn't count. Bringing TNT aboard does the opposite and makes the echo angrier. Both are silent and local, and nothing is sent to the relay (event/DriftGenerosity, event/DriftingCarriageSabotageEvents).
  • If you find a carriage with offensive or broken content, stand on it and run /reportcarriage [reason]; enough independent reports auto-withhold it from the pool pending review.

The travel loop

The full lifecycle is a straight NBT round-trip over the relay (blocks are an opaque gzip+base64 blob the relay never parses):

  1. Fresh β€” a shared slot with no pool match spawns the blank shared template. Nothing is uploaded until a real edit happens.
  2. Submit β€” the first genuine block change marks the carriage dirty; a ~30 s sweep captures its blocks and its entities and uploads them (/carriages/submit). The relay stores the build and immediately leases it back to the submitter so their in-progress work isn't handed to someone else mid-edit.
  3. Lease β€” another world's shared slot pulls a build from the pool (/carriages/lease), places it verbatim, and holds the lock alive with periodic heartbeats.
  4. Save β€” further edits by the lease holder are saved back (/carriages/save, which doubles as a heartbeat).
  5. Return β€” on world exit the held leases are returned (/carriages/return) so the carriage frees up immediately; the ~1 h silence TTL is the backstop for anything not returned.

Edits normally reach the relay as sub-second deltas of just the changed cells. Entities ride along with those: every delta carries the carriage's whole current entity list, since an entity moves or appears without touching a block. Placing an armor stand therefore changes nothing a delta would notice, so a 30 s sweep also compares a decor fingerprint (armor stands, item frames, paintings, end crystals β€” deliberately not mobs, or a pacing villager would re-upload the build forever) and uploads when a builder has changed it.

What fills a shared slot

Each shared slot rolls one of three outcomes, deterministically per carriage β€” walking back over the same stretch of track re-decides identically:

outcome default share what you get
Pool 65% (sharedCarriagePoolChance) a build by any player
Own 30% (sharedCarriageOwnChance) a build by someone currently in this world
Fresh the remainder, 5% a blank shared template

An own build is only reused when nobody else holds it, this world hasn't already placed it, and it belongs to the stage the slot sits in. If a bucket can't be served β€” nothing buffered, or the relay is unreachable β€” the slot falls through to the other kind of build, and then to a fresh template. The fresh share never falls through, which is what keeps new blank canvases entering the pool.

Free Play and normal play are separate pools

A build made in a Free Play session (creative or spectator mode, a known cheat mod, or deviated Adventure Item Stats settings) only ever travels to other Free Play worlds, and a normal-play build only to normal worlds. The two pools never serve each other, so a creative-mode room can't turn up as loot-bearing content in a survival run.

A world counts as Free Play if any player in it is running that way. Switching to creative mid-run hands back every carriage the world was holding: they stay standing and playable, they just stop uploading β€” edits made since the last save are not kept, which is the cost of never writing Free Play work into the normal pool.

How long a build survives in the pool

The pool is a ring: past a ceiling, the oldest available build is deleted outright, with its history, its stage assignments and its reports. A build under an active lease is never evicted out from under whoever is holding it, and Free Play builds count against the same ceiling as normal ones (they are separate pools to serve from, not separate ring bounds).

The ceiling is an operator setting, live-editable from the relay dashboard's Storage β†’ Data caps card ("Shared carriages"). It used to be a fixed 2,000, which at the pool's real arrival rate β€” around a thousand builds a day β€” meant a build survived roughly two days. It is now 100,000, and the dashboard shows the fill against it so the ceiling can be raised again before it starts biting.

Builds evicted before that change are recoverable only from a full-database backup snapshot, using the relay's tools/carriages-restore.js. That is a repair tool, not a safety net: it can only reach back as far as the snapshots go.

Moderation β€” "serve now"

Shared carriages are served immediately and moderated in three layers:

  • Automatic text scan. On submit/save the mod scrapes the authored text out of the carriage's block-entities and its entities β€” sign lines, lectern and chest books (title + pages), item custom names, and an entity's own name, worn/held gear and container contents (default item names are ignored) β€” and sends it with the upload. The relay runs that text through the same moderation reviewer the community books use; a real content signal stores the carriage flagged (withheld from the pool) instead of approved. A carriage with no player text spends no reviewer call. Editing offensive text away flips it back to approved on the next save; an admin reject is never overridden by a later edit.
  • In-game report. /reportcarriage [reason] (also /dungeontrain reportcarriage) files a report against the carriage underfoot; past a threshold it auto-flags out of the pool.
  • Admin review. A Carriages page in the relay's data explorer lists every stored carriage with its flag, report count, lease state, dimensions, owner, the scraped text, the moderation reason, and a full edit history (when + who), with Approve / Flag / Reject actions.

Dimensional carriages drift too

A sealed (Bedrock) dimensional carriage can take part in the same loop. When a pair rolls its room, and that room's walls are Bedrock, the pair has a 1 in 15 chance (sharedRoomChance) of being a drifting room; the other fourteen are the plain template and never touch the relay. A drifting room then rolls the same pool / yours / fresh split a shared slot does β€” and it will only ever be handed a copy of exactly the same room: the resolved sub-variant by name (an evilhouse never receives a house) at the same size, in the same stage. If nothing matching is buffered, the room is fresh: the template, registered so the first block a player places or breaks inside it uploads it as a portal_room build, with later edits streaming as deltas. Walking into one gives the usual gray line ("…drifting dimensional carriage…"), and a leased copy adds the credit and death lines a leased carriage does.

Only Bedrock rooms qualify because a sealed box is one thing a single blob describes; Endless, Void and Chunk Dimension rooms are never drifted. Authors can keep any room out of it with the room's Drift setting (a row on the plot panel, or /dt editor portals <room> drift off); it defaults to on. Two differences from a carriage worth knowing: edits made by redstone, pistons or explosions inside a room are not captured (only a player's place/break), and a room re-laid after the train drifts keeps its blocks but not its authored mobs. Relay side, the lease names the build kind and only ever serves play captures of rooms β€” a room submitted from the Train Builder stays in its author's profile.

Group carriages drift too (v0.1186.0)

A Group carriage (one carriage spanning the whole group, drawn by the layout weights) has a 1 in 50 chance (sharedGroupChance) of being a drifting group. It is rolled deterministically per group, apart from the room and slot rolls. A drifting group then rolls the same pool / yours / fresh split as a shared slot, on its first carriage:

  • Leased: another world's group-long build is stamped verbatim across every carriage of the run, under the group's stage. It is only ever handed a build with exactly the same box (group length Γ— height Γ— width) in the same stage, so a world with a different group size never sees it.
  • Fresh: the ordinary Group carriage, contents and all. It is registered once, over the whole box, so the first block a player places or breaks anywhere in it uploads the whole group as one carriage_group build, and later edits stream as deltas.

Entities in a leased group are put back into the carriage they stand in, so decor in the third carriage stays with the third carriage as the train moves. The on-enter lines, death log, report command and advancements all resolve by footprint and work anywhere in the group. On the relay side, carriage_group is a lease kind (relay β‰₯ 0.239.0) and is play captures only: a Whole Group built in the Train Builder stays in its author's profile, whatever LEASE_BUILDER_BUILDS says. Against an older relay the mod hands back any non-group answer, so drifting groups simply stay fresh. Implementation: SharedGroupDrift (roll + pick), SharedGroupPool (lease buffer keyed by stage + box, which prefetches only once a group has drifted here).

Configuration

config/dungeontrain-server.toml:

Key Default Meaning
sharedCarriagesEnabled false Master switch β€” leasing, contributing, and the on-enter hint
sharedCarriagePoolChance 0.65 Per shared slot, chance to lease a build by any player
sharedCarriageOwnChance 0.30 Per shared slot, chance to lease back a build by a player in this world. Whatever pool + own leave over is the fresh-template share; if the two are set past 1.0 together, the own share is trimmed to fit
sharedCarriageMaxEntities 64 How many entities a shared carriage may carry into and out of the pool. 0 disables entity capture entirely β€” builds then travel as bare blocks
sharedRoomChance 0.0667 Per Bedrock dimensional carriage, chance that it drifts at all (1 in 15). A drifting room then uses the pool / own chances above
sharedGroupChance 0.02 Per Group carriage, chance that it drifts as one group-long build (1 in 50). A drifting group then uses the pool / own chances above

Contributing (uploading your edits) needs network consent, like signing a book; leasing and placing a pooled build need only the server master switch.

Implementation

Piece Role
SharedCarriageFlags which variant ids are shared (shared-carriages.json)
TrainAssembler.spawnGroup the pool-vs-fresh branch that leases + places or drops a blank slot
SharedCarriageRegistry in-memory subLevelId β†’ instance map of resident shared carriages
CarriageBlockSnapshot capture/place of a carriage's blocks + block-entities as portable NBT
CarriageEntitySnapshot capture/respawn of the carriage's free entities (blob ents), and the decor fingerprint
PendingRelayEntitySpawn a leased build's entities, spawned once the group settles (never at placement)
CarriageTextScan scrapes sign / book / named-item / entity text for moderation
SharedCarriagePool async prefetch buffer of leases for the spawn path
SharedGroupDrift / SharedGroupPool drifting Group carriages: the per-group roll + lease pick, and their own lease buffer
SharedCarriageEvents the throttled tick: prefetch, submit-fresh, save-leased, heartbeat, return
SharedCarriageClient HTTP client for the relay /carriages/* endpoints
ReportCarriageCommand /reportcarriage + /dungeontrain reportcarriage
relay carriages.js the single-holder lease pool, moderation verdict, and edit trail (SQLite)
TemplateLootPrefabs the loot prefabs a builder template's chests link to, carried with the build and installed (or asked about) on download β€” see Editor-Container-Variants
relay carriage-loot-prefabs.js the per-builder loot_prefabs library those land in; LOOT_PREFABS_MAX_PER_OWNER (default 500) caps it by skipping new ids, never evicting

Planned

  • Stage-aware leasing β€” record the carriage's stage + cart position at first capture and only lease a build back into a same-stage slot. (The 3D preview listed here previously has shipped β€” see below.)

The 3D preview

The relay's admin Carriages page renders each stored build in 3D from the blob itself: textured voxels for the blocks (procedurally painted β€” no Mojang textures ship with the web app), a Peel control that hides the one to three bounding faces nearest the camera so a closed build can be seen inside, and a history scrubber across every recorded save and delta.

Entities appear as flat camera-facing cards labelled with the entity's type and custom name, tinted per type β€” a card, not a likeness, since the web app has no entity models. An Entities toggle hides them for a look at the blockwork, a peeled-away wall takes its item frames with it, and the detail header reports the entity count.

Related

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