113 lines
4.2 KiB
JavaScript
113 lines
4.2 KiB
JavaScript
import { Worker } from "node:worker_threads";
|
|
import { performance } from "node:perf_hooks";
|
|
import { generateMap } from "../src/mapPipeline.js";
|
|
import { createWorldMap } from "../src/worldMap.js";
|
|
import { buildMaximumProductionLasso, derivePatchSeed } from "./production-fixtures.mjs";
|
|
|
|
const budgetMs = Math.max(1, Number(process.env.PATCH_MAX_WORKER_BUDGET_MS) || 60_000);
|
|
const timeoutMs = Math.max(90_000, budgetMs + 30_000);
|
|
const worldSeed = Number(process.env.PATCH_TEST_WORLD_SEED ?? 114514) >>> 0;
|
|
const candidateVariant = Number(process.env.PATCH_TEST_VARIANT ?? 0) >>> 0;
|
|
|
|
const initialStartedAt = performance.now();
|
|
const initial = generateMap(worldSeed);
|
|
const initialGenerationMs = Math.round(performance.now() - initialStartedAt);
|
|
const world = createWorldMap(initial);
|
|
const rect = buildMaximumProductionLasso(world);
|
|
|
|
const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || "auto";
|
|
const seed = derivePatchSeed(world.seed, terrainType, candidateVariant);
|
|
const candidatePlan = [{ candidateId: `max-worker:${candidateVariant}`, candidateOrdinal: 1, variant: candidateVariant, seed }];
|
|
const worker = new Worker(new URL("./browser-worker-node-shim.mjs", import.meta.url), { type: "module" });
|
|
const startedAt = performance.now();
|
|
let progressEvents = 0;
|
|
let maxRssBytes = process.memoryUsage().rss;
|
|
let lastLabel = "";
|
|
const memoryTimer = setInterval(() => {
|
|
const memory = process.memoryUsage();
|
|
maxRssBytes = Math.max(maxRssBytes, memory.rss);
|
|
}, 100);
|
|
|
|
async function finish(exitCode, payload) {
|
|
clearInterval(memoryTimer);
|
|
clearTimeout(timeoutTimer);
|
|
try { await worker.terminate(); } catch {}
|
|
const elapsedMs = Math.round(performance.now() - startedAt);
|
|
const finalMerge = payload?.candidateQuality?.finalMerge || null;
|
|
const qualityPass = payload?.candidateQuality?.hardPass === true && finalMerge?.hardPass === true;
|
|
const seamPass = payload?.seamDiagnostics?.hardPass === true;
|
|
const withinBudget = elapsedMs < budgetMs;
|
|
const ok = payload?.ok === true && payload?.searchStatus === "succeeded" && qualityPass && seamPass && withinBudget;
|
|
console.log(JSON.stringify({
|
|
ok,
|
|
worldSeed,
|
|
candidateVariant,
|
|
workerResultOk: payload?.ok === true,
|
|
initialGenerationMs,
|
|
elapsedMs,
|
|
budgetMs,
|
|
withinBudget,
|
|
maxRssBytes,
|
|
progressEvents,
|
|
lastLabel,
|
|
searchStatus: payload?.searchStatus || null,
|
|
terminalCode: payload?.terminalCode || payload?.code || null,
|
|
qualityPass,
|
|
seamPass,
|
|
finalMerge,
|
|
humanTerrainReconciliation: payload?.seamDiagnostics?.humanTerrainReconciliation || null,
|
|
error: payload?.error || null,
|
|
reason: payload?.reason || null,
|
|
}, null, 2));
|
|
process.exit(ok ? 0 : exitCode || 1);
|
|
}
|
|
|
|
const timeoutTimer = setTimeout(() => finish(2, { ok: false, error: `Timed out after ${timeoutMs} ms.` }), timeoutMs);
|
|
worker.on("message", (message) => {
|
|
if (message.id !== 1) return;
|
|
if (message.type === "progress") {
|
|
progressEvents++;
|
|
const progress = message.progress || {};
|
|
const label = String(progress.label || "");
|
|
if (label && label !== lastLabel && (/Large expansion tile|finalization complete/i.test(label))) {
|
|
lastLabel = label;
|
|
console.error(`[max-worker] ${Math.round(performance.now() - startedAt)} ms: ${label}`);
|
|
}
|
|
return;
|
|
}
|
|
finish(message.ok ? 1 : 3, {
|
|
ok: message.ok,
|
|
code: message.code,
|
|
error: message.error,
|
|
searchStatus: message.result?.searchStatus,
|
|
terminalCode: message.result?.code,
|
|
reason: message.result?.reason,
|
|
candidateQuality: message.result?.candidateQuality,
|
|
seamDiagnostics: message.result?.seamDiagnostics,
|
|
});
|
|
});
|
|
worker.on("error", (error) => finish(3, { ok: false, error: error?.stack || String(error) }));
|
|
worker.postMessage({
|
|
id: 1,
|
|
world,
|
|
rect,
|
|
options: {
|
|
patchMode: "expansion",
|
|
terrainType,
|
|
variant: candidateVariant,
|
|
seed,
|
|
maxQualityRetries: 0,
|
|
qualityTerrainAttempts: 1,
|
|
acceptBestAvailableQuality: false,
|
|
includeSeamVisualization: false,
|
|
},
|
|
search: {
|
|
searchId: "max-worker-benchmark",
|
|
operationId: "max-worker-benchmark",
|
|
committedRevision: 1,
|
|
workerEpoch: 1,
|
|
executionAttempt: 1,
|
|
totalCandidateCount: 1,
|
|
candidatePlan,
|
|
},
|
|
});
|