Auto-re-parse game data when parser version changes
All checks were successful
Deploy / deploy (push) Successful in 2m13s
All checks were successful
Deploy / deploy (push) Successful in 2m13s
The first-boot parse only ran on an empty DB, so deploying a parser fix left existing rows stale (e.g. enchanted ingredients still flat T6). Add PARSER_VERSION, stored in meta on parse; on boot the server re-parses when the stored version differs, auto-healing existing data after a deploy — no manual "Re-download game data" needed. Bumped to v2 for the enchanted-ingredient id fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7ed585d6a9
commit
9cd17d0934
2 changed files with 31 additions and 13 deletions
|
|
@ -8,6 +8,14 @@ import { gamedataFilePath } from './download.js';
|
||||||
* attributes appear as "@name" keys, and any child element may be an object or an array).
|
* attributes appear as "@name" keys, and any child element may be an object or an array).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bump whenever the parsing logic changes in a way that alters stored items/recipes.
|
||||||
|
* The server re-parses on boot when the DB's stored version differs, so a deploy that
|
||||||
|
* fixes parsing auto-heals existing data without a manual "re-download".
|
||||||
|
* v2: enchanted resource/ingredient ids carry @<enchant> (e.g. T6_SHOES_PLATE_SET1@4)
|
||||||
|
*/
|
||||||
|
export const PARSER_VERSION = 2;
|
||||||
|
|
||||||
type Raw = Record<string, any>;
|
type Raw = Record<string, any>;
|
||||||
|
|
||||||
function asArray<T>(v: T | T[] | undefined | null): T[] {
|
function asArray<T>(v: T | T[] | undefined | null): T[] {
|
||||||
|
|
@ -246,6 +254,7 @@ export async function parseGameData(): Promise<ParseStats> {
|
||||||
};
|
};
|
||||||
await setMeta('gamedata_parsed_at', new Date().toISOString());
|
await setMeta('gamedata_parsed_at', new Date().toISOString());
|
||||||
await setMeta('gamedata_stats', JSON.stringify(stats));
|
await setMeta('gamedata_stats', JSON.stringify(stats));
|
||||||
|
await setMeta('gamedata_parser_version', String(PARSER_VERSION));
|
||||||
cacheClear(); // items/recipes changed → drop every cached read
|
cacheClear(); // items/recipes changed → drop every cached read
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,22 +26,31 @@ const app = Fastify({ logger: { level: 'info' } });
|
||||||
|
|
||||||
await initDb(); // create schema on startup
|
await initDb(); // create schema on startup
|
||||||
|
|
||||||
// First-boot convenience: if no game data is loaded yet, download + parse it in
|
// On boot: (re)parse game data if the DB is empty OR was parsed by an older parser
|
||||||
// the background so a fresh deployment is usable without a manual step.
|
// version — so a deploy that fixes parsing auto-heals existing data. Runs in the
|
||||||
|
// background so it doesn't block startup.
|
||||||
void (async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
const { get } = await import('./db/index.js');
|
const { get, getMeta } = await import('./db/index.js');
|
||||||
const row = await get<{ n: number }>('SELECT COUNT(*) AS n FROM items');
|
const { PARSER_VERSION, parseGameData } = await import('./gamedata/parse.js');
|
||||||
if ((row?.n ?? 0) === 0) {
|
const { downloadGameData } = await import('./gamedata/download.js');
|
||||||
app.log.info('No game data found — downloading and parsing (first boot)...');
|
|
||||||
const { downloadGameData } = await import('./gamedata/download.js');
|
const count = (await get<{ n: number }>('SELECT COUNT(*) AS n FROM items'))?.n ?? 0;
|
||||||
const { parseGameData } = await import('./gamedata/parse.js');
|
const storedVersion = Number((await getMeta('gamedata_parser_version')) ?? 0);
|
||||||
await downloadGameData(false);
|
const empty = count === 0;
|
||||||
const stats = await parseGameData();
|
const stale = storedVersion !== PARSER_VERSION;
|
||||||
app.log.info(`Game data loaded: ${stats.items} items, ${stats.recipes} recipes.`);
|
if (!empty && !stale) return;
|
||||||
}
|
|
||||||
|
app.log.info(
|
||||||
|
empty
|
||||||
|
? 'No game data found — downloading and parsing (first boot)...'
|
||||||
|
: `Game data parsed by an older version (v${storedVersion} → v${PARSER_VERSION}) — re-parsing...`,
|
||||||
|
);
|
||||||
|
await downloadGameData(false); // uses cached files if already present
|
||||||
|
const stats = await parseGameData();
|
||||||
|
app.log.info(`Game data loaded: ${stats.items} items, ${stats.recipes} recipes.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
app.log.error(`First-boot game data load failed: ${e instanceof Error ? e.message : e}`);
|
app.log.error(`Startup game data load failed: ${e instanceof Error ? e.message : e}`);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue