The first commit, the core and all files is adding to repo

This commit is contained in:
2026-10-04 14:20:13 +05:00
commit 53d7b263c4
94 changed files with 10668 additions and 0 deletions

48
scripts/assets.mjs Normal file
View File

@@ -0,0 +1,48 @@
import { mkdir, readFile, writeFile, access } from "node:fs/promises";
import { createHash } from "node:crypto";
import { unzipSync } from "fflate";
export const assetsRoot = ".cache/minecraft/1.21.11";
export async function prepareAssets() {
try {
await access(`${assetsRoot}/assets/minecraft/blockstates/grass_block.json`);
return;
} catch {}
const manifest = await (
await fetch(
"https://piston-meta.mojang.com/mc/game/version_manifest_v2.json",
)
).json();
const entry = manifest.versions.find((v) => v.id === "1.21.11");
if (!entry) throw new Error("Official Minecraft 1.21.11 metadata missing");
const version = await (await fetch(entry.url)).json();
await mkdir(".cache/minecraft", { recursive: true });
const jarPath = ".cache/minecraft/client-1.21.11.jar";
let jar;
try {
jar = await readFile(jarPath);
} catch {
const response = await fetch(version.downloads.client.url);
if (!response.ok) throw new Error("Minecraft asset download failed");
jar = Buffer.from(await response.arrayBuffer());
await writeFile(jarPath, jar);
}
if (
createHash("sha1").update(jar).digest("hex") !==
version.downloads.client.sha1
)
throw new Error("Minecraft client checksum mismatch");
const entries = unzipSync(jar, {
filter: (f) =>
/^assets\/minecraft\/(models|blockstates|textures\/block)\//.test(
f.name,
) && /\.(json|png)$/.test(f.name),
});
for (const [name, bytes] of Object.entries(entries)) {
const path = `${assetsRoot}/${name}`;
await mkdir(path.slice(0, path.lastIndexOf("/")), { recursive: true });
await writeFile(path, bytes);
}
console.log(
`Prepared ${Object.keys(entries).length} verified vanilla model/texture assets.`,
);
}

37
scripts/clean-build.mjs Normal file
View File

@@ -0,0 +1,37 @@
import { resolve } from "node:path";
import { existsSync } from "node:fs";
import { rm } from "node:fs/promises";
import { spawn } from "node:child_process";
// OneDrive marks directories as Windows reparse points, which Next's Node
// cleanup may mistake for links. Use native PowerShell for this build cache.
const root = resolve("."),
target = resolve(root, ".next");
if (target !== resolve(root, ".next") || !target.startsWith(root))
throw new Error("Unsafe build cache target");
if (existsSync(target)) {
if (process.platform === "win32")
await new Promise((accept, reject) => {
const escaped = target.replaceAll("'", "''");
const child = spawn(
"powershell.exe",
[
"-NoProfile",
"-NonInteractive",
"-Command",
`Remove-Item -LiteralPath '${escaped}' -Recurse -Force -ErrorAction Stop`,
],
{ stdio: "inherit", windowsHide: true },
);
child.on("exit", (code) =>
code === 0
? accept()
: reject(
new Error(
"Could not clear the Next build cache. Stop the dev server first.",
),
),
);
child.on("error", reject);
});
else await rm(target, { recursive: true, force: true });
}

13
scripts/fabric-proof.mjs Normal file
View File

@@ -0,0 +1,13 @@
import {spawn} from 'node:child_process';
import {readFile,writeFile,rename} from 'node:fs/promises';
import {existsSync} from 'node:fs';
const directory='.cache/paper-proof',plugin=`${directory}/plugins/minegeo.jar`,disabled=plugin+'.disabled';
if(existsSync(plugin))await rename(plugin,disabled);
const previous=await readFile(`${directory}/server.properties`,'utf8');
await writeFile(`${directory}/server.properties`,previous.replace('online-mode=true','online-mode=false'));
let client,server,success=false;const points=[[0,100,20],[-20,100,20],[20,100,-20],[-20,100,-20],[30,100,0]];
try{
await new Promise((accept,reject)=>{server=spawn('java',['-Xmx2G','-jar','paper.jar','--nogui'],{cwd:directory,stdio:['pipe','pipe','pipe'],windowsHide:true});let started=false;let tail='';const output=data=>{process.stdout.write(data);tail=(tail+data.toString()).slice(-6000);if(!started&&tail.includes('Done (')){started=true;server.stdin.write('setworldspawn 0 100 20\n');accept();}if(data.toString().includes('MineGeoTest joined'))server.stdin.write('gamemode creative MineGeoTest\ntp MineGeoTest 0 100 20\n');};server.stdout.on('data',output);server.stderr.on('data',output);server.on('exit',code=>{if(!started)reject(new Error('Fixture server exited '+code));});setTimeout(()=>{if(!started)reject(new Error('Fixture server startup timed out'));},120000).unref();});
await new Promise((accept,reject)=>{client=spawn(process.platform==='win32'?'gradlew.bat':'./gradlew',[':fabric:runCaptureTest'],{cwd:'minecraft',stdio:['pipe','pipe','pipe'],shell:process.platform==='win32',windowsHide:true});let seen=new Set();const output=data=>{process.stdout.write(data);const text=data.toString();for(let i=1;i<=4;i++)if(text.includes('MINEGEO_TEST_CAPTURE_'+i)&&!seen.has(i)){seen.add(i);const p=points[i];server.stdin.write(`tp MineGeoTest ${p.join(' ')}\n`);}if(text.includes('MINEGEO_TEST_PASS'))success=true;};client.stdout.on('data',output);client.stderr.on('data',output);client.on('exit',code=>success&&code===0?accept():reject(new Error('Fabric capture test did not pass: '+code)));const timer=setTimeout(()=>{client.kill();reject(new Error('Fabric fixture timed out'));},360000);timer.unref();});
console.log('Fabric captured five real panoramas on a server without MineGeo installed, and restored settings on success/cancellation.');
}finally{server?.stdin.write('stop\n');await writeFile(`${directory}/server.properties`,previous);if(existsSync(disabled))await rename(disabled,plugin);}

