Skip to content

Feature Starter Train

Brennan Hatton edited this page May 2, 2026 · 3 revisions

Starter train on login + configurable spawn Y

Since: v0.15.0 — PR #19 v0.27.0 — PR #37: train assembled at world-gen time; player spawn avoids water and the void. v0.83.0 — PR #120: login spawn rewritten — cached placement + PlayerFudgeSpawnMixin deliver the player at the final position from frame 1 (no visible jump). Now its own feature page; see Login spawn placement for the full pipeline.

The mod starts itself — no /dungeontrain spawn needed. When the server starts, if no Dungeon Train is present, a 10-carriage train is auto-assembled at (0, trainY, 0) before any player joins, so the world's first frame already contains the train. Every joining player (including the first) is then placed off-corridor, on natural ground, looking at the nearest above-ground stretch of track — see the dedicated Login spawn placement page for that pipeline.

Train assembly — at server start

TrainBootstrapEvents subscribes to ServerStartedEvent with EventPriority.LOW so the client-side WorldLifecycleEvents handler commits any pending Create-World-screen choices (trainY, dims, startsWithTrain) into DungeonTrainWorldData first. The bootstrap then:

  1. Reads per-world DungeonTrainWorldData from the overworld.
  2. Skips if startsWithTrain=false or any carriage of a Dungeon Train already exists.
  3. Otherwise calls TrainAssembler.spawnTrain(level, (0, trainY, 0), TRAIN_VELOCITY, 10, origin, dims). Internally this delegates to spawnGroup to place a seed group of carriages with half-flatbed pads on each end (see Feature-Train § Per-carriage architecture for the full mechanism). The append-only TrainCarriageAppender extends from there once a player gets within range.
  4. Calls PlayerJoinEvents.computeAndCacheBootstrapPlacement(...) to compute and cache the player's eventual login spawn position, then writes it to the level's default spawn point. This is what lets first-time players land at the cached placement directly via the PlayerFudgeSpawnMixin (no visible vanilla-spawn flash).

Common-scope (no @Dist guard), so dedicated servers also auto-spawn — previously the train only appeared on first player login.

Player placement — on login

The full pipeline (cached placement, mixin, buffered-X scan, perpendicular walk, on-train-roof last resort, leaves rejection, no-water guarantee) lives on its own page: Login spawn placement.

For this page, the short version: every joining player is placed off-corridor on natural ground within ~50 blocks of the train, facing the nearest above-ground stretch of track. The placement is computed at ServerStartedEvent time (right after the bootstrap above) and cached, so the very first frame the player sees on a fresh world is already correct — no (0, 64, 0) flash, no visible teleport, no rotation snap.

trainY config

The spawn altitude is a server-scoped Forge config value (same config file as numCarriages / speed):

Field Range Default
Train Y [-64, 320] 78

Default 78 is ~15 blocks above vanilla sea level (63) — high enough to clear most coastline terrain but low enough that the train visibly plows through hills and shorelines. Editable via Esc → Mods → Dungeon Train → Config (new "Train Y:" row) or directly in <save>/serverconfig/dungeontrain-server.toml:

[train]
    trainY = 78

Applies to next spawn only. Changing the config doesn't move an existing train — its Y is baked into the ship when it was assembled. To apply a new Y: delete the current train (e.g. /dungeontrain spawn 10 re-assembles) or rejoin a world where no train is loaded.

Dev/admin override

The existing /dungeontrain spawn [count] command keeps working — it still spawns 10 blocks in front of the player's look direction, independent of trainY. Treat auto-spawn as the default path and /dungeontrain spawn as the admin/testing override.

Files

Known limitations

  • Starting dimension is configurable per world. The bootstrap runs in whichever dimension the world's startingDimension says (default Overworld). See Feature-World-Type for the Nether / End starting-dimension flow.
  • Login event only. Respawning after death fires PlayerRespawnEvent, which is not hooked — the player respawns at their bed or world spawn. The level's default spawn point is the cached placement, so dying without a bed lands the player back on natural ground next to the train.
  • No cross-dimension teleport on login. If the player's last-logged-in dimension differs from startingDimension, they stay where they were. Teleporting them across dimensions on every login would feel invasive.
  • Placement is fixed at server start, not per-login. The cached placement is computed once at ServerStartedEvent from the train's then-current position. By login time the train has moved a few blocks; the appender extends the train back over the cached placement, so the player still spawns next to a carriage. Long server uptimes between bootstrap and first login are fine — the train is append-only.
  • On-train-roof last resort in tiny-island biomes. If neither the buffered-X scan nor the perpendicular walk (capped at 60 blocks) finds dry ground, the player is placed on top of the carriage roof rather than underwater on a seabed. See Login spawn placement § Fallback chain for the full ladder.

Clone this wiki locally