146 lines
4.5 KiB
JavaScript
146 lines
4.5 KiB
JavaScript
#!/usr/bin/env node
|
|
import { spawn } from "node:child_process";
|
|
import { access, readFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const sourceFile = path.join(
|
|
projectRoot,
|
|
"dungeon-pipeline",
|
|
"epoch-five-player-instances.json",
|
|
);
|
|
|
|
const exists = async (file) => {
|
|
try {
|
|
await access(file);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
const readJson = async (file) => JSON.parse(await readFile(file, "utf8"));
|
|
const slugPart = (value) => String(value ?? "")
|
|
.normalize("NFKD")
|
|
.replace(/[\u0300-\u036f]/g, "")
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, "-")
|
|
.replace(/^-|-$/g, "") || "unknown";
|
|
|
|
function run(script, argumentsList) {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(process.execPath, [script, ...argumentsList], {
|
|
cwd: projectRoot,
|
|
env: process.env,
|
|
stdio: "inherit",
|
|
windowsHide: true,
|
|
});
|
|
child.once("error", reject);
|
|
child.once("exit", (code) => (
|
|
code === 0
|
|
? resolve()
|
|
: reject(new Error(`${path.basename(script)} ${argumentsList.join(" ")} exited ${code}.`))
|
|
));
|
|
});
|
|
}
|
|
|
|
const source = await readJson(sourceFile);
|
|
const requested = new Set(process.argv.slice(2).filter((argument) => argument !== "--force"));
|
|
const force = process.argv.includes("--force");
|
|
const dungeons = source.dungeons.filter((dungeon) => (
|
|
!requested.size || requested.has(dungeon.slug)
|
|
));
|
|
if (!dungeons.length) throw new Error("No requested Epoch dungeon slugs were found.");
|
|
if (!process.env[source.source.rootEnvironmentVariable] && !process.env.EPOCH_CLIENT_ROOT) {
|
|
throw new Error(`${source.source.rootEnvironmentVariable} must point to the installed Epoch client.`);
|
|
}
|
|
|
|
const dungeonCli = path.join(projectRoot, "scripts", "dungeon-pipeline", "cli.mjs");
|
|
const scaffoldCli = path.join(
|
|
projectRoot,
|
|
"scripts",
|
|
"manastorm-assets",
|
|
"scaffold-converted-import.mjs",
|
|
);
|
|
const importCli = path.join(
|
|
projectRoot,
|
|
"scripts",
|
|
"manastorm-assets",
|
|
"import-existing-stage.mjs",
|
|
);
|
|
const manastormCli = path.join(
|
|
projectRoot,
|
|
"scripts",
|
|
"manastorm-pipeline",
|
|
"cli.mjs",
|
|
);
|
|
const reports = [];
|
|
|
|
for (const dungeon of dungeons) {
|
|
const manastormSlug = `${dungeon.mapId}-${slugPart(dungeon.mapDirectory)}`;
|
|
const shippingManifest = path.join(
|
|
projectRoot,
|
|
"public",
|
|
"assets",
|
|
"game",
|
|
"manastorm",
|
|
manastormSlug,
|
|
"stage-pack.json",
|
|
);
|
|
if (!force && await exists(shippingManifest)) {
|
|
reports.push({ slug: dungeon.slug, manastormSlug, status: "already-built" });
|
|
continue;
|
|
}
|
|
try {
|
|
const dungeonWork = path.join(projectRoot, "..", "HealerMan-Storage", "pipeline-work", "dungeons", dungeon.slug);
|
|
const resumableStages = [
|
|
{
|
|
command: "extract",
|
|
outputs: [path.join(dungeonWork, "source-export", "automation-result.json")],
|
|
},
|
|
{
|
|
command: "convert",
|
|
outputs: [path.join(dungeonWork, "staging", "environment", "conversion-report.json")],
|
|
},
|
|
{
|
|
command: "optimize",
|
|
outputs: [path.join(dungeonWork, "staging", "environment", "optimization-report.json")],
|
|
},
|
|
{
|
|
command: "navmesh-conversion",
|
|
outputs: [
|
|
path.join(dungeonWork, "recast-report.json"),
|
|
path.join(dungeonWork, "staging", `${dungeon.slug}-navigation.glb`),
|
|
],
|
|
},
|
|
];
|
|
for (const { command, outputs } of resumableStages) {
|
|
if (!force && (await Promise.all(outputs.map(exists))).every(Boolean)) continue;
|
|
await run(dungeonCli, [command, dungeon.slug]);
|
|
}
|
|
await run(scaffoldCli, [manastormSlug, dungeon.slug]);
|
|
await run(importCli, [manastormSlug]);
|
|
await run(manastormCli, ["optimize", manastormSlug]);
|
|
await run(manastormCli, ["pack", manastormSlug]);
|
|
reports.push({ slug: dungeon.slug, manastormSlug, status: "green" });
|
|
} catch (error) {
|
|
reports.push({
|
|
slug: dungeon.slug,
|
|
manastormSlug,
|
|
status: "error",
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}
|
|
|
|
const failed = reports.filter((report) => report.status === "error");
|
|
console.log(JSON.stringify({
|
|
status: failed.length ? "error" : "green",
|
|
dungeons: reports.length,
|
|
built: reports.filter((report) => report.status === "green").length,
|
|
skipped: reports.filter((report) => report.status === "already-built").length,
|
|
failed,
|
|
reports,
|
|
}, null, 2));
|
|
if (failed.length) process.exitCode = 1;
|