import { readFile, writeFile, mkdir, copyFile } from "node:fs/promises"; import { existsSync } from "node:fs"; import { createHash } from "node:crypto"; import { spawn } from "node:child_process"; import { gunzipSync } from "node:zlib"; import { renderSnapshot } from "../renderer/render.mjs"; import { prepareAssets } from "./assets.mjs"; const directory = ".cache/paper-proof"; await mkdir(`${directory}/plugins`, { recursive: true }); const jar = `${directory}/paper.jar`; if (!existsSync(jar)) { const builds = await ( await fetch( "https://fill.papermc.io/v3/projects/paper/versions/1.21.11/builds", { headers: { "User-Agent": "MineGeo/0.1 local development" } }, ) ).json(); const build = builds.find((b) => b.channel === "STABLE"); const download = build.downloads["server:default"]; const bytes = Buffer.from(await (await fetch(download.url)).arrayBuffer()); if ( createHash("sha256").update(bytes).digest("hex") !== download.checksums.sha256 ) throw new Error("Paper checksum mismatch"); await writeFile(jar, bytes); } await copyFile( "minecraft/plugin/build/libs/minegeo-plugin-1.21.11.jar", `${directory}/plugins/minegeo.jar`, ); // This disposable loopback-only server exists solely for the requested integration test. await writeFile(`${directory}/eula.txt`, "eula=true\n"); await writeFile( `${directory}/server.properties`, "server-ip=127.0.0.1\nserver-port=25579\nonline-mode=true\nlevel-seed=928341\nview-distance=5\nsimulation-distance=2\nspawn-protection=0\nenable-rcon=false\nmax-players=1\n", ); if (!existsSync(`${directory}/plugins/MineGeo/export/terrain.json.gz`)) { await new Promise((resolve, reject) => { const child = spawn("java", ["-Xmx2G", "-jar", "paper.jar", "--nogui"], { cwd: directory, stdio: ["pipe", "pipe", "pipe"], }); let ready = false, exported = false, buffer = ""; const timer = setTimeout(() => { child.stdin.write("stop\n"); reject(new Error("Paper proof timed out")); }, 240000); process.once("SIGINT", () => child.stdin.write("stop\n")); const output = (data) => { const text = data.toString(); process.stdout.write(text); buffer = (buffer + text).slice(-20000); if (!ready && buffer.includes("Done (")) { ready = true; // The fixture explicitly generates its test area before export. The production // plugin never generates chunks. No player connects during this test. child.stdin.write("forceload add -48 -48 47 47\n"); setTimeout( () => child.stdin.write( "fill -47 99 -47 47 99 47 minecraft:grass_block\nfill -6 100 -6 6 103 6 minecraft:stone_bricks\nfill -5 104 -5 5 105 5 minecraft:oak_planks\nfill 16 100 16 24 100 24 minecraft:water\nminegeo area world -48 -48 48 48\nminegeo export Paper Proof\n", ), 12000, ); } if (!exported && buffer.includes("Export complete:")) { exported = true; child.stdin.write("stop\n"); } if (!exported && text.includes("Export failed:")) child.stdin.write("stop\n"); }; child.stdout.on("data", output); child.stderr.on("data", output); child.on("exit", (code) => { clearTimeout(timer); exported ? resolve() : reject(new Error(`Paper exited ${code} without an export`)); }); }); } const source = `${directory}/plugins/MineGeo/export`; const manifest = JSON.parse(await readFile(`${source}/manifest.json`, "utf8")); const terrain = JSON.parse( gunzipSync(await readFile(`${source}/terrain.json.gz`)).toString(), ); await prepareAssets(); const report = await renderSnapshot( terrain, manifest.viewpoints, "public/generated/paper-proof", { size: 1024 }, ); await mkdir(".data/fixtures", { recursive: true }); await copyFile(`${source}/manifest.json`, ".data/fixtures/paper-proof.json"); await writeFile( "artifacts-renderer-proof.json", JSON.stringify( { ...report, source: "real Paper 1.21.11 terrain snapshot", bounds: manifest.bounds, viewpoints: manifest.viewpoints.length, }, null, 2, ), ); console.log("RENDERER PROOF:", JSON.stringify(report));