Skip to content

Feature Train Chain

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

Successor-train chain (retired in v0.83.0 — historical)

Active: v0.40.0 – v0.82.0

Retired. The successor-train chain was made obsolete by the per-carriage Trains refactor in v0.83.0 (PR #119). Each carriage is now its own Sable sub-level with its own shipyard allocation — there's no 128-chunk wall to work around, so the entire successor-spawning machinery (TrainChainManager, spawnSuccessor, the forwardLimit/backwardLimit provider state) was deleted. See Feature-Train for the current architecture.

The text below is retained as historical context for anyone reading older code or commit history. None of these classes exist on main anymore.


What it did

Let the player ride a Dungeon Train indefinitely, past the carriage index ≈ ±228 hard wall that used to freeze the game. When the rolling-window manager spots the player approaching that wall, it spawns a second, independent train just ahead of the current one. The new train has its own fresh shipyard allocation, so it has a full 128-chunk runway of its own. The player walks off the tail end of Ship A and onto the front of Ship B without any visible seam — the rolling window on each ship paints carriages right up to the seam on its own side.

Under the hood the chain is driven by three pieces:

  • CarriageFootprint — replaces ship.getWorldAABB() in the per-tick clearBlocksAhead and killEntitiesIn sweeps. Ship AABBs grow unbounded as the train travels (VS never reclaims shipyard chunks); using a footprint of the 10 currently-active carriages keeps the work bounded. Dropped server lag from 728 ticks behind (at pIdx 228) to occasional 80-tick blips.
  • TrainChainManager — once any player's local pIdx on a train passes 100, spawns a successor via TrainAssembler.spawnSuccessor with its carriage-0 positioned exactly where the predecessor's next carriage would have gone. Caps the predecessor's rolling-window forward extent at pIdx 105 so it stops painting past the seam, and sets the successor's backward extent at 0 so it doesn't paint backwards onto the predecessor.
  • Per-ship carriage persistence — CarriagePersistenceStore snapshots every erased carriage as a StructureTemplate at <world>/dungeontrain/carriage-persist/<dim>/<shipId>/<idx>.nbt. Walk past a carriage, edit something, come back → the edits restore. Chain-aware: each ship in the chain keys its own subfolder, so overlapping local pIdx between ships can't collide.

Why the chain exists

Each Valkyrien Skies 2.4.11 ship gets a fixed 128-chunk shipyard allocation at creation time. Beyond that bound the ship's voxel data can't be stored, rendered, or collided with. Our single-ship rolling-window design eventually paints carriages past that allocation and the server starts rejecting player movement forward — manifesting as the stuck-at-228 freeze. The chain works with the allocation rather than against it: each successor ship brings its own fresh region.

Tuning

Two constants in TrainChainManager:

  • SUCCESSOR_TRIGGER_PIDX = 100 — how far along a train the player gets before its successor spawns. 100 leaves ~128 carriages of safety before the 2048-block VS wall at pIdx 228.
  • Predecessor forward-limit = trigger + halfFront — the last carriage index the predecessor's rolling window will paint. For the default 11-carriage window this is 100 + 5 = 105.

Known limits

  • Retired ships linger. Once the player has crossed onto a successor, the predecessor's rolling window collapses to zero active carriages but the ship itself stays loaded as an empty VS ship. After a long session this accumulates; a /dt reset cleans them up. Proper retired-ship cleanup is Phase 2.
  • Reverse direction still walls out. Walking -X past pIdx −228 on the starter train is still blocked. A symmetric predecessor-spawn trigger is a follow-up.
  • Save/reload mid-chain only keeps the primary. Only the first train in a chain survives a world-reload right now; successors disappear. Fix is to serialise chain state into DungeonTrainWorldData.

Related files

→ See Compatibility for the VS 128-chunk shipyard-allocation constraint this feature works around.

Clone this wiki locally