feat: v0.7.0 — server-authoritative sim engine: tick-on-read clock (time-scale + pause via POST /api/sim), contract expiry + market spawn, analytic plane movement/finalization
This commit is contained in:
+28
-2
@@ -29,7 +29,8 @@ CREATE TABLE IF NOT EXISTS planes (
|
||||
location_icao TEXT NOT NULL,
|
||||
contract_id TEXT,
|
||||
lat REAL NOT NULL,
|
||||
lon REAL NOT NULL
|
||||
lon REAL NOT NULL,
|
||||
departure_sim_time INTEGER
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS contracts (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
@@ -50,10 +51,25 @@ CREATE TABLE IF NOT EXISTS account (
|
||||
CREATE TABLE IF NOT EXISTS sim_state (
|
||||
clock_ms INTEGER NOT NULL,
|
||||
time_scale REAL NOT NULL,
|
||||
paused INTEGER NOT NULL
|
||||
paused INTEGER NOT NULL,
|
||||
last_real_tick_ms INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
`;
|
||||
|
||||
// v0.7.0: DBs created by earlier versions lack the new columns; CREATE TABLE IF
|
||||
// NOT EXISTS won't add them, so ALTER (guarded by PRAGMA table_info). Fresh DBs
|
||||
// already have everything from DDL above and this is a no-op for them.
|
||||
function migrate(raw: RawSqlJsDatabase): void {
|
||||
const cols = (table: string): Set<string> =>
|
||||
new Set(raw.exec(`PRAGMA table_info(${table})`)[0].values.map((r) => String(r[1])));
|
||||
if (!cols('sim_state').has('last_real_tick_ms')) {
|
||||
raw.exec('ALTER TABLE sim_state ADD COLUMN last_real_tick_ms INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
if (!cols('planes').has('departure_sim_time')) {
|
||||
raw.exec('ALTER TABLE planes ADD COLUMN departure_sim_time INTEGER');
|
||||
}
|
||||
}
|
||||
|
||||
// FLIGHT_DB_PATH override exists so unit tests can point at a temp file instead
|
||||
// of the real ./db/flight.db.
|
||||
export function defaultDbPath(): string {
|
||||
@@ -70,6 +86,7 @@ async function open(file: string): Promise<GameDb> {
|
||||
}
|
||||
const raw = bytes ? new SQL.Database(new Uint8Array(bytes)) : new SQL.Database();
|
||||
raw.exec(DDL);
|
||||
migrate(raw);
|
||||
|
||||
const instance: GameDb = { db: drizzle(raw, { schema }), raw, file };
|
||||
|
||||
@@ -78,6 +95,15 @@ async function open(file: string): Promise<GameDb> {
|
||||
if (Number(res[0]?.values?.[0]?.[0] ?? 0) === 0) {
|
||||
await seedEmpty(instance);
|
||||
}
|
||||
|
||||
// v0.7.0 fixup: pre-v0.7.0 DBs got last_real_tick_ms = 0 from the ALTER TABLE
|
||||
// default. Reset it to "now" so the first tick-on-read resumes cleanly instead
|
||||
// of taking one clamped catch-up jump. Fresh seeds already carry a real value.
|
||||
const simRows = raw.exec('SELECT clock_ms, last_real_tick_ms FROM sim_state');
|
||||
if (simRows[0]?.values?.length && Number(simRows[0].values[0][1]) === 0) {
|
||||
raw.exec(`UPDATE sim_state SET last_real_tick_ms = ${Date.now()}`);
|
||||
await persist(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user