24
scripts/java-build.mjs Normal file
View File

@@ -0,0 +1,24 @@
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { mkdir, copyFile } from "node:fs/promises";
const windows = process.platform === "win32";
const command = windows ? "gradlew.bat" : "./gradlew";
const targets = process.argv.slice(2);
if (!targets.length) targets.push(":plugin:build", ":fabric:build");
await new Promise((resolve, reject) => {
const child = spawn(command, targets, {
cwd: "minecraft",
stdio: "inherit",
shell: windows,
});
child.on("error", reject);
child.on("exit", (code) =>
code === 0 ? resolve() : reject(new Error(`Gradle exited ${code}`)),
);
});
await mkdir("public/downloads", { recursive: true });
for (const name of ["plugin", "fabric"]) {
const file = `minegeo-${name}-1.21.11.jar`,
path = `minecraft/${name}/build/libs/${file}`;
if (existsSync(path)) await copyFile(path, `public/downloads/${file}`);
}

113
scripts/proof.mjs Normal file
View File

@@ -0,0 +1,113 @@
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));

21
scripts/seed-proof.ts Normal file
View File

@@ -0,0 +1,21 @@
import {createClient} from '@supabase/supabase-js';
import {createHash,randomBytes} from 'node:crypto';
import {mkdir,readFile,writeFile} from 'node:fs/promises';
import {existsSync} from 'node:fs';
process.loadEnvFile('.env.local');
const client=createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!,process.env.SUPABASE_SECRET_KEY!,{auth:{persistSession:false}});
function check<T>(r:{data:T,error:{message:string}|null}):NonNullable<T>{if(r.error)throw new Error(r.error.message);return r.data!;}
await mkdir('.data',{recursive:true});
if(!existsSync('.data/test-account.json')){
const password=randomBytes(32).toString('hex'),token='mg_'+randomBytes(32).toString('hex'),email='minegeo-fixture-'+randomBytes(4).toString('hex')+'@example.com';
const {user}=check(await client.auth.admin.createUser({email,password,email_confirm:true}));if(!user)throw new Error('No fixture account returned');
check(await client.from('profiles').insert({id:user.id,display_name:'MineGeo test world',role:'moderator'}));
check(await client.from('contributor_tokens').insert({owner_id:user.id,token_hash:createHash('sha256').update(token).digest('hex'),label:'Local integration fixture'}));
await writeFile('.data/test-account.json',JSON.stringify({id:user.id,email,password,token}));
}
const manifest=JSON.parse(await readFile('.cache/paper-proof/plugins/MineGeo/export/manifest.json','utf8'));
manifest.name='The First Expedition';manifest.serverName='MineGeo Test World';manifest.description='A real Paper 1.21.11 renderer test world: terrain, water and a builder’s outpost. Exported with no connected capture player. This small map is for trying MineGeo.';
await mkdir('.data/proof-upload',{recursive:true});
await writeFile('.data/proof-upload/manifest.json',JSON.stringify(manifest));
await writeFile('.data/proof-upload/terrain.json.gz',await readFile('.cache/paper-proof/plugins/MineGeo/export/terrain.json.gz'));
console.log('Fixture contributor and real Paper upload are ready. Credentials remain in ignored local files.');

32
scripts/setup.mjs Normal file
View File

@@ -0,0 +1,32 @@
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { copyFile, mkdir } from "node:fs/promises";
import { prepareAssets } from "./assets.mjs";
if (!existsSync(".env.local")) await copyFile(".env.example", ".env.local");
await mkdir(".data", { recursive: true });
await mkdir("public/generated", { recursive: true });
await mkdir("public/downloads", { recursive: true });
await new Promise((resolve, reject) => {
const p = spawn(
process.execPath,
["node_modules/playwright/cli.js", "install", "chromium"],
{ stdio: "inherit" },
);
p.on("exit", (code) =>
code === 0
? resolve()
: reject(new Error(`Chromium installation exited ${code}`)),
);
});
await prepareAssets();
process.loadEnvFile(".env.local");
const configured = Boolean(
process.env.NEXT_PUBLIC_SUPABASE_URL &&
process.env.SUPABASE_SECRET_KEY &&
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,
);
console.log(
configured
? "Supabase configuration present. Apply the checked-in migration before starting."
: "Supabase is not configured. Local guest sandbox is available; hosted uploads/moderation remain disabled.",
);

37
scripts/supervise.mjs Normal file
View File

@@ -0,0 +1,37 @@
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
if (existsSync(".env.local")) process.loadEnvFile(".env.local");
const dev = process.argv[2] === "dev";
const children = [];
let stopping = false;
function run(args) {
const c = spawn(process.execPath, args, {
stdio: "inherit",
env: process.env,
});
children.push(c);
c.on("exit", (code) => {
if (!stopping) {
console.error(
`MineGeo process exited (${code}). Shutting down companion process.`,
);
stop(code ?? 1);
}
});
return c;
}
function stop(code = 0) {
if (stopping) return;
stopping = true;
for (const c of children) c.kill("SIGTERM");
setTimeout(() => process.exit(code), 500).unref();
}
process.on("SIGINT", () => stop());
process.on("SIGTERM", () => stop());
run([
"node_modules/next/dist/bin/next",
dev ? "dev" : "start",
"--hostname",
process.env.MINEGEO_HOST || "127.0.0.1",
]);
run(["--import", "tsx", "worker/main.ts"]);