ui/synthetic-report: dev-only legacy report loader on lobby
Adds api/synthetic-report.ts, an in-memory registry + JSON->GameReport decoder for synthetic-mode game sessions. The lobby grows a import.meta.env.DEV-gated "Synthetic test reports" section with a JSON file picker; loading a file registers the decoded report under a synthetic-<uuid> id and navigates to /games/<id>/map. The in-game shell layout detects the synthetic id range, takes the report straight from the registry via gameState.initSynthetic, and deliberately skips both galaxyClient.set and orderDraft.bindClient. Order auto-sync stays silent: scheduleSync already short-circuits on non-UUID game ids, and without a bound client the network path is unreachable. applyOrderOverlay continues to project locally-valid draft commands onto the rendered report so renames / production choices / route edits are visible immediately. A page reload loses the in-memory entry and redirects to /lobby — synthetic mode is a debug affordance, not a session. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,16 @@ export class GameStateStore {
|
||||
* later phases (history mode, calc) will read it directly.
|
||||
*/
|
||||
currentTurn = $state(0);
|
||||
/**
|
||||
* synthetic is set by `initSynthetic` for DEV-only sessions backed
|
||||
* by a hand-loaded report (lobby's "Load synthetic report"
|
||||
* affordance). The flag travels through the layout so the order
|
||||
* tab and any future server-bound features can short-circuit and
|
||||
* stay local. The auto-sync pipeline already protects itself via
|
||||
* the UUID guard on `OrderDraftStore.scheduleSync`, so flipping
|
||||
* this flag is enough to keep the network silent.
|
||||
*/
|
||||
synthetic = $state(false);
|
||||
|
||||
private client: GalaxyClient | null = null;
|
||||
private cache: Cache | null = null;
|
||||
@@ -161,6 +171,31 @@ export class GameStateStore {
|
||||
this.error = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* initSynthetic seeds the store from a pre-loaded `GameReport`
|
||||
* without touching the network. Used by the lobby's DEV-only
|
||||
* "Load synthetic report" affordance: the layout invokes this
|
||||
* instead of `init` when the route id is in the synthetic id
|
||||
* range. The store ends up in `ready` immediately; no polling,
|
||||
* no visibility-driven refresh, no client / cache-of-server
|
||||
* binding.
|
||||
*/
|
||||
async initSynthetic(opts: {
|
||||
cache: Cache;
|
||||
gameId: string;
|
||||
report: GameReport;
|
||||
}): Promise<void> {
|
||||
this.cache = opts.cache;
|
||||
this.gameId = opts.gameId;
|
||||
this.synthetic = true;
|
||||
this.gameName = "Synthetic";
|
||||
this.error = null;
|
||||
this.wrapMode = await readWrapMode(opts.cache, opts.gameId);
|
||||
this.report = opts.report;
|
||||
this.currentTurn = opts.report.turn;
|
||||
this.status = "ready";
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.destroyed = true;
|
||||
if (this.visibilityListener !== null && typeof document !== "undefined") {
|
||||
|
||||
Reference in New Issue
Block a user