const { chromium } = require('C:/Users/phenom/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright'); const fs = require('node:fs'); const path = require('node:path'); const id = process.argv[2]; if (!id || !/^[a-z0-9-]+$/.test(id)) throw new Error('Provide a dungeon ID for this targeted asset review.'); function option(name) { const index = process.argv.indexOf(name); if (index < 0) return null; if (!process.argv[index + 1]) throw new Error(`${name} requires a value.`); return process.argv[index + 1]; } const override = option('--visual-override'); const hotspotsFile = option('--hotspots'); const runtimePreparation = process.argv.includes('--runtime-preparation'); const sampleMs = Number(option('--sample-ms') || 0); if (!Number.isFinite(sampleMs) || sampleMs < 0 || sampleMs > 10_000) { throw new Error('--sample-ms must be between 0 and 10000.'); } const label = override ? path.basename(override, '.glb') : 'shipping'; const output = path.resolve(option('--output-dir') || `artifacts/playtest/wow-environment-quality/continuation/${id}-${label}${runtimePreparation ? '-runtime-prepared' : ''}`); const hotspots = hotspotsFile ? JSON.parse(fs.readFileSync(hotspotsFile, 'utf8')) : []; fs.mkdirSync(output, { recursive: true }); (async () => { const browser = await chromium.launch({ channel: 'msedge', headless: true }); const page = await browser.newPage({ viewport: { width: 1280, height: 800 } }); const errors = []; page.on('pageerror', (error) => errors.push(error.message)); const params = new URLSearchParams({ dungeon: id }); if (override) params.set('visualOverride', override); if (runtimePreparation) params.set('runtimePreparation', '1'); const baseUrl = process.env.WOW_ENVIRONMENT_REVIEW_URL || 'http://127.0.0.1:4208'; try { await page.goto(`${baseUrl}/scripts/dungeon-pipeline/environment-preview.html?${params}`, { waitUntil: 'domcontentloaded', timeout: 180_000, }); await page.waitForFunction(() => window.wowEnvironmentReview?.ready, null, { timeout: 180_000 }); if (runtimePreparation && !(await page.evaluate(() => window.wowEnvironmentReview.diagnostics().runtimePreparation === true))) { throw new Error('The preview renderer is stale; restart the preview server before runtime-prepared checks.'); } await page.evaluate(() => window.wowEnvironmentReview.setQuality('high')); const ground = await page.evaluate(() => window.wowEnvironmentReview.groundSamples()); const viewpoints = await page.evaluate(() => window.wowEnvironmentReview.viewpoints); const captures = []; async function diagnostics() { if (sampleMs) { await page.evaluate(() => window.wowEnvironmentReview.resetFrameSample()); await page.waitForTimeout(sampleMs); } return page.evaluate((sampled) => ({ ...window.wowEnvironmentReview.diagnostics(), ...(sampled ? { frameSample: window.wowEnvironmentReview.frameSample() } : {}) }), Boolean(sampleMs)); } const indices = [...new Set([0, Math.floor((viewpoints.length - 1) / 2), viewpoints.length - 1])]; for (const index of indices) { for (const [heading, yaw] of [['forward', 0], ['side', Math.PI / 2], ['reverse', Math.PI]]) { await page.evaluate(({ index, yaw }) => window.wowEnvironmentReview.view(index, yaw), { index, yaw }); await page.waitForTimeout(500); const file = `view-${index}-${heading}.png`; await page.screenshot({ path: path.join(output, file) }); captures.push({ name: viewpoints[index].name, heading, file, diagnostics: await diagnostics() }); } } for (const [index, hotspot] of hotspots.entries()) { if (!Array.isArray(hotspot.eye) || !Array.isArray(hotspot.target) || [...hotspot.eye, ...hotspot.target].length !== 6 || ![...hotspot.eye, ...hotspot.target].every(Number.isFinite)) { throw new Error(`Hotspot ${index} requires finite eye and target triples.`); } await page.evaluate(({ eye, target }) => window.wowEnvironmentReview.setCamera(eye, target), hotspot); await page.waitForTimeout(500); const file = `hotspot-${index}.png`; await page.screenshot({ path: path.join(output, file) }); captures.push({ ...hotspot, file, diagnostics: await diagnostics() }); } const result = { scope: 'targeted isolated asset review; no gameplay certification', id, override, runtimePreparation, sampleMs, errors, ground, captures }; fs.writeFileSync(path.join(output, 'results.json'), `${JSON.stringify(result, null, 2)}\n`); if (errors.length) throw new Error(errors.join('\n')); console.log(JSON.stringify({ id, override, output, captures: captures.length, errors })); } catch (error) { await page.screenshot({ path: path.join(output, 'failure.png') }).catch(() => {}); throw error; } finally { await browser.close(); } })().catch((error) => { console.error(error); process.exitCode = 1; });