Idle fleet freezes world clock — market stops generating jobs; slow to real-time instead of freezing #128

Closed
opened 2026-09-22 19:08:38 +02:00 by kamilkrzywda · 1 comment
Owner

Reported by user (2026-09-22): "when game is paused no new jobs are appearing - game shouldn't stop at all - it should slow down to 1/100 or something - 1/24 would be fun number - let's use our speed constants."

Problem

Since v0.52.0, when the whole fleet is grounded (idle-pause via shouldAdvanceTime in lib/sim/engine.ts), the world clock freezes completely. Package generation/expiry only run on advancing ticks → while idle, no new contracts appear and nothing expires. From the player's perspective the game is dead until they dispatch a plane.

Change

When all planes are grounded, advance the world clock at a minimum rate instead of freezing: real-time (1 game-second per real second) = WORLD_TIME_SCALE / 24 — derived from the existing speed constants in lib/worldClock.ts, no new magic number. When any plane is active, behavior is unchanged (full scale; test-hook set-timescale overrides as today).

Tests to update

  • e2e/sim.spec.ts "a fully grounded fleet freezes the clock exactly across polls" → now: advances at ~real-time (bounded tolerance, clearly below full 24x)
  • e2e/state.spec.ts:42 exact clock equality across restart relies on idle-freeze → tolerance-based bounds instead
  • Audit unit tests (lib/sim/engine.test.ts etc.) + other e2e specs for freeze assumptions; the activateWorld/repair pattern in actions.spec.ts stays valid

Process

Branch off master after v0.55.0 (PR #126) merges. Version 0.56.0. No API change (no OpenAPI delta).

Reported by user (2026-09-22): "when game is paused no new jobs are appearing - game shouldn't stop at all - it should slow down to 1/100 or something - 1/24 would be fun number - let's use our speed constants." ## Problem Since v0.52.0, when the whole fleet is grounded (idle-pause via `shouldAdvanceTime` in lib/sim/engine.ts), the world clock freezes completely. Package generation/expiry only run on advancing ticks → while idle, no new contracts appear and nothing expires. From the player's perspective the game is dead until they dispatch a plane. ## Change When all planes are grounded, advance the world clock at a minimum rate instead of freezing: **real-time** (1 game-second per real second) = `WORLD_TIME_SCALE / 24` — derived from the existing speed constants in lib/worldClock.ts, no new magic number. When any plane is active, behavior is unchanged (full scale; test-hook set-timescale overrides as today). ## Tests to update - e2e/sim.spec.ts "a fully grounded fleet freezes the clock exactly across polls" → now: advances at ~real-time (bounded tolerance, clearly below full 24x) - e2e/state.spec.ts:42 exact clock equality across restart relies on idle-freeze → tolerance-based bounds instead - Audit unit tests (lib/sim/engine.test.ts etc.) + other e2e specs for freeze assumptions; the activateWorld/repair pattern in actions.spec.ts stays valid ## Process Branch off master after v0.55.0 (PR #126) merges. Version 0.56.0. No API change (no OpenAPI delta).
Author
Owner

Fixed in v0.56.0 (PR #130, merge commit 9b2938a).

A fully grounded fleet no longer freezes the world clock — it now advances at real-time: new constant IDLE_WORLD_TIME_SCALE = WORLD_TIME_SCALE / 24 (= exactly 1) in lib/worldClock.ts, and tickOnce (lib/sim/engine.ts) uses it instead of skipping advancement when shouldAdvanceTime is false. Packages keep spawning/expiring while idle, so the market stays fresh; an active fleet keeps its full scale including any FLIGHT_TEST_HOOKS override (grounded + hook scale 1000 → ×1, not ×1000), and lastRealTickMs always tracks real time so resuming activity never fast-forwards the idle duration. The client mirror (advanceSimClock) extrapolates at the same idle rate between polls instead of freezing.

Tests: unit — engine.test.ts pins clamped real-time advance, resume-without-catch-up, hook-scale-ignored-while-grounded and the constant itself; useSimClock.test.ts mirrors it (366 total). E2E — sim.spec.ts freeze test became a bounded real-time delta check; state.spec.ts restart test uses derived bounds instead of exact equality; right-pane.spec.ts penalty preview no longer races a frozen clock.

Deployed: https://flight.dev.kkhost.pl (/api/version = 0.56.0, verified).

Fixed in **v0.56.0** (PR #130, merge commit `9b2938a`). A fully grounded fleet no longer freezes the world clock — it now advances at real-time: new constant `IDLE_WORLD_TIME_SCALE = WORLD_TIME_SCALE / 24` (= exactly 1) in `lib/worldClock.ts`, and `tickOnce` (lib/sim/engine.ts) uses it instead of skipping advancement when `shouldAdvanceTime` is false. Packages keep spawning/expiring while idle, so the market stays fresh; an active fleet keeps its full scale including any FLIGHT_TEST_HOOKS override (grounded + hook scale 1000 → ×1, not ×1000), and `lastRealTickMs` always tracks real time so resuming activity never fast-forwards the idle duration. The client mirror (`advanceSimClock`) extrapolates at the same idle rate between polls instead of freezing. Tests: unit — engine.test.ts pins clamped real-time advance, resume-without-catch-up, hook-scale-ignored-while-grounded and the constant itself; useSimClock.test.ts mirrors it (366 total). E2E — sim.spec.ts freeze test became a bounded real-time delta check; state.spec.ts restart test uses derived bounds instead of exact equality; right-pane.spec.ts penalty preview no longer races a frozen clock. Deployed: https://flight.dev.kkhost.pl (`/api/version` = 0.56.0, verified).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OpenCode/Flight-Ops#128