Skip to main content

Chunk System

The chunk system procedurally generates the forest trail that players navigate. Chunks stream ahead of the player and despawn behind them.

Pipeline Overview

Server Startup
→ Load start chunk (ChunkPrefab_Start)
→ Prewarm 3 chunks ahead
→ Sync game seed to clients

Heartbeat (0.25s)
→ Update safe zone players
→ Ensure chunks ahead for all players
→ Check run completion
→ Process pending chunk queue (one per tick)
→ Process pending unload queue (one per tick)

Key Files

FilePurpose
chunk-service.tsMain orchestrator — heartbeat, safe zones, run completion
chunk-lifecycle-manager.tsPer-chunk spawn / unload queue, ground-grid upkeep
run-director.tsSource of truth for the run plan — owns PacingManager + FeedbackState, handles devtools replans
chunk-generator.tsTemplate cloning, positioning, overlap detection
chunk-prepper.tsCreates Start/End parts from baseplate attachments
utils/pacing-manager.tsDifficulty cycle, safe zone markers, enemy scaling (pure calculator)
utils/chunk-planner.tsDFS with chronological backtracking — pre-validates the entire run's chunk layout
utils/planner/plan-chase-section.tsRiver / minecart atomic sections + river realignment
utils/planner/plan-fork-section.tsSplit-and-merge branch sections with interleaved DFS
danger-zone-manager.tsRing zones around baseplates for enemy spawning
baseplate-manager.tsOctree-based spatial indexing of chunk positions
director/feedback-state.tsPhase 8 accumulator — rolling signal vector from gameplay events
director/feedback-policies.tsPressure adjusters consuming FeedbackSignal (flag-gated)

RunDirector

RunDirector (formerly RunPlanManager) is the single handle ChunkService uses for all run-level state. It owns:

  • The pre-validated placement plan (PlacementInstruction[]) produced by ChunkPlanner.planRun.
  • A composed PacingManager — pacing queries like getPacingForChunk(idx), getMaxTrailMarker() are exposed as director delegates.
  • The FeedbackState accumulator (Phase 8) — tracks deaths, clears, and streaks to feed the pressure policies.
  • Preset / fork-section / chase-section configuration from devtools.
  • Seed-retry logic for ensurePlanBuilt and the devtools planWithSeed / replanFutureChunks paths.

Callers consume it through ChunkService — never instantiate a separate PacingManager outside the director unless you're in a pure-function context (tests, planner sub-modules).

Chunk Planner

ChunkPlanner.planRun is the main DFS loop. Chase and fork sections were extracted into utils/planner/:

  • plan-chase-section.ts — atomic multi-chunk sections (River, minecart). Handles river-specific steering: straight-first on entry, cardinal realignment over the last few chunks.
  • plan-fork-section.ts — split-and-merge branches. Interleaves A / B placements (A₀, B₀, A₁, B₁, …) with its own backtracking stack, then runs computeMergeChunks to reconverge.
  • planner/build-candidates.ts, overlap-checks.ts, simulate-placement.ts, screening.ts, bush-validation.ts — shared helpers.

Chunk Creation Flow

  1. Plan lookuprunDirector.getRunPlan() returns the pre-validated PlacementInstruction for the next chunk index.
  2. Template SelectiongetChunkForPacing() resolves the instruction against ReplicatedStorage.Prefabs.Cyclable.
  3. PreparationprepareChunk() creates Start/End parts from baseplate attachments.
  4. Positioning — End-to-end alignment using Start/End attachment CFrames (planner already validated this works).
  5. Registration — Tagged Gameplay_Chunk, baseplate registered in octree.
  6. Danger Zones — Deferred ring creation around the baseplate.

Pacing System

The game cycles through phases, with safe zones at fixed trail markers:

Calm (2 chunks) → Tense (2 chunks) → Challenge (2 chunks) → repeat
Safe Zones at markers: 10, 30, 70

Trail Markers

  • ~1 marker per 2 chunks: trailMarker = floor(chunkIndex / 2) + 1
  • Physical wooden posts placed by GameplayChunk component
  • Max trail marker: 30 (MVP) — chunks stop generating after this

Difficulty Scaling

  • Multiplier: 1.0 + (trailMarker × 0.05), capped at 3.0
  • Affects enemy density and aggression per phase

Chunk Unloading

When enabled, chunks 3+ behind the furthest-back player are queued for unloading:

  1. Path nodes re-parented to UnloadedPathNodes folder (keeps refs valid)
  2. Danger zones removed
  3. Chunk model destroyed
  4. Echo boundary updated for late joiners

Safe Zones

Safe zone detection uses a dual approach:

  • Registered parts — Tagged SafeZone parts checked via PointToObjectSpace
  • Pacing-based — Chunks with isSafeZone pacing flag

Safe zone effects (0.25s tick):

  • Health regen: 10 HP/sec (2.5 per tick)
  • Insanity decay (client-side)
  • Enemy exclusion

Server Verification

Client detects safe zone entry via ZonePlus, fires remote. Server verifies with retry logic (3 attempts, 0.15s apart) to account for network latency.

Run Completion

When a player reaches the final safe zone (marker 30):

  1. checkRunCompletion() detects player on SafeZone chunk at max marker
  2. runCompleteCallback fires
  3. Player teleported to lobby