Online co-op browser remake of Arknights' Stronghold Protocol: Alliance (client-simulated combat, official waves/bonds/items/strategies, 3D board, facing wheel, operator loadout, official emotes, deployment scripts).
65 lines
2.5 KiB
JavaScript
65 lines
2.5 KiB
JavaScript
// server/sim/nodeData.js — the Node-only default data loader of the simulation (DESIGN §14).
|
|
//
|
|
// server/sim/** is shared with browsers (served read-only at /sim/), so every Node API lives here. simdata.js imports
|
|
// this module dynamically — and only under Node — to build its default DataSource:
|
|
// * generated data: `server/data.js → getData()` (data/*.json, tools/build-data.mjs),
|
|
// * research fallback: docs/research/03-operators.json, 05-enemies.json, 05-maps.json (sim tests before/without data).
|
|
// Browsers never load this file: they inject the fetched /data/*.json with simdata.js setSimData(data).
|
|
//
|
|
// This module must not import simdata.js (simdata awaits this module at its top level; a cycle would deadlock).
|
|
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
/** Repository root. */
|
|
export const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
|
|
const QUIET = Object.freeze({ warn() {}, error() {}, info() {} });
|
|
|
|
/** data/*.json through the server/data.js singleton (null when unavailable). */
|
|
export async function loadGenerated() {
|
|
try {
|
|
const mod = await import('../data.js');
|
|
return mod.getData({ log: QUIET });
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Drop the server/data.js singleton, then load again (tests / hot reload after data regeneration). */
|
|
export async function reloadGenerated() {
|
|
try {
|
|
const mod = await import('../data.js');
|
|
mod.resetData?.();
|
|
} catch { /* ignore */ }
|
|
return loadGenerated();
|
|
}
|
|
|
|
/**
|
|
* Research JSON in the raw-map shape a DataSource accepts: `{ chess, enemies, tokens, stages, waves }` (tokens carry
|
|
* `byOwner[chessId]` variants; stages get the research legend).
|
|
*/
|
|
export function loadResearch() {
|
|
const read = (f) => {
|
|
try { return JSON.parse(fs.readFileSync(path.join(ROOT, 'docs', 'research', f), 'utf8')); } catch { return null; }
|
|
};
|
|
const ops = read('03-operators.json');
|
|
const en = read('05-enemies.json');
|
|
const maps = read('05-maps.json');
|
|
const chess = {};
|
|
const tokens = {};
|
|
for (const c of ops?.chess ?? []) {
|
|
chess[c.chessId] = c;
|
|
for (const t of c.tokens ?? []) {
|
|
if (!tokens[t.tokenId]) tokens[t.tokenId] = { ...t, byOwner: {} };
|
|
tokens[t.tokenId].byOwner[c.chessId] = t;
|
|
}
|
|
}
|
|
const stages = {};
|
|
for (const [id, s] of Object.entries(maps?.stages ?? {})) {
|
|
stages[id] = { ...s, legend: maps?._meta?.legend ?? null };
|
|
}
|
|
return { chess, enemies: en?.enemies ?? {}, tokens, stages, waves: maps?.roundLevels ?? {} };
|
|
}
|