Fixed world clock: no player time controls (ISS-117)
CI / build + unit + e2e (pull_request) Failing after 12m10s
CI / build + unit + e2e (pull_request) Failing after 12m10s
The world always runs at WORLD_TIME_SCALE = 24 (lib/worldClock.ts). A server
background tick (instrumentation.ts -> startBackgroundTick, 1 s interval)
advances the sim with no open browser; idle-pause freezes the clock exactly
while no plane is flying or in repair. Fresh worlds start year 2000
(STARTING_CLOCK_MS in seed.ts).
- Removed: POST /api/sim + OpenAPI entry, applyTimeControls/clampTimeScale,
lib/timeScale.ts, LeftPane pause button + scale slider machinery,
ISS-057 slow-on-landing/resume in flyPlane.
- Snapshot simState is now { clockMs } only; client extrapolates at the fixed
rate between polls and freezes while idle (useSimClock).
- New test-only hook POST /api/test/set-timescale behind FLIGHT_TEST_HOOKS=1
(sibling of set-health); all e2e setScale helpers call it, afterEach restores
the world rate. E2E pause tests replaced by idle-pause assertions; sim.spec.ts
activates the world via set-health + /repair for advance/expiry tests.
This commit is contained in:
+12
-8
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { WORLD_TIME_SCALE } from '@/lib/worldClock';
|
||||
import { haversineKm } from '@/lib/geo';
|
||||
import { PLANE_TYPES, repRate, STARTER_MODEL } from './catalog';
|
||||
import { SPECIALS } from './specials';
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
REWARD_PER_KM,
|
||||
rewardFor,
|
||||
STARTING_CASH,
|
||||
STARTING_CLOCK_MS,
|
||||
STARTING_REPUTATION,
|
||||
type Rng,
|
||||
type SeedAirport,
|
||||
@@ -73,7 +75,7 @@ describe('buildSeed', () => {
|
||||
expect(c.originIcao).not.toBe(c.destIcao);
|
||||
expect(c.status).toBe('open');
|
||||
expect(c.planeId).toBeNull();
|
||||
expect(c.createdSimTime).toBe(NOW);
|
||||
expect(c.createdSimTime).toBe(STARTING_CLOCK_MS); // game-clock origin, not wall time (ISS-117)
|
||||
}
|
||||
expect(new Set(seed.packages.map((c) => c.id)).size).toBe(seed.packages.length);
|
||||
});
|
||||
@@ -101,22 +103,23 @@ describe('buildSeed', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('sets deadlines inside the 6–72h game-time window after now (scaled by any special status)', () => {
|
||||
it('sets deadlines inside the 6–72h game-time window after the game-clock origin (scaled by any special status)', () => {
|
||||
const seed = seedWith(42);
|
||||
for (const c of seed.packages) {
|
||||
// v0.37.0 (ISS-073): a special's deadlineScale tightens the drawn window (e.g. priority → 3–36h).
|
||||
// ISS-117: the window is anchored at STARTING_CLOCK_MS, not wall time.
|
||||
const scale = c.special ? SPECIALS[c.special]?.deadlineScale ?? 1 : 1;
|
||||
expect(c.deadlineSimTime).toBeGreaterThan(NOW + 6 * 3_600_000 * scale - 1);
|
||||
expect(c.deadlineSimTime).toBeLessThanOrEqual(NOW + 72 * 3_600_000 * scale);
|
||||
expect(c.deadlineSimTime).toBeGreaterThan(STARTING_CLOCK_MS + 6 * 3_600_000 * scale - 1);
|
||||
expect(c.deadlineSimTime).toBeLessThanOrEqual(STARTING_CLOCK_MS + 72 * 3_600_000 * scale);
|
||||
}
|
||||
});
|
||||
|
||||
it('seeds the starting account and sim state', () => {
|
||||
const seed = seedWith(42);
|
||||
expect(seed.account).toEqual({ cash: STARTING_CASH, reputation: STARTING_REPUTATION });
|
||||
// lastRealTickMs starts at the seed moment so the first tick-on-read advances ~0;
|
||||
// lastSelectedTimeScale mirrors the seeded speed (v0.30.0, ISS-057).
|
||||
expect(seed.simState).toEqual({ clockMs: NOW, timeScale: 1, paused: false, lastRealTickMs: NOW, lastSelectedTimeScale: 1 });
|
||||
// ISS-117 (v0.52.0): game clock starts at year 2000 with the fixed world rate; lastRealTickMs is
|
||||
// the REAL seed moment so the first tick-on-read advances ~0. paused/lastSelectedTimeScale are legacy columns.
|
||||
expect(seed.simState).toEqual({ clockMs: STARTING_CLOCK_MS, timeScale: WORLD_TIME_SCALE, paused: false, lastRealTickMs: NOW, lastSelectedTimeScale: WORLD_TIME_SCALE });
|
||||
});
|
||||
|
||||
it('is fully deterministic for a fixed RNG and inputs', () => {
|
||||
@@ -186,7 +189,8 @@ describe('generatePackage (shared seed/spawn builder)', () => {
|
||||
const count = PACKAGE_COUNT_MIN + Math.floor(rngA() * (PACKAGE_COUNT_MAX - PACKAGE_COUNT_MIN + 1));
|
||||
const expected: string[] = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
expected.push(JSON.stringify(generatePackage({ id: `package-${i + 1}`, airports: AIRPORTS, rng: rngA, nowMs: NOW })));
|
||||
// ISS-117: buildSeed anchors packages at the game-clock origin, not the wall-time input.
|
||||
expected.push(JSON.stringify(generatePackage({ id: `package-${i + 1}`, airports: AIRPORTS, rng: rngA, nowMs: STARTING_CLOCK_MS })));
|
||||
}
|
||||
const seed = buildSeed({ airports: AIRPORTS, rng: mulberry32(42), nowMs: NOW });
|
||||
expect(seed.packages.map((c) => JSON.stringify(c))).toEqual(expected);
|
||||
|
||||
Reference in New Issue
Block a user