npm workspaces (Fastify+TS server, Vite+React web), SQLite schema, ao-bin-dumps downloader and parser (items, recipes, enchant upgrades, EN localization), item browser UI and debug API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { pipeline } from 'node:stream/promises';
|
|
import { Readable } from 'node:stream';
|
|
import { AO_BIN_DUMPS_BASE, GAMEDATA_DIR } from '../config.js';
|
|
|
|
/** Files pulled from ao-data/ao-bin-dumps. Add zone/world files in the travel phase. */
|
|
export const GAMEDATA_FILES = [
|
|
{ remote: 'items.json', local: 'items.json' },
|
|
{ remote: 'formatted/items.json', local: 'items_localized.json' },
|
|
] as const;
|
|
|
|
export function gamedataFilePath(local: string): string {
|
|
return path.join(GAMEDATA_DIR, local);
|
|
}
|
|
|
|
async function downloadFile(remote: string, dest: string): Promise<void> {
|
|
const url = `${AO_BIN_DUMPS_BASE}/${remote}`;
|
|
const res = await fetch(url);
|
|
if (!res.ok || !res.body) {
|
|
throw new Error(`Download failed for ${url}: HTTP ${res.status}`);
|
|
}
|
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
const tmp = `${dest}.download`;
|
|
await pipeline(Readable.fromWeb(res.body as import('node:stream/web').ReadableStream), fs.createWriteStream(tmp));
|
|
fs.renameSync(tmp, dest);
|
|
}
|
|
|
|
export interface DownloadResult {
|
|
file: string;
|
|
status: 'downloaded' | 'cached';
|
|
bytes: number;
|
|
}
|
|
|
|
/**
|
|
* Ensure all game data files exist locally. With force=true, re-download everything
|
|
* (used by the "refresh game data" action after a game patch).
|
|
*/
|
|
export async function downloadGameData(force = false): Promise<DownloadResult[]> {
|
|
const results: DownloadResult[] = [];
|
|
for (const f of GAMEDATA_FILES) {
|
|
const dest = gamedataFilePath(f.local);
|
|
if (!force && fs.existsSync(dest)) {
|
|
results.push({ file: f.local, status: 'cached', bytes: fs.statSync(dest).size });
|
|
continue;
|
|
}
|
|
await downloadFile(f.remote, dest);
|
|
results.push({ file: f.local, status: 'downloaded', bytes: fs.statSync(dest).size });
|
|
}
|
|
return results;
|
|
}
|