The pane this build runs in does not composite, so requestAnimationFrame never fires, the loop stalls, and nothing can be screenshotted from outside. Adds a `?debug=1` handle that steps the simulation by hand and renders on demand, plus a dev-only endpoint that takes a POSTed frame and writes it to shots/. Moving the image as bytes rather than a pasted data URL matters: a truncated base64 string decodes to nothing. Looking at the result immediately found a real problem. Base beacons were wide translucent cylinders — legible from a distance, a solid green wall from inside, and the car parks in the middle of one. Now a slim pillar plus a ring on the ground that marks where to stop. Also raised the sky and fog brightness, which was dark enough to read as night. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
import type { Plugin } from 'vite';
|
|
import { defineConfig } from 'vite';
|
|
|
|
/**
|
|
* Dev-only: lets the running game POST a rendered frame to disk.
|
|
*
|
|
* The game cannot always be looked at — a backgrounded or non-compositing tab
|
|
* never fires requestAnimationFrame, so nothing draws and nothing can be
|
|
* screenshotted from outside. With this, plus the `?debug=1` handle in
|
|
* src/debug.ts, a script can step the simulation, render a frame, and drop the
|
|
* image somewhere it can actually be opened.
|
|
*
|
|
* Never registered in a production build.
|
|
*/
|
|
function screenshotEndpoint(): Plugin {
|
|
const directory = resolve(process.cwd(), 'shots');
|
|
|
|
return {
|
|
name: 'dbtl-screenshot',
|
|
apply: 'serve',
|
|
configureServer(server) {
|
|
server.middlewares.use('/__shot', (req, res) => {
|
|
if (req.method !== 'POST') {
|
|
res.statusCode = 405;
|
|
res.end('POST only');
|
|
return;
|
|
}
|
|
const name = (new URL(req.url ?? '', 'http://x').searchParams.get('name') ?? 'frame')
|
|
// Keep this from writing outside the shots directory.
|
|
.replace(/[^a-z0-9_-]/gi, '');
|
|
|
|
const chunks: Buffer[] = [];
|
|
req.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
req.on('end', () => {
|
|
try {
|
|
mkdirSync(directory, { recursive: true });
|
|
const file = resolve(directory, `${name || 'frame'}.jpg`);
|
|
writeFileSync(file, Buffer.concat(chunks));
|
|
res.setHeader('content-type', 'application/json');
|
|
res.end(JSON.stringify({ ok: true, file, bytes: Buffer.concat(chunks).length }));
|
|
} catch (err) {
|
|
res.statusCode = 500;
|
|
res.end(String(err));
|
|
}
|
|
});
|
|
});
|
|
},
|
|
};
|
|
}
|
|
|
|
export default defineConfig({
|
|
plugins: [screenshotEndpoint()],
|
|
});
|