Skip to content

Feature Spawn Cinematic

Brennan Hatton edited this page Aug 30, 2026 · 5 revisions

Spawn cinematic + spawn-on-train

Since: v0.293.0 — PR #365

The first time a player enters a world, they now spawn standing on a flatbed deck of the moving train — and before they get control, a locked "spectator-style" intro camera flies up from the old ground spawn spot to reveal the train, then hands over control. Press Space to skip.

This repurposes the Login spawn placement pipeline: the ground pose that the old system used to stand the player at is now the camera's starting position (and may now sit over water). The player's body goes on the train instead.

What the player sees

  1. World loads; the camera is low to the ground (or just over the water) beside the train, facing it — the exact pose the player used to spawn at.
  2. Over ~6 s the camera rises and eases back while continuously aiming at the player (who is riding the train), so the character and train stay framed as the train pulls forward.
  3. The HUD and held item are hidden for the whole shot — a clean cinematic. The only thing that can appear is a "Press Space to skip" prompt, and only once the player tries a non-Space input.
  4. On skip or timeout the camera blends back to the player's eyes and control returns — the player is standing on a flatbed deck of the moving train.
  5. A beat later the welcome-book lightning strike lands next to them (held until the cinematic ends, so it never fires mid-shot).

The loading sequence before it

Since: v0.738.0 — PR #1205

The player never sees the raw spawn: a themed loading screen (train filling with carriages, story line, one continuous progress bar) covers the whole join, and CinematicPreloadGate holds it up — first pinning the player in place while the server puts them on the train, then waiting for the chunks around the camera-start column to stream in.

All screens of a vanilla join are themed, so the sequence reads as one — starting with the world-open GenericMessageScreen ("Reading world data"), which is swapped for a themed screen from ScreenEvent.Opening rather than mixed into, because that class inherits Screen.render instead of declaring it (PR #1209). Then: LevelLoadingScreen (world load), vanilla's ReceivingLevelScreen (which otherwise flashes the main-menu panorama and "Downloading terrain" right at the world → train handoff — fixed in #1205), and DT's own CinematicLoadingScreen. The ReceivingLevelScreen theming applies only during the join (LoadingSequenceProgress.isJoining(), ended by LoadingSequenceWatcher once the player is in-world), so portal travel and dimension changes keep vanilla's nether/end visuals. Train Builder worlds keep the vanilla chunk-dot screen throughout.

At 100% the title becomes "All Aboard!" and the screen reveals the shot it is about to play:

  • the panel cross-fades over 30 s into black at 70% opacity over the live world;
  • the train graphic fades over the first half of that, the title over the second;
  • the render camera is parked at the cinematic's frame-0 pose (CinematicCameraController.beginPreview, applied by CameraCinematicMixin), with the HUD hidden — so Space starts the fly-up from exactly the framing already on screen, with no camera pop. Those are the chunks the gate just waited for.

The hold ends when the loading story finishes or the player presses Space, so in practice the 30 s reveal is cut short — it is a slow lift of the dim, not a fixed-length beat.

Separately, the train engine ambient fades in over 30 s from the first tick it has a real volume to play (TrainEngineSound.fadeIn), instead of arriving at full volume the moment you are aboard. It scales whichever rule spoke — the distance curve or the portal-corridor rule — so their shapes and the player's Train Engine Volume setting are unaffected.

Flow

First login (this world)
  server: place body on a flatbed deck of the LIVE train (PlayerJoinEvents.tryPlace)
        : compute the old ground pose (allowWater) → camera start
        : CinematicIntroPacket → client; mark SEEN_INTRO_CINEMATIC; brief invulnerability
  client: detach render camera at the ground pose, freeze input, hide HUD + hand
        : each frame → rise + ease-back, look-at the local player
        : Space → end;  any other input → show "Press Space to skip"
        : end → blend to the player eye, restore input/HUD → CinematicDonePacket
  server: clear invulnerability; release the held welcome-book lightning

Spawn on the flatbed

The body is placed on a flatbed pad deck of the live train (the open floor cars at carriage-group boundaries), feet at trainY + 1, at the track-centre Z, facing +X (travel direction). The X is the front-pad centre of the resolved group, computed from the driver's live canonicalPos plus the group's halfPadLen / subLevelStride geometry — see PlayerJoinEvents.computeFlatbedTarget. Cross-dimension respawns also land on the flatbed (RespawnDimensionEvents).

Camera

Entirely client-side — a render-camera override, no physics involvement. CameraCinematicMixin injects the tail of Camera.setup(...) and, while active, replaces the camera position + rotation with the controller's pose for that frame's partial-tick.

CinematicCameraController.computePose(partialTick):

  • progress t over durationTicks, smoothstepped;
  • position = ground start + riseHeight·e up + pullBack·e back along the fixed frame-0 direction away from the player;
  • rotation = look-at the live local player (offset up by lookYOffset), blended from the sent start pose over the first ~15 % so frame 0 matches the ground view;
  • an ~8-tick release blend eases the camera into the player's eye on end (no hard cut).

Controls

Input Effect
Space Skip the cinematic immediately
Esc / switch windows (alt-tab) Opens the pause menu — fully usable (mouse + Esc); the cinematic suspends and resumes when you close it
Any other in-world key / mouse / scroll Surfaces the "Press Space to skip" prompt; otherwise swallowed

Input is frozen by swapping LocalPlayer.input for a no-op Input (so held keys can't drive movement through aiStep); in-world mouse buttons, the interaction key-mappings, and scroll are cancelled so nothing leaks into the world. The pause menu is allowed, though — pressing Esc or switching windows (pauseOnLostFocus) opens a fully usable PauseScreen. While any screen owns input the in-world blockers stand down and the cinematic suspends (the tick clock freezes), then resumes from where it left off once the screen closes. Inventory / chat stay blocked, to keep the shot clean. (Fixed in PR #402.)

No UI

mc.options.hideGui is forced on for the duration (restored after), hiding the hotbar / health / hunger / crosshair / XP and the mod HUD overlays; RenderHandEvent is cancelled so the first-person item doesn't float in the detached view. The skip prompt renders regardless of hideGui — it's the one intended overlay.

Lightning timing

The starting-book welcome strike normally fires ~10 ticks after login. CinematicIntroService.isCinematicActive(uuid) holds it while the cinematic plays; it fires the tick the cinematic ends (immediately on skip, ~6 s on timeout), landing next to the player on the train.

Frequency & config

Plays once per player per world — gated by a serialized per-player seen_intro_cinematic attachment (copyOnDeath, so a death never replays it; only login triggers it). SERVER config (dungeontrain-server.toml, [intro]):

Key Default Meaning
introCinematicEnabled true Play the intro on first entry. When off, the player still spawns on the flatbed — just no cinematic.
introCinematicDurationTicks 120 Length in ticks (20 = 1 s), range [20, 600].

Networking

  • CinematicIntroPacket (server → client): camera start pose, rise / pull-back / look-offset, duration.
  • CinematicDonePacket (client → server): cinematic ended — clears invulnerability early and releases the lightning.

Protocol bumped 23 → 24; clients and servers must both run ≥ v0.293.0 to connect.

Files

Related

Known limitations

  • Welcome book can lag behind. The book drops as a world ItemEntity at the player's position; on the moving train it may fall a couple of blocks behind before pickup. Follow-up: ride the train or go straight to inventory.
  • Late multiplayer first-join. The camera start is recomputed from the live train, but a player's first join long after world creation puts the body on the train wherever it has moved to — the cinematic still frames correctly since it tracks the player.

Clone this wiki locally