92 lines
2.8 KiB
TypeScript
92 lines
2.8 KiB
TypeScript
import { loadAirportsGeoJson } from '@/lib/airports';
|
|
import { getGameDb } from '@/lib/db/client';
|
|
import * as schema from '@/lib/db/schema';
|
|
|
|
// Read-only game-state snapshot for client polling (GET /api/state).
|
|
|
|
export interface PlaneState {
|
|
id: string;
|
|
model: string;
|
|
status: 'available' | 'flying';
|
|
fuel: number;
|
|
maxFuel: number;
|
|
locationIcao: string;
|
|
locationName: string;
|
|
contractId: string | null;
|
|
lat: number;
|
|
lon: number;
|
|
}
|
|
|
|
export interface ContractState {
|
|
id: string;
|
|
originIcao: string;
|
|
originName: string;
|
|
destIcao: string;
|
|
destName: string;
|
|
reward: number;
|
|
cargoType: string;
|
|
cargoCount: number;
|
|
deadlineSimTime: number;
|
|
status: 'open' | 'accepted' | 'completed';
|
|
planeId: string | null;
|
|
createdSimTime: number;
|
|
}
|
|
|
|
export interface StateSnapshot {
|
|
account: { cash: number; reputation: number };
|
|
simState: { clockMs: number; timeScale: number; paused: boolean };
|
|
planes: PlaneState[];
|
|
contracts: ContractState[];
|
|
}
|
|
|
|
export async function getStateSnapshot(): Promise<StateSnapshot> {
|
|
const { db } = await getGameDb();
|
|
// NOTE: use plain select(), not the relational db.query.* API — in drizzle-orm
|
|
// 0.45.x the sql-js driver drops the result mapper for relational queries and
|
|
// returns raw rows keyed by SQL column names (snake_case, no type decoding).
|
|
const [accountRows, simRows, planeRows, contractRows] = await Promise.all([
|
|
db.select().from(schema.account),
|
|
db.select().from(schema.simState),
|
|
db.select().from(schema.planes),
|
|
db.select().from(schema.contracts),
|
|
]);
|
|
const accountRow = accountRows[0];
|
|
const simRow = simRows[0];
|
|
if (!accountRow || !simRow) throw new Error('game state missing account/sim_state row');
|
|
|
|
const fc = await loadAirportsGeoJson();
|
|
const nameByIcao = new Map(fc.features.map((f) => [f.properties.icao, f.properties.name]));
|
|
const nameOf = (icao: string) => nameByIcao.get(icao) ?? icao;
|
|
|
|
return {
|
|
account: { cash: accountRow.cash, reputation: accountRow.reputation },
|
|
simState: { clockMs: simRow.clockMs, timeScale: simRow.timeScale, paused: simRow.paused },
|
|
planes: planeRows.map((p) => ({
|
|
id: p.id,
|
|
model: p.model,
|
|
status: p.status,
|
|
fuel: p.fuel,
|
|
maxFuel: p.maxFuel,
|
|
locationIcao: p.locationIcao,
|
|
locationName: nameOf(p.locationIcao),
|
|
contractId: p.contractId,
|
|
lat: p.lat,
|
|
lon: p.lon,
|
|
})),
|
|
contracts: contractRows.map((c) => ({
|
|
id: c.id,
|
|
originIcao: c.originIcao,
|
|
originName: nameOf(c.originIcao),
|
|
destIcao: c.destIcao,
|
|
destName: nameOf(c.destIcao),
|
|
reward: c.reward,
|
|
cargoType: c.cargoType,
|
|
cargoCount: c.cargoCount,
|
|
deadlineSimTime: c.deadlineSimTime,
|
|
status: c.status,
|
|
planeId: c.planeId,
|
|
createdSimTime: c.createdSimTime,
|
|
})),
|
|
};
|
|
}
|