Fail fast on missing terrain assets and recover empty renders

This commit is contained in:
2026-10-04 22:32:11 +05:00
parent 79d0f53a79
commit 763cf8713c
15 changed files with 842 additions and 21 deletions

View File

@@ -3,7 +3,8 @@ import { createServer } from "node:http";
import { readFile, writeFile, mkdir } from "node:fs/promises";
import { resolve, sep } from "node:path";
import sharp from "sharp";
import { assetsRoot } from "../scripts/assets.mjs";
import { assetsRoot, prepareAssets } from "../scripts/assets.mjs";
import { assertTerrainImage } from "./image-validation.mjs";
/** @param {import('../shared/protocol').TerrainSnapshot} snapshot
* @param {Array<{id:string,x:number,y:number,z:number}>} viewpoints
* @param {string} output
@@ -15,6 +16,12 @@ export async function renderSnapshot(
{ size = 1024, signal, onProgress } = {},
) {
const start = Date.now();
if (signal?.aborted) throw new Error("Rendering cancelled");
await onProgress?.({
percent: 22,
message: "Checking Minecraft models and textures",
});
await prepareAssets();
await mkdir(output, { recursive: true });
const server = createServer(async (req, res) => {
try {
@@ -126,6 +133,10 @@ export async function renderSnapshot(
rendererPage.on("pageerror", (err) =>
console.error("Renderer:", err.message),
);
rendererPage.on("console", (message) => {
if (["warning", "error"].includes(message.type()))
console.error(`Renderer ${message.type()}: ${message.text()}`);
});
await rendererPage.goto(`http://127.0.0.1:${server.address().port}`);
await rendererPage.waitForFunction(() => window.rendererReady);
return rendererPage;
@@ -169,6 +180,8 @@ export async function renderSnapshot(
{ p, face, size },
);
const encoded = Buffer.from(data.split(",")[1], "base64");
if (face === "ny")
await assertTerrainImage(encoded, `Panorama ${p.id}, downward face`);
// Chromium encodes WebP once; retain a PNG fallback for canvas runtimes
// without WebP support rather than silently writing the wrong MIME type.
if (data.startsWith("data:image/webp;"))
@@ -191,6 +204,7 @@ export async function renderSnapshot(
await report({ percent: 85, message: "Rendering the surface map" });
const map = await page.evaluate(() => window.renderMap(1024));
const mapBytes = Buffer.from(map.split(",")[1], "base64");
await assertTerrainImage(mapBytes, "Surface map");
if (map.startsWith("data:image/webp;"))
await writeFile(`${output}/map.webp`, mapBytes);
else