import { Worker } from "node:worker_threads"; import { performance } from "node:perf_hooks"; import { generateMap } from "../src/mapPipeline.js"; import { createWorldMap } from "../src/worldMap.js"; 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; function deriveSeed(worldSeed, terrainType, variant) { let h = (worldSeed >>> 0) ^ 0x9e3779b9; h = Math.imul(h ^ (variant >>> 0), 668265263) >>> 0; for (const ch of String(terrainType || "auto")) h = Math.imul(h ^ ch.charCodeAt(0), 16777619) >>> 0; return h >>> 0; } const initialStartedAt = performance.now(); const initial = generateMap(worldSeed); const initialGenerationMs = Math.round(performance.now() - initialStartedAt); const world = createWorldMap(initial); const width = 470; const height = 333; const x0 = Math.max(0, Math.min(world.width - width, world.originX + 238)); const y0 = Math.max(0, Math.min(world.height - height, world.originY)); const rect = { x0, y0, x1: x0 + width, y1: y0 + height, kind: "lasso" }; const insetX = Math.max(4, Math.floor(width * 0.16)); const insetY = Math.max(4, Math.floor(height * 0.16)); rect.polygon = [ { x: rect.x0 + insetX, y: rect.y0 }, { x: rect.x1 - 1, y: rect.y0 + insetY }, { x: rect.x1 - insetX, y: rect.y1 - 1 }, { x: rect.x0, y: rect.y1 - insetY }, ]; const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || "auto"; const seed = deriveSeed(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 maxHeapBytes = process.memoryUsage().heapUsed; let lastLabel = ""; const memoryTimer = setInterval(() => { const memory = process.memoryUsage(); maxRssBytes = Math.max(maxRssBytes, memory.rss); maxHeapBytes = Math.max(maxHeapBytes, memory.heapUsed); }, 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, maxHeapBytes, 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, }, });