2026-06-25 18:09:33 +01:00
|
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
|
|
|
import { resolve } from "node:path";
|
|
|
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
|
|
|
|
|
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
|
|
|
const root = resolve(scriptPath, "..", "..");
|
|
|
|
|
const repoRoot = resolve(root, "..");
|
|
|
|
|
|
|
|
|
|
const sources = [
|
|
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonDocuments.cs"),
|
|
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveCommandDocument.cs"),
|
2026-06-28 01:18:21 +01:00
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssetDocuments.cs"),
|
2026-07-05 15:12:29 +01:00
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs"),
|
|
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTradeItemQueries.cs"),
|
2026-06-25 18:09:33 +01:00
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs"),
|
2026-07-05 19:56:18 +01:00
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeGameViewportDocuments.cs"),
|
2026-07-05 01:17:27 +01:00
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeRenderSplatDocuments.cs"),
|
2026-06-25 18:09:33 +01:00
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStarbridgeDocuments.cs"),
|
|
|
|
|
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseAuthorityPolicy.cs"),
|
|
|
|
|
];
|
|
|
|
|
const outputPath = resolve(root, "Electron/aetheria-rts-generated-bindings.ts");
|
|
|
|
|
const check = process.argv.includes("--check");
|
|
|
|
|
|
|
|
|
|
const ipcChannels = [
|
|
|
|
|
["mapViewport", "aetheria-rts:map-viewport"],
|
|
|
|
|
["objectsViewport", "aetheria-rts:objects-viewport"],
|
|
|
|
|
["gravityViewport", "aetheria-rts:gravity-viewport"],
|
2026-07-05 01:17:27 +01:00
|
|
|
["renderSplatsViewport", "aetheria-rts:render-splats-viewport"],
|
2026-06-25 18:09:33 +01:00
|
|
|
["viewportFeed", "aetheria-rts:viewport-feed"],
|
|
|
|
|
["viewportFeedStop", "aetheria-rts:viewport-feed-stop"],
|
|
|
|
|
["viewportFeedUpdate", "aetheria-rts:viewport-feed-update"],
|
|
|
|
|
["selectedObject", "aetheria-rts:selected-object"],
|
|
|
|
|
["inventory", "aetheria-rts:inventory"],
|
|
|
|
|
["daemonHealth", "aetheria-rts:daemon-health"],
|
|
|
|
|
["authorityStatus", "aetheria-rts:authority-status"],
|
|
|
|
|
["starbridgeSession", "aetheria-rts:starbridge-session"],
|
2026-06-28 01:18:21 +01:00
|
|
|
["assetManifest", "aetheria-rts:asset-manifest"],
|
2026-06-25 18:09:33 +01:00
|
|
|
["setMoveVector", "aetheria-rts:set-move-vector"],
|
|
|
|
|
["setTarget", "aetheria-rts:set-target"],
|
|
|
|
|
["surfaceCatalog", "aetheria-rts:surface-catalog"],
|
|
|
|
|
["surfaceCatalogIndex", "aetheria-rts:surface-catalog-index"],
|
2026-07-10 13:37:03 +01:00
|
|
|
["eveSurface", "eve:surface"],
|
2026-07-10 06:48:19 +01:00
|
|
|
["eveProviderAdvertisement", "eve:provider-advertisement"],
|
2026-07-10 07:17:55 +01:00
|
|
|
["eveDocument", "eve:embedded-document"],
|
2026-07-10 13:37:03 +01:00
|
|
|
["submitEveCommand", "eve:submit-command"],
|
|
|
|
|
["windowControl", "eve-electron:window-control"],
|
2026-06-25 18:09:33 +01:00
|
|
|
["health", "aetheria-rts:health"],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const sourceText = sources.map(path => readFileSync(path, "utf8")).join("\n");
|
|
|
|
|
|
|
|
|
|
const enums = [
|
|
|
|
|
{
|
|
|
|
|
exportName: "AetheriaRuntimeDaemonCommandKinds",
|
|
|
|
|
entries: parseEnum(sourceText, "AetheriaRuntimeDaemonCommandKinds"),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "AetheriaRuntimeEveCommandKinds",
|
|
|
|
|
entries: parseEnum(sourceText, "AetheriaRuntimeEveCommandKind"),
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
const documents = [
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeDaemonCommandDocument",
|
|
|
|
|
className: "AetheriaRuntimeDaemonCommandDocument",
|
|
|
|
|
schemaName: "daemonCommand",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeEveCommandDocument",
|
|
|
|
|
className: "AetheriaRuntimeEveCommandDocument",
|
|
|
|
|
schemaName: "eveCommand",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeDaemonFrameDocument",
|
|
|
|
|
className: "AetheriaRuntimeDaemonFrameDocument",
|
|
|
|
|
schemaName: "daemonFrame",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeDaemonHealthDocument",
|
|
|
|
|
className: "AetheriaRuntimeDaemonHealthDocument",
|
|
|
|
|
schemaName: "daemonHealth",
|
|
|
|
|
},
|
2026-06-28 01:18:21 +01:00
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeAssetRef",
|
|
|
|
|
className: "AetheriaRuntimeAssetRef",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeAssetManifestDocument",
|
|
|
|
|
className: "AetheriaRuntimeAssetManifestDocument",
|
|
|
|
|
schemaName: "assetManifest",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeAssetManifestEntry",
|
|
|
|
|
className: "AetheriaRuntimeAssetManifestEntry",
|
|
|
|
|
},
|
2026-06-25 18:09:33 +01:00
|
|
|
{
|
2026-07-05 19:56:18 +01:00
|
|
|
exportName: "aetheriaRuntimeGameViewportDocument",
|
|
|
|
|
className: "AetheriaRuntimeGameViewportDocument",
|
|
|
|
|
schemaName: "gameViewport",
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeObjectsViewportDocument",
|
|
|
|
|
className: "AetheriaRuntimeObjectsViewportDocument",
|
|
|
|
|
schemaName: "objectsViewport",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeGravityViewportDocument",
|
|
|
|
|
className: "AetheriaRuntimeGravityViewportDocument",
|
|
|
|
|
schemaName: "gravityViewport",
|
|
|
|
|
},
|
2026-07-05 01:17:27 +01:00
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRenderSplatsViewportDocument",
|
|
|
|
|
className: "AetheriaRuntimeRenderSplatsViewportDocument",
|
|
|
|
|
schemaName: "renderSplatsViewport",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRenderSplatLayerDefinition",
|
|
|
|
|
className: "AetheriaRuntimeRenderSplatLayerDefinition",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRenderSplatSoa",
|
|
|
|
|
className: "AetheriaRuntimeRenderSplatSoa",
|
|
|
|
|
},
|
2026-06-25 18:09:33 +01:00
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeCurrentZoneDocument",
|
|
|
|
|
className: "AetheriaRuntimeCurrentZoneDocument",
|
|
|
|
|
schemaName: "currentZone",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeCurrentEntityDocument",
|
|
|
|
|
className: "AetheriaRuntimeCurrentEntityDocument",
|
|
|
|
|
schemaName: "currentEntity",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeCurrentDockingDocument",
|
|
|
|
|
className: "AetheriaRuntimeCurrentDockingDocument",
|
|
|
|
|
schemaName: "currentDocking",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStationRefitDocument",
|
|
|
|
|
className: "AetheriaRuntimeStationRefitDocument",
|
|
|
|
|
schemaName: "stationRefit",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStationRefitEntityOption",
|
|
|
|
|
className: "AetheriaRuntimeStationRefitEntityOption",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStationStockItem",
|
|
|
|
|
className: "AetheriaRuntimeStationStockItem",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeSectorMapDocument",
|
|
|
|
|
className: "AetheriaRuntimeSectorMapDocument",
|
|
|
|
|
schemaName: "sectorMap",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeSectorMapZone",
|
|
|
|
|
className: "AetheriaRuntimeSectorMapZone",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeSectorMapLink",
|
|
|
|
|
className: "AetheriaRuntimeSectorMapLink",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeZoneRenderDocument",
|
|
|
|
|
className: "AetheriaRuntimeZoneRenderDocument",
|
|
|
|
|
schemaName: "zoneRender",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeZoneRenderBodyPose",
|
|
|
|
|
className: "AetheriaRuntimeZoneRenderBodyPose",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeZoneRenderAsteroidBeltPose",
|
|
|
|
|
className: "AetheriaRuntimeZoneRenderAsteroidBeltPose",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeSelectedObjectDocument",
|
|
|
|
|
className: "AetheriaRuntimeSelectedObjectDocument",
|
|
|
|
|
schemaName: "selectedObject",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeInventoryDocument",
|
|
|
|
|
className: "AetheriaRuntimeInventoryDocument",
|
|
|
|
|
schemaName: "inventory",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRunCheckpointCommit",
|
|
|
|
|
className: "AetheriaRuntimeRunCheckpointCommit",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeZoneSnapshotCommit",
|
|
|
|
|
className: "AetheriaRuntimeZoneSnapshotCommit",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeEntitySnapshotCommit",
|
|
|
|
|
className: "AetheriaRuntimeEntitySnapshotCommit",
|
|
|
|
|
},
|
2026-07-05 19:00:23 +01:00
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeProjectileCommit",
|
|
|
|
|
className: "AetheriaRuntimeProjectileCommit",
|
|
|
|
|
},
|
2026-06-25 18:09:33 +01:00
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeBodySnapshotCommit",
|
|
|
|
|
className: "AetheriaRuntimeBodySnapshotCommit",
|
|
|
|
|
},
|
2026-07-05 01:17:27 +01:00
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeSunVisualCommit",
|
|
|
|
|
className: "AetheriaRuntimeSunVisualCommit",
|
|
|
|
|
},
|
2026-06-25 18:09:33 +01:00
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeEntityStatGridCommit",
|
|
|
|
|
className: "AetheriaRuntimeEntityStatGridCommit",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeCargoBayLoadoutCommit",
|
|
|
|
|
className: "AetheriaRuntimeCargoBayLoadoutCommit",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeLoadoutItemSlotCommit",
|
|
|
|
|
className: "AetheriaRuntimeLoadoutItemSlotCommit",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeLoadoutItemCommit",
|
|
|
|
|
className: "AetheriaRuntimeLoadoutItemCommit",
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-07-05 19:56:18 +01:00
|
|
|
exportName: "aetheriaRuntimeGameViewportBounds",
|
|
|
|
|
className: "AetheriaRuntimeViewportBounds",
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
{
|
2026-07-05 19:56:18 +01:00
|
|
|
exportName: "aetheriaRuntimeGameViewportObject",
|
|
|
|
|
className: "AetheriaRuntimeViewportObject",
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRtsEntityStatus",
|
2026-07-05 19:56:18 +01:00
|
|
|
className: "AetheriaRuntimeEntityStatus",
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRtsInventoryItem",
|
2026-07-05 19:56:18 +01:00
|
|
|
className: "AetheriaRuntimeInventoryItem",
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRtsGravityInfluence",
|
2026-07-05 19:56:18 +01:00
|
|
|
className: "AetheriaRuntimeGravityInfluence",
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeRtsBodyView",
|
2026-07-05 19:56:18 +01:00
|
|
|
className: "AetheriaRuntimeBodyView",
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeScenarioDocument",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeScenarioDocument",
|
|
|
|
|
schemaName: "starbridgeScenario",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeSessionDocument",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeSessionDocument",
|
|
|
|
|
schemaName: "starbridgeSession",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeSessionSummaryDocument",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeSessionSummaryDocument",
|
|
|
|
|
schemaName: "starbridgeSessionSummary",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeStationStockItem",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeStationStockItem",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeWaveDefinition",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeWaveDefinition",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeWaveForecast",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeWaveForecast",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeRuntimeRole",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeRuntimeRole",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeStarbridgeBaseStatus",
|
|
|
|
|
className: "AetheriaRuntimeStarbridgeBaseStatus",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeVerseAuthorityPolicyDocument",
|
|
|
|
|
className: "AetheriaRuntimeVerseAuthorityPolicyDocument",
|
|
|
|
|
schemaName: "verseAuthorityPolicy",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeAuthorityRule",
|
|
|
|
|
className: "AetheriaRuntimeAuthorityRule",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeAuthorityRuntimeRole",
|
|
|
|
|
className: "AetheriaRuntimeAuthorityRuntimeRole",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
exportName: "aetheriaRuntimeAuthorityLeaseDocument",
|
|
|
|
|
className: "AetheriaRuntimeAuthorityLeaseDocument",
|
|
|
|
|
schemaName: "authorityLease",
|
|
|
|
|
},
|
|
|
|
|
].map(document => ({
|
|
|
|
|
...document,
|
|
|
|
|
slots: parseMessagePackSlots(sourceText, document.className),
|
|
|
|
|
schema: parseCultDocumentSchema(sourceText, document.className),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const generated = render(enums, documents);
|
|
|
|
|
const existing = readExisting(outputPath);
|
|
|
|
|
|
|
|
|
|
if (check) {
|
2026-07-10 13:37:03 +01:00
|
|
|
if (existing !== generated) {
|
2026-06-25 18:09:33 +01:00
|
|
|
console.error("RTS generated bindings are stale. Run `npm run generate:rts-bindings`.");
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
writeFileSync(outputPath, generated, "utf8");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readExisting(path) {
|
|
|
|
|
try {
|
2026-06-28 01:18:21 +01:00
|
|
|
return readFileSync(path, "utf8");
|
2026-06-25 18:09:33 +01:00
|
|
|
} catch {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseEnum(text, enumName) {
|
|
|
|
|
const body = readTypeBody(text, `enum ${enumName}`);
|
|
|
|
|
const entries = [];
|
|
|
|
|
let nextValue = 0;
|
|
|
|
|
for (const rawLine of body.split(/\r?\n/u)) {
|
|
|
|
|
const line = rawLine.replace(/\/\/.*$/u, "").trim().replace(/,$/u, "");
|
|
|
|
|
if (!line)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const match = /^([A-Za-z_][A-Za-z0-9_]*)(?:\s*=\s*(-?\d+))?$/u.exec(line);
|
|
|
|
|
if (!match)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const value = match[2] === undefined ? nextValue : Number.parseInt(match[2], 10);
|
|
|
|
|
entries.push([toCamelCase(match[1]), value]);
|
|
|
|
|
nextValue = value + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseMessagePackSlots(text, className) {
|
|
|
|
|
const body = readTypeBody(text, `class ${className}`);
|
|
|
|
|
const slots = [];
|
|
|
|
|
const propertyPattern = /\[Key\((\d+)\)\][\s\S]*?public\s+[A-Za-z0-9_<>,.?[\]\s]+\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{\s*get;\s*set;\s*\}/gu;
|
|
|
|
|
for (const match of body.matchAll(propertyPattern)) {
|
|
|
|
|
slots.push([toCamelCase(match[2]), Number.parseInt(match[1], 10)]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (slots.length === 0)
|
|
|
|
|
throw new Error(`No [Key] properties found for ${className}.`);
|
|
|
|
|
|
|
|
|
|
return slots.sort((left, right) => left[1] - right[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseCultDocumentSchema(text, className) {
|
|
|
|
|
const classIndex = text.indexOf(`class ${className}`);
|
|
|
|
|
if (classIndex < 0)
|
|
|
|
|
throw new Error(`Could not find ${className}.`);
|
|
|
|
|
|
|
|
|
|
const prefix = text.slice(Math.max(0, classIndex - 500), classIndex);
|
|
|
|
|
const matches = [...prefix.matchAll(/\[CultDocument\("([^"]+)",\s*"([^"]+)"\)\]/gu)];
|
|
|
|
|
return matches.length > 0 ? matches[matches.length - 1][2] : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readTypeBody(text, declaration) {
|
|
|
|
|
const declarationIndex = text.indexOf(declaration);
|
|
|
|
|
if (declarationIndex < 0)
|
|
|
|
|
throw new Error(`Could not find ${declaration}.`);
|
|
|
|
|
|
|
|
|
|
const openIndex = text.indexOf("{", declarationIndex);
|
|
|
|
|
if (openIndex < 0)
|
|
|
|
|
throw new Error(`Could not find body for ${declaration}.`);
|
|
|
|
|
|
|
|
|
|
let depth = 0;
|
|
|
|
|
for (let index = openIndex; index < text.length; index += 1) {
|
|
|
|
|
const character = text[index];
|
|
|
|
|
if (character === "{")
|
|
|
|
|
depth += 1;
|
|
|
|
|
if (character === "}") {
|
|
|
|
|
depth -= 1;
|
|
|
|
|
if (depth === 0)
|
|
|
|
|
return text.slice(openIndex + 1, index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error(`Could not read body for ${declaration}.`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function render(enums, documents) {
|
|
|
|
|
const schemas = documents
|
|
|
|
|
.filter(document => document.schema)
|
|
|
|
|
.map(document => ` ${document.schemaName}: "${document.schema}",`)
|
|
|
|
|
.join("\n");
|
|
|
|
|
const enumBlocks = enums
|
|
|
|
|
.map(item => renderEnumBlock(item.exportName, item.entries))
|
|
|
|
|
.join("\n\n");
|
|
|
|
|
const slotBlocks = documents
|
|
|
|
|
.map(document => renderSlotBlock(document.exportName, document.slots))
|
|
|
|
|
.join("\n\n");
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
return `// <auto-generated />\n// Generated by scripts/generate-rts-bindings.mjs from Aetheria C# MessagePack documents.\n\nimport { CultMesh } from "cultmesh-ts";\nimport type { CultMeshLiveFeedDiagnostic, CultMeshOperationContext, CultMeshOperationReceipt, CultMeshQuerySource, CultMeshQueryContext, CultMeshQuerySurfaceDiagnostic, CultMeshQueryWatcher, CultMeshRouteHint, CultMeshSurfaceCatalogDiagnostic, CultMeshSurfaceCatalogIndexDiagnostic, CultMeshUnsubscribe, CultMeshVerseContext, CultMeshViewportRequest } from "cultmesh-ts";\nimport type { IpcMain } from "electron";\n\nexport const AetheriaRtsSchemas = {\n${schemas}\n} as const;\n\n${enumBlocks}\n\nexport const AetheriaRtsIpcChannels = {\n${renderIpcChannelLines()}\n} as const;\n\n${slotBlocks}\n\n${renderRtsDocumentTypes("CultMeshViewportRequest")}\n\n${renderRtsOperations()}\n`;
|
2026-06-25 18:09:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderEnumBlock(exportName, entries) {
|
|
|
|
|
const lines = entries
|
|
|
|
|
.map(([name, value]) => ` ${name}: ${value},`)
|
|
|
|
|
.join("\n");
|
|
|
|
|
return `export const ${exportName} = {\n${lines}\n} as const;`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderSlotBlock(exportName, slots) {
|
|
|
|
|
const slotLines = slots.map(([name, value]) => ` ${name}: ${value},`).join("\n");
|
|
|
|
|
const slotNames = slots.map(([name]) => ` | "${name}"`).join("\n");
|
|
|
|
|
const typeName = `${toPascalCase(exportName)}Slot`;
|
|
|
|
|
return `export type ${typeName} =\n${slotNames};\n\nexport const ${exportName}Slots: Record<${typeName}, number> = {\n${slotLines}\n};`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toCamelCase(value) {
|
|
|
|
|
return value.slice(0, 1).toLowerCase() + value.slice(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toPascalCase(value) {
|
|
|
|
|
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderRtsOperations() {
|
|
|
|
|
return `export type AetheriaRuntimeSetMoveVectorRequest = {
|
|
|
|
|
actorEntityKey: string;
|
|
|
|
|
directionX: number;
|
|
|
|
|
directionY: number;
|
|
|
|
|
scalar: number;
|
|
|
|
|
observedFrameId?: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeSetTargetRequest = {
|
|
|
|
|
actorEntityKey: string;
|
|
|
|
|
targetEntityKey: string;
|
|
|
|
|
observedFrameId?: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeDaemonCommandReceipt = CultMeshOperationReceipt & {
|
|
|
|
|
commandId: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeSelectedObjectRequest = {
|
|
|
|
|
entityIndex: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeViewportFeedRequest = {
|
|
|
|
|
viewport: CultMeshViewportRequest;
|
|
|
|
|
selectedEntityIndex?: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeViewportFeedSnapshot = {
|
|
|
|
|
viewport: ViewportResponse;
|
2026-06-30 19:14:49 +01:00
|
|
|
selectedObject: SelectedObjectDocument | null;
|
|
|
|
|
inventory: InventoryDocument | null;
|
|
|
|
|
daemonHealth: DaemonHealthDocument;
|
|
|
|
|
authorityStatus: AuthorityStatusDocument;
|
|
|
|
|
starbridgeSession: StarbridgeSessionDocument;
|
|
|
|
|
assetManifest: AssetManifestDocument;
|
2026-06-25 18:09:33 +01:00
|
|
|
receivedAtUtc: string;
|
|
|
|
|
sampleMs: number;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-05 00:32:49 +01:00
|
|
|
export type AetheriaEveSurfaceRequest = {
|
|
|
|
|
surfaceId?: string;
|
|
|
|
|
recordKey?: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-10 07:17:55 +01:00
|
|
|
export type EveEmbeddedDocumentRequest = {
|
|
|
|
|
documentId: string;
|
|
|
|
|
schemaId?: string;
|
|
|
|
|
slotId?: string;
|
|
|
|
|
presentationKind?: string;
|
|
|
|
|
context?: Record<string, unknown>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type EveResolvedDocument = {
|
|
|
|
|
documentId: string;
|
|
|
|
|
schemaId: string;
|
|
|
|
|
document?: unknown;
|
|
|
|
|
surface?: AetheriaMenuSurfaceTree;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-10 06:48:19 +01:00
|
|
|
export type EveProviderAdvertisement = {
|
|
|
|
|
providerId: string;
|
|
|
|
|
title: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
surfaces: Array<{
|
|
|
|
|
surfaceId: string;
|
|
|
|
|
key: string;
|
|
|
|
|
transport: string;
|
|
|
|
|
status: string;
|
|
|
|
|
}>;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-05 00:32:49 +01:00
|
|
|
export type AetheriaEveCommandRequest = {
|
|
|
|
|
providerId?: string;
|
|
|
|
|
surfaceId?: string;
|
|
|
|
|
command?: string;
|
|
|
|
|
clientId?: string;
|
|
|
|
|
issuedAtUtc?: string;
|
|
|
|
|
payload?: Record<string, unknown>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaMenuSurfaceDocument = {
|
|
|
|
|
providerId: string;
|
|
|
|
|
providerKind: string;
|
|
|
|
|
title: string;
|
|
|
|
|
version: number;
|
|
|
|
|
updatedAtUtc: string;
|
|
|
|
|
surface: AetheriaMenuSurfaceTree;
|
|
|
|
|
commands: AetheriaMenuSurfaceCommand[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaMenuSurfaceTree = {
|
|
|
|
|
id: string;
|
|
|
|
|
root: AetheriaMenuSurfaceComponent;
|
|
|
|
|
styles: AetheriaMenuStyleToken[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaMenuSurfaceComponent = {
|
|
|
|
|
id: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
props: Record<string, string>;
|
|
|
|
|
layout?: Record<string, string>;
|
|
|
|
|
style?: Record<string, string>;
|
2026-07-05 01:17:27 +01:00
|
|
|
embeddedDocuments?: AetheriaMenuEmbeddedDocumentSlot[];
|
2026-07-05 00:32:49 +01:00
|
|
|
children: AetheriaMenuSurfaceComponent[];
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-05 01:17:27 +01:00
|
|
|
export type AetheriaMenuEmbeddedDocumentSlot = {
|
|
|
|
|
slotId: string;
|
|
|
|
|
documentId: string;
|
|
|
|
|
schemaId: string;
|
|
|
|
|
presentationKind: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-05 00:32:49 +01:00
|
|
|
export type AetheriaMenuStyleToken = {
|
|
|
|
|
name: string;
|
|
|
|
|
value: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaMenuSurfaceCommand = {
|
|
|
|
|
command: string;
|
|
|
|
|
label: string;
|
|
|
|
|
transport: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-25 18:09:33 +01:00
|
|
|
export type AetheriaRtsMainClient = {
|
|
|
|
|
mapViewport(request: CultMeshViewportRequest): Promise<ViewportResponse>;
|
|
|
|
|
objectsViewport(request: CultMeshViewportRequest): Promise<ObjectsViewportResponse>;
|
|
|
|
|
gravityViewport(request: CultMeshViewportRequest): Promise<GravityViewportResponse>;
|
2026-07-05 01:17:27 +01:00
|
|
|
renderSplatsViewport(request: CultMeshViewportRequest): Promise<RenderSplatsViewportResponse>;
|
2026-06-30 19:14:49 +01:00
|
|
|
selectedObject(request: AetheriaRuntimeSelectedObjectRequest): Promise<SelectedObjectDocument>;
|
|
|
|
|
inventory(request: AetheriaRuntimeSelectedObjectRequest): Promise<InventoryDocument>;
|
|
|
|
|
daemonHealth(): Promise<DaemonHealthDocument>;
|
|
|
|
|
authorityStatus(): Promise<AuthorityStatusDocument>;
|
|
|
|
|
starbridgeSession(): Promise<StarbridgeSessionDocument>;
|
|
|
|
|
assetManifest(): Promise<AssetManifestDocument>;
|
2026-06-25 18:09:33 +01:00
|
|
|
watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): CultMeshUnsubscribe;
|
|
|
|
|
setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
|
|
|
|
setTarget(request: AetheriaRuntimeSetTargetRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
|
|
|
|
surfaceCatalogDiagnostics(): CultMeshSurfaceCatalogDiagnostic;
|
|
|
|
|
surfaceCatalogIndexDiagnostics(): CultMeshSurfaceCatalogIndexDiagnostic;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRtsHealthProvider = () => unknown;
|
|
|
|
|
|
|
|
|
|
export function registerAetheriaRtsIpcHandlers(
|
|
|
|
|
ipcMain: IpcMain,
|
|
|
|
|
getClient: () => AetheriaRtsMainClient,
|
|
|
|
|
getHealth: AetheriaRtsHealthProvider,
|
|
|
|
|
): void {
|
|
|
|
|
const viewportFeedSubscriptions = new Map<string, CultMeshUnsubscribe>();
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.mapViewport, async (_event, request: CultMeshViewportRequest) =>
|
|
|
|
|
getClient().mapViewport(request));
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.objectsViewport, async (_event, request: CultMeshViewportRequest) =>
|
|
|
|
|
getClient().objectsViewport(request));
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.gravityViewport, async (_event, request: CultMeshViewportRequest) =>
|
|
|
|
|
getClient().gravityViewport(request));
|
2026-07-05 01:17:27 +01:00
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.renderSplatsViewport, async (_event, request: CultMeshViewportRequest) =>
|
|
|
|
|
getClient().renderSplatsViewport(request));
|
2026-06-25 18:09:33 +01:00
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.selectedObject, async (_event, request: AetheriaRuntimeSelectedObjectRequest) =>
|
|
|
|
|
getClient().selectedObject(request));
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.inventory, async (_event, request: AetheriaRuntimeSelectedObjectRequest) =>
|
|
|
|
|
getClient().inventory(request));
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.daemonHealth, async () => getClient().daemonHealth());
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.authorityStatus, async () => getClient().authorityStatus());
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.starbridgeSession, async () => getClient().starbridgeSession());
|
2026-06-28 01:18:21 +01:00
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.assetManifest, async () => getClient().assetManifest());
|
2026-06-25 18:09:33 +01:00
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.viewportFeed, async (event, request: { subscriptionId: string; feed: AetheriaRuntimeViewportFeedRequest }) => {
|
|
|
|
|
viewportFeedSubscriptions.get(request.subscriptionId)?.();
|
|
|
|
|
const unsubscribe = getClient().watchViewportFeed(request.feed, snapshot => {
|
|
|
|
|
event.sender.send(AetheriaRtsIpcChannels.viewportFeedUpdate, {
|
|
|
|
|
subscriptionId: request.subscriptionId,
|
|
|
|
|
snapshot,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
viewportFeedSubscriptions.set(request.subscriptionId, unsubscribe);
|
|
|
|
|
});
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.viewportFeedStop, (_event, subscriptionId: string) => {
|
|
|
|
|
viewportFeedSubscriptions.get(subscriptionId)?.();
|
|
|
|
|
viewportFeedSubscriptions.delete(subscriptionId);
|
|
|
|
|
});
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.setMoveVector, async (_event, request: AetheriaRuntimeSetMoveVectorRequest) =>
|
|
|
|
|
getClient().setMoveVector(request));
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.setTarget, async (_event, request: AetheriaRuntimeSetTargetRequest) =>
|
|
|
|
|
getClient().setTarget(request));
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.surfaceCatalog, () => getClient().surfaceCatalogDiagnostics());
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.surfaceCatalogIndex, () => getClient().surfaceCatalogIndexDiagnostics());
|
|
|
|
|
ipcMain.handle(AetheriaRtsIpcChannels.health, () => getHealth());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeDaemonCommandSender = (
|
|
|
|
|
commandId: string,
|
|
|
|
|
issuedAtUtc: string,
|
|
|
|
|
command: unknown[],
|
|
|
|
|
context: CultMeshOperationContext,
|
|
|
|
|
) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeViewportQueryExecutor<TResult> = (
|
|
|
|
|
request: CultMeshViewportRequest,
|
|
|
|
|
context: CultMeshQueryContext,
|
|
|
|
|
) => Promise<TResult>;
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeQueryExecutor<TParameters, TResult> = (
|
|
|
|
|
request: TParameters,
|
|
|
|
|
context: CultMeshQueryContext,
|
|
|
|
|
) => Promise<TResult>;
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameQueryExecutors = {
|
2026-06-25 18:09:33 +01:00
|
|
|
mapViewport: AetheriaRuntimeViewportQueryExecutor<ViewportResponse>;
|
|
|
|
|
objectsViewport: AetheriaRuntimeViewportQueryExecutor<ObjectsViewportResponse>;
|
|
|
|
|
gravityViewport: AetheriaRuntimeViewportQueryExecutor<GravityViewportResponse>;
|
2026-07-05 01:17:27 +01:00
|
|
|
renderSplatsViewport: AetheriaRuntimeViewportQueryExecutor<RenderSplatsViewportResponse>;
|
2026-06-30 19:14:49 +01:00
|
|
|
selectedObject: AetheriaRuntimeQueryExecutor<AetheriaRuntimeSelectedObjectRequest, SelectedObjectDocument>;
|
|
|
|
|
inventory: AetheriaRuntimeQueryExecutor<AetheriaRuntimeSelectedObjectRequest, InventoryDocument>;
|
|
|
|
|
daemonHealth: AetheriaRuntimeQueryExecutor<void, DaemonHealthDocument>;
|
|
|
|
|
authorityStatus: AetheriaRuntimeQueryExecutor<void, AuthorityStatusDocument>;
|
|
|
|
|
starbridgeSession: AetheriaRuntimeQueryExecutor<void, StarbridgeSessionDocument>;
|
|
|
|
|
assetManifest: AetheriaRuntimeQueryExecutor<void, AssetManifestDocument>;
|
2026-06-25 18:09:33 +01:00
|
|
|
};
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameQueryWatchers = Partial<{
|
2026-06-25 18:09:33 +01:00
|
|
|
mapViewport: CultMeshQueryWatcher<CultMeshViewportRequest, ViewportResponse>;
|
|
|
|
|
objectsViewport: CultMeshQueryWatcher<CultMeshViewportRequest, ObjectsViewportResponse>;
|
|
|
|
|
gravityViewport: CultMeshQueryWatcher<CultMeshViewportRequest, GravityViewportResponse>;
|
2026-07-05 01:17:27 +01:00
|
|
|
renderSplatsViewport: CultMeshQueryWatcher<CultMeshViewportRequest, RenderSplatsViewportResponse>;
|
2026-06-30 19:14:49 +01:00
|
|
|
selectedObject: CultMeshQueryWatcher<AetheriaRuntimeSelectedObjectRequest, SelectedObjectDocument>;
|
|
|
|
|
inventory: CultMeshQueryWatcher<AetheriaRuntimeSelectedObjectRequest, InventoryDocument>;
|
|
|
|
|
daemonHealth: CultMeshQueryWatcher<void, DaemonHealthDocument>;
|
|
|
|
|
authorityStatus: CultMeshQueryWatcher<void, AuthorityStatusDocument>;
|
|
|
|
|
starbridgeSession: CultMeshQueryWatcher<void, StarbridgeSessionDocument>;
|
|
|
|
|
assetManifest: CultMeshQueryWatcher<void, AssetManifestDocument>;
|
2026-06-25 18:09:33 +01:00
|
|
|
}>;
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameQueryDiagnostic = CultMeshQuerySurfaceDiagnostic;
|
2026-06-25 18:09:33 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameLiveFeedDiagnostic = CultMeshLiveFeedDiagnostic;
|
2026-06-25 18:09:33 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameSurfaceCatalogDiagnostic = CultMeshSurfaceCatalogDiagnostic;
|
2026-06-25 18:09:33 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameOperationHandles = ReturnType<typeof createAetheriaRuntimeGameOperationHandles>;
|
2026-06-25 18:09:33 +01:00
|
|
|
|
2026-07-05 19:56:18 +01:00
|
|
|
export type AetheriaRuntimeGameDocuments = ReturnType<typeof createAetheriaRuntimeGameDocuments>;
|
2026-06-25 18:09:33 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameStatePointers = AetheriaRuntimeGameDocuments;
|
2026-06-27 15:26:20 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameDocumentResolvers = Partial<{
|
2026-06-25 18:09:33 +01:00
|
|
|
daemonFrame: (context: CultMeshQueryContext) => Promise<unknown>;
|
|
|
|
|
daemonHealth: (context: CultMeshQueryContext) => Promise<unknown>;
|
|
|
|
|
authorityPolicy: (context: CultMeshQueryContext) => Promise<unknown>;
|
|
|
|
|
starbridgeSession: (context: CultMeshQueryContext) => Promise<unknown>;
|
2026-06-28 01:18:21 +01:00
|
|
|
assetManifest: (context: CultMeshQueryContext) => Promise<unknown>;
|
2026-06-25 18:09:33 +01:00
|
|
|
}>;
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameStatePointerResolvers = AetheriaRuntimeGameDocumentResolvers;
|
2026-06-27 15:26:20 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export const AetheriaRuntimeGameDocumentSources = {
|
2026-06-30 19:14:49 +01:00
|
|
|
daemonFrame: CultMesh.querySource("daemon:aetheria.frame.latest.v1", {
|
2026-06-25 18:09:33 +01:00
|
|
|
schemaId: AetheriaRtsSchemas.daemonFrame,
|
|
|
|
|
description: "latest daemon frame"
|
|
|
|
|
}),
|
2026-06-30 19:14:49 +01:00
|
|
|
daemonHealth: CultMesh.querySource("daemon:aetheria.health.latest.v1", {
|
2026-06-25 18:09:33 +01:00
|
|
|
schemaId: AetheriaRtsSchemas.daemonHealth,
|
|
|
|
|
description: "latest daemon health"
|
|
|
|
|
}),
|
2026-06-30 19:14:49 +01:00
|
|
|
authorityPolicy: CultMesh.querySource("daemon:aetheria.authority.policy.latest.v1", {
|
2026-06-25 18:09:33 +01:00
|
|
|
schemaId: AetheriaRtsSchemas.verseAuthorityPolicy,
|
|
|
|
|
description: "latest Verse authority policy"
|
|
|
|
|
}),
|
2026-06-30 19:14:49 +01:00
|
|
|
starbridgeSession: CultMesh.querySource("daemon:aetheria.starbridge.session.latest.v1", {
|
2026-06-25 18:09:33 +01:00
|
|
|
schemaId: AetheriaRtsSchemas.starbridgeSessionSummary,
|
|
|
|
|
description: "latest Starbridge session summary"
|
2026-06-28 01:18:21 +01:00
|
|
|
}),
|
2026-06-30 19:14:49 +01:00
|
|
|
assetManifest: CultMesh.querySource("daemon:aetheria.asset_manifest.latest.v1", {
|
2026-06-28 01:18:21 +01:00
|
|
|
schemaId: AetheriaRtsSchemas.assetManifest,
|
|
|
|
|
description: "latest daemon asset manifest"
|
2026-06-25 18:09:33 +01:00
|
|
|
})
|
|
|
|
|
} as const;
|
|
|
|
|
|
2026-07-05 19:56:18 +01:00
|
|
|
export function createAetheriaRuntimeGameDocuments(
|
2026-06-25 18:09:33 +01:00
|
|
|
routeHint: CultMeshRouteHint = CultMesh.routeHint(),
|
2026-07-06 03:41:32 +01:00
|
|
|
resolvers: AetheriaRuntimeGameDocumentResolvers = {},
|
2026-06-25 18:09:33 +01:00
|
|
|
) {
|
|
|
|
|
return {
|
2026-06-27 15:26:20 +01:00
|
|
|
daemonFrame: CultMesh.document(
|
2026-07-06 03:41:32 +01:00
|
|
|
AetheriaRuntimeGameDocumentSources.daemonFrame.sourceId,
|
2026-06-27 15:26:20 +01:00
|
|
|
{ schemaId: AetheriaRtsSchemas.daemonFrame },
|
2026-06-25 18:09:33 +01:00
|
|
|
async (context) => resolvers.daemonFrame?.(context),
|
|
|
|
|
{
|
|
|
|
|
routeHint,
|
2026-07-06 03:41:32 +01:00
|
|
|
sources: [AetheriaRuntimeGameDocumentSources.daemonFrame],
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
),
|
2026-06-27 15:26:20 +01:00
|
|
|
daemonHealth: CultMesh.document(
|
2026-07-06 03:41:32 +01:00
|
|
|
AetheriaRuntimeGameDocumentSources.daemonHealth.sourceId,
|
2026-06-27 15:26:20 +01:00
|
|
|
{ schemaId: AetheriaRtsSchemas.daemonHealth },
|
2026-06-25 18:09:33 +01:00
|
|
|
async (context) => resolvers.daemonHealth?.(context),
|
|
|
|
|
{
|
|
|
|
|
routeHint,
|
2026-07-06 03:41:32 +01:00
|
|
|
sources: [AetheriaRuntimeGameDocumentSources.daemonHealth],
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
),
|
2026-06-27 15:26:20 +01:00
|
|
|
authorityPolicy: CultMesh.document(
|
2026-07-06 03:41:32 +01:00
|
|
|
AetheriaRuntimeGameDocumentSources.authorityPolicy.sourceId,
|
2026-06-27 15:26:20 +01:00
|
|
|
{ schemaId: AetheriaRtsSchemas.verseAuthorityPolicy },
|
2026-06-25 18:09:33 +01:00
|
|
|
async (context) => resolvers.authorityPolicy?.(context),
|
|
|
|
|
{
|
|
|
|
|
routeHint,
|
2026-07-06 03:41:32 +01:00
|
|
|
sources: [AetheriaRuntimeGameDocumentSources.authorityPolicy],
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
),
|
2026-06-27 15:26:20 +01:00
|
|
|
starbridgeSession: CultMesh.document(
|
2026-07-06 03:41:32 +01:00
|
|
|
AetheriaRuntimeGameDocumentSources.starbridgeSession.sourceId,
|
2026-06-27 15:26:20 +01:00
|
|
|
{ schemaId: AetheriaRtsSchemas.starbridgeSessionSummary },
|
2026-06-25 18:09:33 +01:00
|
|
|
async (context) => resolvers.starbridgeSession?.(context),
|
|
|
|
|
{
|
|
|
|
|
routeHint,
|
2026-07-06 03:41:32 +01:00
|
|
|
sources: [AetheriaRuntimeGameDocumentSources.starbridgeSession],
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
),
|
2026-06-28 01:18:21 +01:00
|
|
|
assetManifest: CultMesh.document(
|
2026-07-06 03:41:32 +01:00
|
|
|
AetheriaRuntimeGameDocumentSources.assetManifest.sourceId,
|
2026-06-28 01:18:21 +01:00
|
|
|
{ schemaId: AetheriaRtsSchemas.assetManifest },
|
|
|
|
|
async (context) => resolvers.assetManifest?.(context),
|
|
|
|
|
{
|
|
|
|
|
routeHint,
|
2026-07-06 03:41:32 +01:00
|
|
|
sources: [AetheriaRuntimeGameDocumentSources.assetManifest],
|
2026-06-28 01:18:21 +01:00
|
|
|
},
|
|
|
|
|
),
|
2026-06-25 18:09:33 +01:00
|
|
|
} as const;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export const createAetheriaRuntimeGameStatePointers = createAetheriaRuntimeGameDocuments;
|
2026-06-27 15:26:20 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export function createAetheriaRuntimeGameQueryHandles(
|
|
|
|
|
executors: AetheriaRuntimeGameQueryExecutors,
|
2026-06-25 18:09:33 +01:00
|
|
|
routeHint: CultMeshRouteHint = CultMesh.routeHint(),
|
2026-07-06 03:41:32 +01:00
|
|
|
watchers: AetheriaRuntimeGameQueryWatchers = {},
|
2026-06-25 18:09:33 +01:00
|
|
|
) {
|
|
|
|
|
return {
|
2026-06-30 19:14:49 +01:00
|
|
|
mapViewport: CultMesh.query<CultMeshViewportRequest, ViewportResponse>(
|
2026-07-05 19:56:18 +01:00
|
|
|
AetheriaRtsSchemas.gameViewport,
|
2026-06-25 18:09:33 +01:00
|
|
|
executors.mapViewport,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.mapViewport },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
|
|
|
|
objectsViewport: CultMesh.query<CultMeshViewportRequest, ObjectsViewportResponse>(
|
2026-06-25 18:09:33 +01:00
|
|
|
AetheriaRtsSchemas.objectsViewport,
|
|
|
|
|
executors.objectsViewport,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.objectsViewport },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
|
|
|
|
gravityViewport: CultMesh.query<CultMeshViewportRequest, GravityViewportResponse>(
|
2026-06-25 18:09:33 +01:00
|
|
|
AetheriaRtsSchemas.gravityViewport,
|
|
|
|
|
executors.gravityViewport,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.gravityViewport },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
2026-07-05 01:17:27 +01:00
|
|
|
renderSplatsViewport: CultMesh.query<CultMeshViewportRequest, RenderSplatsViewportResponse>(
|
|
|
|
|
AetheriaRtsSchemas.renderSplatsViewport,
|
|
|
|
|
executors.renderSplatsViewport,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.renderSplatsViewport },
|
2026-07-05 01:17:27 +01:00
|
|
|
),
|
2026-06-30 19:14:49 +01:00
|
|
|
selectedObject: CultMesh.query<AetheriaRuntimeSelectedObjectRequest, SelectedObjectDocument>(
|
2026-06-25 18:09:33 +01:00
|
|
|
AetheriaRtsSchemas.selectedObject,
|
|
|
|
|
executors.selectedObject,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.selectedObject },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
|
|
|
|
inventory: CultMesh.query<AetheriaRuntimeSelectedObjectRequest, InventoryDocument>(
|
2026-06-25 18:09:33 +01:00
|
|
|
AetheriaRtsSchemas.inventory,
|
|
|
|
|
executors.inventory,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.inventory },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
|
|
|
|
daemonHealth: CultMesh.query<void, DaemonHealthDocument>(
|
2026-06-25 18:09:33 +01:00
|
|
|
AetheriaRtsSchemas.daemonHealth,
|
|
|
|
|
executors.daemonHealth,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonHealth], watchQuery: watchers.daemonHealth },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
|
|
|
|
authorityStatus: CultMesh.query<void, AuthorityStatusDocument>(
|
2026-06-25 18:09:33 +01:00
|
|
|
AetheriaRtsSchemas.verseAuthorityPolicy,
|
|
|
|
|
executors.authorityStatus,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.authorityPolicy], watchQuery: watchers.authorityStatus },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
|
|
|
|
starbridgeSession: CultMesh.query<void, StarbridgeSessionDocument>(
|
2026-06-25 18:09:33 +01:00
|
|
|
AetheriaRtsSchemas.starbridgeSessionSummary,
|
|
|
|
|
executors.starbridgeSession,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.starbridgeSession], watchQuery: watchers.starbridgeSession },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
|
|
|
|
assetManifest: CultMesh.query<void, AssetManifestDocument>(
|
2026-06-28 01:18:21 +01:00
|
|
|
AetheriaRtsSchemas.assetManifest,
|
|
|
|
|
executors.assetManifest,
|
2026-07-06 03:41:32 +01:00
|
|
|
{ routeHint, sources: [AetheriaRuntimeGameDocumentSources.assetManifest], watchQuery: watchers.assetManifest },
|
2026-06-30 19:14:49 +01:00
|
|
|
),
|
2026-06-25 18:09:33 +01:00
|
|
|
} as const;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export function describeAetheriaRuntimeGameQueryHandles(
|
|
|
|
|
handles: ReturnType<typeof createAetheriaRuntimeGameQueryHandles>,
|
2026-06-25 18:09:33 +01:00
|
|
|
) {
|
|
|
|
|
return {
|
2026-07-06 03:41:32 +01:00
|
|
|
mapViewport: describeAetheriaRuntimeGameQuerySurface(handles.mapViewport),
|
|
|
|
|
objectsViewport: describeAetheriaRuntimeGameQuerySurface(handles.objectsViewport),
|
|
|
|
|
gravityViewport: describeAetheriaRuntimeGameQuerySurface(handles.gravityViewport),
|
|
|
|
|
renderSplatsViewport: describeAetheriaRuntimeGameQuerySurface(handles.renderSplatsViewport),
|
|
|
|
|
selectedObject: describeAetheriaRuntimeGameQuerySurface(handles.selectedObject),
|
|
|
|
|
inventory: describeAetheriaRuntimeGameQuerySurface(handles.inventory),
|
|
|
|
|
daemonHealth: describeAetheriaRuntimeGameQuerySurface(handles.daemonHealth),
|
|
|
|
|
authorityStatus: describeAetheriaRuntimeGameQuerySurface(handles.authorityStatus),
|
|
|
|
|
starbridgeSession: describeAetheriaRuntimeGameQuerySurface(handles.starbridgeSession),
|
|
|
|
|
assetManifest: describeAetheriaRuntimeGameQuerySurface(handles.assetManifest),
|
2026-06-25 18:09:33 +01:00
|
|
|
} as const;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export function describeAetheriaRuntimeGameSurfaceCatalog(
|
|
|
|
|
handles: ReturnType<typeof createAetheriaRuntimeGameQueryHandles>,
|
|
|
|
|
operations?: AetheriaRuntimeGameOperationHandles,
|
2026-07-05 19:56:18 +01:00
|
|
|
documents: AetheriaRuntimeGameDocuments = createAetheriaRuntimeGameDocuments(),
|
2026-07-06 03:41:32 +01:00
|
|
|
): AetheriaRuntimeGameSurfaceCatalogDiagnostic {
|
2026-06-25 18:09:33 +01:00
|
|
|
return CultMesh.describeSurfaceCatalog(
|
2026-07-05 22:14:58 +01:00
|
|
|
"gamecult.aetheria.surfaces.v1",
|
2026-06-25 18:09:33 +01:00
|
|
|
[
|
2026-06-27 15:35:42 +01:00
|
|
|
CultMesh.describeSurface(documents.daemonFrame),
|
|
|
|
|
CultMesh.describeSurface(documents.daemonHealth),
|
|
|
|
|
CultMesh.describeSurface(documents.authorityPolicy),
|
|
|
|
|
CultMesh.describeSurface(documents.starbridgeSession),
|
2026-06-28 01:18:21 +01:00
|
|
|
CultMesh.describeSurface(documents.assetManifest),
|
2026-06-25 18:09:33 +01:00
|
|
|
CultMesh.describeSurface(handles.mapViewport),
|
|
|
|
|
CultMesh.describeSurface(handles.objectsViewport),
|
|
|
|
|
CultMesh.describeSurface(handles.gravityViewport),
|
2026-07-05 01:17:27 +01:00
|
|
|
CultMesh.describeSurface(handles.renderSplatsViewport),
|
2026-06-25 18:09:33 +01:00
|
|
|
CultMesh.describeSurface(handles.selectedObject),
|
|
|
|
|
CultMesh.describeSurface(handles.inventory),
|
|
|
|
|
CultMesh.describeSurface(handles.daemonHealth),
|
|
|
|
|
CultMesh.describeSurface(handles.authorityStatus),
|
|
|
|
|
CultMesh.describeSurface(handles.starbridgeSession),
|
2026-06-28 01:18:21 +01:00
|
|
|
CultMesh.describeSurface(handles.assetManifest),
|
2026-06-25 18:09:33 +01:00
|
|
|
...(operations
|
|
|
|
|
? [
|
|
|
|
|
CultMesh.describeSurface(operations.setMoveVector),
|
|
|
|
|
CultMesh.describeSurface(operations.setTarget),
|
|
|
|
|
]
|
|
|
|
|
: []),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
function describeAetheriaRuntimeGameQuerySurface(surface: {
|
2026-06-25 18:09:33 +01:00
|
|
|
queryId: string;
|
|
|
|
|
routeHint: CultMeshRouteHint;
|
2026-06-30 19:14:49 +01:00
|
|
|
sources: readonly CultMeshQuerySource[];
|
2026-07-06 03:41:32 +01:00
|
|
|
}): AetheriaRuntimeGameQueryDiagnostic {
|
2026-06-25 18:09:33 +01:00
|
|
|
return CultMesh.describeQuerySurface(surface);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export function describeAetheriaRuntimeGameLiveFeedSurface(surface: {
|
2026-06-25 18:09:33 +01:00
|
|
|
feedId: string;
|
|
|
|
|
routeHint: CultMeshRouteHint;
|
2026-06-30 19:14:49 +01:00
|
|
|
sources: readonly CultMeshQuerySource[];
|
2026-07-06 03:41:32 +01:00
|
|
|
}): AetheriaRuntimeGameLiveFeedDiagnostic {
|
2026-06-25 18:09:33 +01:00
|
|
|
return CultMesh.describeLiveFeed(surface);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export function createAetheriaRuntimeGameOperationHandles(
|
2026-06-25 18:09:33 +01:00
|
|
|
sendCommand: AetheriaRuntimeDaemonCommandSender,
|
|
|
|
|
createCommandId: () => string = createDefaultCommandId,
|
|
|
|
|
) {
|
|
|
|
|
return {
|
|
|
|
|
setMoveVector: CultMesh.operation<AetheriaRuntimeSetMoveVectorRequest, AetheriaRuntimeDaemonCommandReceipt>(
|
|
|
|
|
"gamecult.aetheria.pilot.set_move_vector.v1",
|
|
|
|
|
async (request, context) => {
|
|
|
|
|
const commandId = context.idempotencyKey ?? createCommandId();
|
|
|
|
|
const issuedAtUtc = new Date().toISOString();
|
|
|
|
|
await sendCommand(
|
|
|
|
|
commandId,
|
|
|
|
|
issuedAtUtc,
|
|
|
|
|
encodeSetMoveVectorCommand(commandId, issuedAtUtc, context.runtimeId, request),
|
|
|
|
|
context,
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
commandId,
|
|
|
|
|
...CultMesh.operationReceipt("gamecult.aetheria.pilot.set_move_vector.v1", true, { route: context.routeHint }),
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
setTarget: CultMesh.operation<AetheriaRuntimeSetTargetRequest, AetheriaRuntimeDaemonCommandReceipt>(
|
|
|
|
|
"gamecult.aetheria.pilot.set_target.v1",
|
|
|
|
|
async (request, context) => {
|
|
|
|
|
const commandId = context.idempotencyKey ?? createCommandId();
|
|
|
|
|
const issuedAtUtc = new Date().toISOString();
|
|
|
|
|
await sendCommand(
|
|
|
|
|
commandId,
|
|
|
|
|
issuedAtUtc,
|
|
|
|
|
encodeSetTargetCommand(commandId, issuedAtUtc, context.runtimeId, request),
|
|
|
|
|
context,
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
commandId,
|
|
|
|
|
...CultMesh.operationReceipt("gamecult.aetheria.pilot.set_target.v1", true, { route: context.routeHint }),
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
} as const;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export type AetheriaRuntimeGameVerseHandles = ReturnType<typeof createAetheriaRuntimeGameVerseHandles>;
|
2026-06-25 18:09:33 +01:00
|
|
|
|
2026-07-06 03:41:32 +01:00
|
|
|
export function createAetheriaRuntimeGameVerseHandles(
|
2026-06-25 18:09:33 +01:00
|
|
|
queryVerse: CultMeshVerseContext,
|
|
|
|
|
commandVerse: CultMeshVerseContext,
|
2026-07-06 03:41:32 +01:00
|
|
|
queries: ReturnType<typeof createAetheriaRuntimeGameQueryHandles>,
|
|
|
|
|
operations: AetheriaRuntimeGameOperationHandles,
|
2026-07-05 19:56:18 +01:00
|
|
|
documents: AetheriaRuntimeGameDocuments = createAetheriaRuntimeGameDocuments(),
|
2026-06-25 18:09:33 +01:00
|
|
|
) {
|
|
|
|
|
const mapViewport = CultMesh.bindQuery(queryVerse, queries.mapViewport);
|
|
|
|
|
const objectsViewport = CultMesh.bindQuery(queryVerse, queries.objectsViewport);
|
|
|
|
|
const gravityViewport = CultMesh.bindQuery(queryVerse, queries.gravityViewport);
|
2026-07-05 01:17:27 +01:00
|
|
|
const renderSplatsViewport = CultMesh.bindQuery(queryVerse, queries.renderSplatsViewport);
|
2026-06-25 18:09:33 +01:00
|
|
|
const selectedObject = CultMesh.bindQuery(queryVerse, queries.selectedObject);
|
|
|
|
|
const inventory = CultMesh.bindQuery(queryVerse, queries.inventory);
|
|
|
|
|
const daemonHealth = CultMesh.bindQuery(queryVerse, queries.daemonHealth);
|
|
|
|
|
const authorityStatus = CultMesh.bindQuery(queryVerse, queries.authorityStatus);
|
|
|
|
|
const starbridgeSession = CultMesh.bindQuery(queryVerse, queries.starbridgeSession);
|
2026-06-28 01:18:21 +01:00
|
|
|
const assetManifest = CultMesh.bindQuery(queryVerse, queries.assetManifest);
|
2026-06-27 15:26:20 +01:00
|
|
|
const daemonFrameDocument = CultMesh.bindDocument(queryVerse, documents.daemonFrame);
|
|
|
|
|
const daemonHealthDocument = CultMesh.bindDocument(queryVerse, documents.daemonHealth);
|
|
|
|
|
const authorityPolicyDocument = CultMesh.bindDocument(queryVerse, documents.authorityPolicy);
|
|
|
|
|
const starbridgeSessionDocument = CultMesh.bindDocument(queryVerse, documents.starbridgeSession);
|
2026-06-28 01:18:21 +01:00
|
|
|
const assetManifestDocument = CultMesh.bindDocument(queryVerse, documents.assetManifest);
|
2026-06-25 18:09:33 +01:00
|
|
|
const setMoveVector = CultMesh.bindOperation(commandVerse, operations.setMoveVector);
|
|
|
|
|
const setTarget = CultMesh.bindOperation(commandVerse, operations.setTarget);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
zone: (_zoneId = "current") => ({
|
|
|
|
|
viewport: {
|
|
|
|
|
within: (request: CultMeshViewportRequest) =>
|
|
|
|
|
mapViewport.execute(request),
|
|
|
|
|
watch: (
|
|
|
|
|
request: CultMeshViewportRequest,
|
|
|
|
|
callback: (value: ViewportResponse) => void,
|
|
|
|
|
) => mapViewport.watch(request, callback),
|
|
|
|
|
},
|
|
|
|
|
objects: {
|
|
|
|
|
visibleWithin: (request: CultMeshViewportRequest) =>
|
|
|
|
|
objectsViewport.execute(request),
|
|
|
|
|
watchVisibleWithin: (
|
|
|
|
|
request: CultMeshViewportRequest,
|
|
|
|
|
callback: (value: ObjectsViewportResponse) => void,
|
|
|
|
|
) => objectsViewport.watch(request, callback),
|
|
|
|
|
},
|
|
|
|
|
gravity: {
|
|
|
|
|
within: (request: CultMeshViewportRequest) =>
|
|
|
|
|
gravityViewport.execute(request),
|
|
|
|
|
watchWithin: (
|
|
|
|
|
request: CultMeshViewportRequest,
|
|
|
|
|
callback: (value: GravityViewportResponse) => void,
|
|
|
|
|
) => gravityViewport.watch(request, callback),
|
|
|
|
|
},
|
2026-07-05 01:17:27 +01:00
|
|
|
renderSplats: {
|
|
|
|
|
within: (request: CultMeshViewportRequest) =>
|
|
|
|
|
renderSplatsViewport.execute(request),
|
|
|
|
|
watchWithin: (
|
|
|
|
|
request: CultMeshViewportRequest,
|
|
|
|
|
callback: (value: RenderSplatsViewportResponse) => void,
|
|
|
|
|
) => renderSplatsViewport.watch(request, callback),
|
|
|
|
|
},
|
2026-06-25 18:09:33 +01:00
|
|
|
}),
|
|
|
|
|
entity: (entityKey: string) => ({
|
|
|
|
|
pilot: {
|
|
|
|
|
move: (
|
|
|
|
|
direction: { readonly x: number; readonly y: number },
|
|
|
|
|
options: { scalar?: number; observedFrameId?: number; commandId?: string } = {},
|
|
|
|
|
) => setMoveVector.invoke({
|
|
|
|
|
actorEntityKey: entityKey,
|
|
|
|
|
directionX: direction.x,
|
|
|
|
|
directionY: direction.y,
|
|
|
|
|
scalar: options.scalar ?? 1,
|
|
|
|
|
observedFrameId: options.observedFrameId,
|
|
|
|
|
}, { idempotencyKey: options.commandId }),
|
|
|
|
|
target: (
|
|
|
|
|
targetEntityKey: string,
|
|
|
|
|
options: { observedFrameId?: number; commandId?: string } = {},
|
|
|
|
|
) => setTarget.invoke({
|
|
|
|
|
actorEntityKey: entityKey,
|
|
|
|
|
targetEntityKey,
|
|
|
|
|
observedFrameId: options.observedFrameId,
|
|
|
|
|
}, { idempotencyKey: options.commandId }),
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
selectedObject: (entityIndex: number) =>
|
|
|
|
|
selectedObject.execute({ entityIndex }),
|
|
|
|
|
inventory: (entityIndex: number) =>
|
|
|
|
|
inventory.execute({ entityIndex }),
|
|
|
|
|
daemon: {
|
|
|
|
|
state: {
|
2026-06-27 15:26:20 +01:00
|
|
|
frame: () => daemonFrameDocument.latest(),
|
|
|
|
|
health: () => daemonHealthDocument.latest(),
|
|
|
|
|
authorityPolicy: () => authorityPolicyDocument.latest(),
|
|
|
|
|
starbridgeSession: () => starbridgeSessionDocument.latest(),
|
2026-06-28 01:18:21 +01:00
|
|
|
assetManifest: () => assetManifestDocument.latest(),
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
health: () => daemonHealth.execute(undefined),
|
|
|
|
|
authorityStatus: () => authorityStatus.execute(undefined),
|
|
|
|
|
starbridgeSession: () => starbridgeSession.execute(undefined),
|
2026-06-28 01:18:21 +01:00
|
|
|
assetManifest: () => assetManifest.execute(undefined),
|
2026-06-25 18:09:33 +01:00
|
|
|
},
|
|
|
|
|
} as const;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function encodeSetMoveVectorCommand(
|
|
|
|
|
commandId: string,
|
|
|
|
|
issuedAtUtc: string,
|
|
|
|
|
runtimeId: string,
|
|
|
|
|
request: AetheriaRuntimeSetMoveVectorRequest,
|
|
|
|
|
): unknown[] {
|
|
|
|
|
const command = createBaseCommand(commandId, issuedAtUtc, runtimeId, request.actorEntityKey, request.observedFrameId);
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.kind] = AetheriaRuntimeDaemonCommandKinds.setMoveVector;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.directionX] = request.directionX;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.directionY] = request.directionY;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.scalarValue] = Math.min(1, Math.max(0, request.scalar));
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function encodeSetTargetCommand(
|
|
|
|
|
commandId: string,
|
|
|
|
|
issuedAtUtc: string,
|
|
|
|
|
runtimeId: string,
|
|
|
|
|
request: AetheriaRuntimeSetTargetRequest,
|
|
|
|
|
): unknown[] {
|
|
|
|
|
const command = createBaseCommand(commandId, issuedAtUtc, runtimeId, request.actorEntityKey, request.observedFrameId);
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.kind] = AetheriaRuntimeDaemonCommandKinds.setTarget;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.targetEntityKey] = request.targetEntityKey;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createBaseCommand(
|
|
|
|
|
commandId: string,
|
|
|
|
|
issuedAtUtc: string,
|
|
|
|
|
runtimeId: string,
|
|
|
|
|
actorEntityKey: string,
|
|
|
|
|
observedFrameId?: number,
|
|
|
|
|
): unknown[] {
|
|
|
|
|
const command = new Array<unknown>(Object.keys(aetheriaRuntimeDaemonCommandDocumentSlots).length).fill(null);
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.schema] = AetheriaRtsSchemas.daemonCommand;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.commandId] = commandId;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.clientId] = runtimeId;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.issuedAtUtc] = issuedAtUtc;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.sessionId] = "local";
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.observedFrameId] = observedFrameId ?? -1;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.kind] = 0;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.actorEntityKey] = actorEntityKey;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.targetEntityKey] = "";
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.targetZoneIndex] = -1;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.equipmentIndex] = -1;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.behaviorIndex] = -1;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.weaponGroup] = -1;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.positionX] = 0;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.positionY] = 0;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.positionZ] = 0;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.directionX] = 0;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.directionY] = 0;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.scalarValue] = 0;
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.textValue] = "";
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.authorRuntimeId] = "";
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.subjectKey] = "";
|
|
|
|
|
command[aetheriaRuntimeDaemonCommandDocumentSlots.claimKind] = "";
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createDefaultCommandId(): string {
|
|
|
|
|
const cryptoApi = globalThis.crypto as { randomUUID?: () => string } | undefined;
|
|
|
|
|
return cryptoApi?.randomUUID?.() ?? \`command-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2)}\`;
|
|
|
|
|
}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderRendererContract() {
|
|
|
|
|
return `// <auto-generated />
|
|
|
|
|
// Generated by scripts/generate-rts-bindings.mjs from the RTS CultMesh surface contract.
|
|
|
|
|
|
|
|
|
|
import type {
|
|
|
|
|
CultMeshOperationReceipt,
|
|
|
|
|
CultMeshSurfaceCatalogDiagnostic,
|
|
|
|
|
CultMeshSurfaceCatalogIndexDiagnostic,
|
|
|
|
|
CultMeshViewportRequest,
|
|
|
|
|
} from "cultmesh-ts";
|
|
|
|
|
|
|
|
|
|
export type Viewport = CultMeshViewportRequest;
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeSetMoveVectorRequest = {
|
|
|
|
|
actorEntityKey: string;
|
|
|
|
|
directionX: number;
|
|
|
|
|
directionY: number;
|
|
|
|
|
scalar: number;
|
|
|
|
|
observedFrameId?: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeSetTargetRequest = {
|
|
|
|
|
actorEntityKey: string;
|
|
|
|
|
targetEntityKey: string;
|
|
|
|
|
observedFrameId?: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeDaemonCommandReceipt = CultMeshOperationReceipt & {
|
|
|
|
|
commandId: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeSelectedObjectRequest = {
|
|
|
|
|
entityIndex: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeViewportFeedRequest = {
|
|
|
|
|
viewport: Viewport;
|
|
|
|
|
selectedEntityIndex?: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaRuntimeViewportFeedSnapshot = {
|
|
|
|
|
viewport: ViewportResponse;
|
2026-06-30 19:14:49 +01:00
|
|
|
selectedObject: SelectedObjectDocument | null;
|
|
|
|
|
inventory: InventoryDocument | null;
|
|
|
|
|
daemonHealth: DaemonHealthDocument;
|
|
|
|
|
authorityStatus: AuthorityStatusDocument;
|
|
|
|
|
starbridgeSession: StarbridgeSessionDocument;
|
|
|
|
|
assetManifest: AssetManifestDocument;
|
2026-06-25 18:09:33 +01:00
|
|
|
receivedAtUtc: string;
|
|
|
|
|
sampleMs: number;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
${renderRtsDocumentTypes("Viewport")}
|
2026-06-25 18:09:33 +01:00
|
|
|
|
2026-07-05 00:32:49 +01:00
|
|
|
export type AetheriaEveSurfaceRequest = {
|
|
|
|
|
surfaceId?: string;
|
|
|
|
|
recordKey?: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-10 07:17:55 +01:00
|
|
|
export type EveEmbeddedDocumentRequest = {
|
|
|
|
|
documentId: string;
|
|
|
|
|
schemaId?: string;
|
|
|
|
|
slotId?: string;
|
|
|
|
|
presentationKind?: string;
|
|
|
|
|
context?: Record<string, unknown>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type EveResolvedDocument = {
|
|
|
|
|
documentId: string;
|
|
|
|
|
schemaId: string;
|
|
|
|
|
document?: unknown;
|
|
|
|
|
surface?: AetheriaMenuSurfaceTree;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-10 06:48:19 +01:00
|
|
|
export type EveProviderAdvertisement = {
|
|
|
|
|
providerId: string;
|
|
|
|
|
title: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
surfaces: Array<{
|
|
|
|
|
surfaceId: string;
|
|
|
|
|
key: string;
|
|
|
|
|
transport: string;
|
|
|
|
|
status: string;
|
|
|
|
|
}>;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-05 00:32:49 +01:00
|
|
|
export type AetheriaEveCommandRequest = {
|
|
|
|
|
providerId?: string;
|
|
|
|
|
surfaceId?: string;
|
|
|
|
|
command?: string;
|
|
|
|
|
clientId?: string;
|
|
|
|
|
issuedAtUtc?: string;
|
|
|
|
|
payload?: Record<string, unknown>;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-04 23:22:05 +01:00
|
|
|
export type AetheriaMenuSurfaceDocument = {
|
|
|
|
|
providerId: string;
|
|
|
|
|
providerKind: string;
|
|
|
|
|
title: string;
|
|
|
|
|
version: number;
|
|
|
|
|
updatedAtUtc: string;
|
|
|
|
|
surface: AetheriaMenuSurfaceTree;
|
|
|
|
|
commands: AetheriaMenuSurfaceCommand[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaMenuSurfaceTree = {
|
|
|
|
|
id: string;
|
|
|
|
|
root: AetheriaMenuSurfaceComponent;
|
|
|
|
|
styles: AetheriaMenuStyleToken[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaMenuSurfaceComponent = {
|
|
|
|
|
id: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
props: Record<string, string>;
|
2026-07-05 01:17:27 +01:00
|
|
|
embeddedDocuments?: AetheriaMenuEmbeddedDocumentSlot[];
|
2026-07-04 23:22:05 +01:00
|
|
|
children: AetheriaMenuSurfaceComponent[];
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-05 01:17:27 +01:00
|
|
|
export type AetheriaMenuEmbeddedDocumentSlot = {
|
|
|
|
|
slotId: string;
|
|
|
|
|
documentId: string;
|
|
|
|
|
schemaId: string;
|
|
|
|
|
presentationKind: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-04 23:22:05 +01:00
|
|
|
export type AetheriaMenuStyleToken = {
|
|
|
|
|
name: string;
|
|
|
|
|
value: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AetheriaMenuSurfaceCommand = {
|
|
|
|
|
command: string;
|
|
|
|
|
label: string;
|
|
|
|
|
transport: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-25 18:09:33 +01:00
|
|
|
export type AetheriaRtsApi = {
|
|
|
|
|
mapViewport(request: Viewport): Promise<ViewportResponse>;
|
|
|
|
|
objectsViewport(request: Viewport): Promise<ObjectsViewportResponse>;
|
|
|
|
|
gravityViewport(request: Viewport): Promise<GravityViewportResponse>;
|
2026-07-05 01:17:27 +01:00
|
|
|
renderSplatsViewport(request: Viewport): Promise<RenderSplatsViewportResponse>;
|
2026-06-30 19:14:49 +01:00
|
|
|
selectedObject(request: AetheriaRuntimeSelectedObjectRequest): Promise<SelectedObjectDocument>;
|
|
|
|
|
inventory(request: AetheriaRuntimeSelectedObjectRequest): Promise<InventoryDocument>;
|
|
|
|
|
daemonHealth(): Promise<DaemonHealthDocument>;
|
|
|
|
|
authorityStatus(): Promise<AuthorityStatusDocument>;
|
|
|
|
|
starbridgeSession(): Promise<StarbridgeSessionDocument>;
|
|
|
|
|
assetManifest(): Promise<AssetManifestDocument>;
|
2026-06-25 18:09:33 +01:00
|
|
|
watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): () => void;
|
|
|
|
|
setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
|
|
|
|
setTarget(request: AetheriaRuntimeSetTargetRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
|
|
|
|
surfaceCatalog(): Promise<CultMeshSurfaceCatalogDiagnostic>;
|
|
|
|
|
surfaceCatalogIndex(): Promise<CultMeshSurfaceCatalogIndexDiagnostic>;
|
2026-07-05 00:32:49 +01:00
|
|
|
eveSurface(request: AetheriaEveSurfaceRequest): Promise<AetheriaMenuSurfaceDocument>;
|
2026-07-10 06:48:19 +01:00
|
|
|
eveProviderAdvertisement(): Promise<EveProviderAdvertisement>;
|
2026-07-10 07:17:55 +01:00
|
|
|
eveDocument(request: EveEmbeddedDocumentRequest): Promise<EveResolvedDocument | undefined>;
|
2026-07-05 00:32:49 +01:00
|
|
|
submitEveCommand(request: AetheriaEveCommandRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
2026-07-04 23:22:05 +01:00
|
|
|
windowControl(action: "minimize" | "maximize" | "close"): Promise<void>;
|
2026-06-25 18:09:33 +01:00
|
|
|
health(): Promise<unknown>;
|
|
|
|
|
};
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
function renderRtsDocumentTypes(viewportTypeName) {
|
2026-06-25 18:09:33 +01:00
|
|
|
return `export type ViewportResponse = {
|
|
|
|
|
schema: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
publishedAtUtc: string;
|
|
|
|
|
simulationTimeSeconds: number;
|
|
|
|
|
runId: string;
|
|
|
|
|
zoneIndex: number;
|
|
|
|
|
zoneName: string;
|
|
|
|
|
currentEntityKey: string;
|
|
|
|
|
viewport: ${viewportTypeName};
|
|
|
|
|
controlledEntityIndices: number[];
|
|
|
|
|
objects: ViewObject[];
|
|
|
|
|
gravityInfluences: GravityInfluence[];
|
|
|
|
|
bodies: BodyView[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type ObjectsViewportResponse = {
|
|
|
|
|
schema: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
publishedAtUtc: string;
|
|
|
|
|
simulationTimeSeconds: number;
|
|
|
|
|
runId: string;
|
|
|
|
|
zoneIndex: number;
|
|
|
|
|
zoneName: string;
|
|
|
|
|
currentEntityKey: string;
|
|
|
|
|
viewport: ${viewportTypeName};
|
|
|
|
|
controlledEntityIndices: number[];
|
|
|
|
|
objects: ViewObject[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type GravityViewportResponse = {
|
|
|
|
|
schema: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
publishedAtUtc: string;
|
|
|
|
|
simulationTimeSeconds: number;
|
|
|
|
|
runId: string;
|
|
|
|
|
zoneIndex: number;
|
|
|
|
|
zoneName: string;
|
|
|
|
|
viewport: ${viewportTypeName};
|
|
|
|
|
gravityInfluences: GravityInfluence[];
|
|
|
|
|
bodies: BodyView[];
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-05 01:17:27 +01:00
|
|
|
export type RenderSplatsViewportResponse = {
|
|
|
|
|
schema: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
publishedAtUtc: string;
|
|
|
|
|
simulationTimeSeconds: number;
|
|
|
|
|
runId: string;
|
|
|
|
|
zoneIndex: number;
|
|
|
|
|
zoneName: string;
|
|
|
|
|
viewport: ${viewportTypeName};
|
|
|
|
|
layers: RenderSplatLayerDefinition[];
|
|
|
|
|
splats: RenderSplatSoa;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type RenderSplatLayerDefinition = {
|
|
|
|
|
layerKey: string;
|
|
|
|
|
displayName: string;
|
|
|
|
|
channel: number;
|
|
|
|
|
blendMode: string;
|
|
|
|
|
graphicsFormat: string;
|
|
|
|
|
clearBeforeDraw: boolean;
|
|
|
|
|
clearR: number;
|
|
|
|
|
clearG: number;
|
|
|
|
|
clearB: number;
|
|
|
|
|
clearA: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type RenderSplatSoa = {
|
|
|
|
|
count: number;
|
|
|
|
|
centerX: number[];
|
|
|
|
|
centerY: number[];
|
|
|
|
|
halfExtentX: number[];
|
|
|
|
|
halfExtentY: number[];
|
|
|
|
|
rotationCos: number[];
|
|
|
|
|
rotationSin: number[];
|
|
|
|
|
channel: number[];
|
|
|
|
|
falloff: number[];
|
|
|
|
|
valueR: number[];
|
|
|
|
|
valueG: number[];
|
|
|
|
|
valueB: number[];
|
|
|
|
|
valueA: number[];
|
|
|
|
|
sourceKey: string[];
|
|
|
|
|
layerIndex: number[];
|
|
|
|
|
sourceKind: number[];
|
|
|
|
|
frequencyX: number[];
|
|
|
|
|
frequencyY: number[];
|
|
|
|
|
phaseX: number[];
|
|
|
|
|
phaseY: number[];
|
|
|
|
|
animationSpeed: number[];
|
|
|
|
|
sourceFlags: number[];
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-25 18:09:33 +01:00
|
|
|
export type ViewObject = {
|
|
|
|
|
entityIndex: number;
|
|
|
|
|
entityKey: string;
|
|
|
|
|
displayName: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
factionKey: string;
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
z: number;
|
|
|
|
|
directionX: number;
|
|
|
|
|
directionY: number;
|
|
|
|
|
velocityX: number;
|
|
|
|
|
velocityY: number;
|
|
|
|
|
controlled: boolean;
|
|
|
|
|
targetEntityIndex: number;
|
|
|
|
|
isActive: boolean;
|
|
|
|
|
visibility: number;
|
2026-06-28 01:18:21 +01:00
|
|
|
iconAsset: AssetRef;
|
2026-06-25 18:09:33 +01:00
|
|
|
status: EntityStatus;
|
|
|
|
|
inventory: InventoryItem[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type EntityStatus = {
|
|
|
|
|
hull: number;
|
|
|
|
|
shield: number;
|
|
|
|
|
heat: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type InventoryItem = {
|
|
|
|
|
source: string;
|
|
|
|
|
itemKey: string;
|
|
|
|
|
quantity: number;
|
|
|
|
|
quality: number;
|
|
|
|
|
durability: number;
|
|
|
|
|
enabled: boolean;
|
2026-06-28 01:18:21 +01:00
|
|
|
iconAsset: AssetRef;
|
2026-06-25 18:09:33 +01:00
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type SelectedObjectDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
schema: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
runId: string;
|
|
|
|
|
zoneIndex: number;
|
|
|
|
|
entityIndex: number;
|
|
|
|
|
selected: ViewObject | null;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type InventoryDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
schema: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
runId: string;
|
|
|
|
|
zoneIndex: number;
|
|
|
|
|
entityIndex: number;
|
|
|
|
|
entityKey: string;
|
|
|
|
|
items: InventoryItem[];
|
|
|
|
|
equipment: InventoryItem[];
|
|
|
|
|
cargo: InventoryItem[];
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type DaemonHealthDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
schema: string;
|
|
|
|
|
daemonId: string;
|
|
|
|
|
verseId: string;
|
|
|
|
|
publishedAtUtc: string;
|
|
|
|
|
statePath: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
observedCommandCount: number;
|
|
|
|
|
appliedCommandCount: number;
|
|
|
|
|
rejectedCommandCount: number;
|
|
|
|
|
status: string;
|
|
|
|
|
publicationSource: string;
|
|
|
|
|
transport: string;
|
|
|
|
|
commandBoundaryPath: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type AuthorityStatusDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
schema: string;
|
|
|
|
|
verseId: string;
|
|
|
|
|
policyId: string;
|
|
|
|
|
ruleVersion: string;
|
|
|
|
|
hostRuntimeId: string;
|
|
|
|
|
defaultMode: string;
|
|
|
|
|
updatedAtUtc: string;
|
2026-06-30 19:14:49 +01:00
|
|
|
rules: AuthorityRuleDocument[];
|
2026-06-25 18:09:33 +01:00
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type AuthorityRuleDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
ruleId: string;
|
|
|
|
|
subjectPrefix: string;
|
|
|
|
|
claimKinds: string[];
|
|
|
|
|
mode: string;
|
|
|
|
|
runtimeIds: string[];
|
|
|
|
|
leaseScope: string;
|
|
|
|
|
priority: number;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type StarbridgeSessionDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
schema: string;
|
|
|
|
|
frameId: number;
|
|
|
|
|
publishedAtUtc: string;
|
|
|
|
|
sessionId: string;
|
|
|
|
|
scenarioId: string;
|
|
|
|
|
scenarioName: string;
|
|
|
|
|
runId: string;
|
|
|
|
|
zoneIndex: number;
|
|
|
|
|
zoneName: string;
|
|
|
|
|
phase: string;
|
|
|
|
|
currentWaveIndex: number;
|
2026-06-30 19:14:49 +01:00
|
|
|
baseStatus: StarbridgeBaseStatusDocument;
|
|
|
|
|
stationStock: StarbridgeStationStockItemDocument[];
|
|
|
|
|
waveForecast: StarbridgeWaveForecastDocument[];
|
|
|
|
|
runtimeRoles: StarbridgeRuntimeRoleDocument[];
|
2026-06-25 18:09:33 +01:00
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type StarbridgeBaseStatusDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
entityKey: string;
|
|
|
|
|
displayName: string;
|
|
|
|
|
hull: number;
|
|
|
|
|
shield: number;
|
|
|
|
|
heat: number;
|
|
|
|
|
isActive: boolean;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type StarbridgeStationStockItemDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
itemKey: string;
|
|
|
|
|
quantity: number;
|
|
|
|
|
quality: number;
|
|
|
|
|
durability: number;
|
|
|
|
|
source: string;
|
2026-06-28 01:18:21 +01:00
|
|
|
iconAsset: AssetRef;
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type AssetManifestDocument = {
|
2026-06-28 01:18:21 +01:00
|
|
|
schema: string;
|
|
|
|
|
publishedAtUtc: string;
|
|
|
|
|
runId: string;
|
|
|
|
|
baseUri: string;
|
|
|
|
|
assets: AssetManifestEntry[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AssetManifestEntry = {
|
|
|
|
|
ref: AssetRef;
|
|
|
|
|
sizeBytes: number;
|
|
|
|
|
width: number;
|
|
|
|
|
height: number;
|
|
|
|
|
tags: string[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type AssetRef = {
|
|
|
|
|
assetKey: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
uri: string;
|
|
|
|
|
transport: string;
|
|
|
|
|
contentHash: string;
|
|
|
|
|
mimeType: string;
|
|
|
|
|
metadata: Record<string, string>;
|
2026-06-25 18:09:33 +01:00
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type StarbridgeWaveForecastDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
waveIndex: number;
|
|
|
|
|
displayName: string;
|
|
|
|
|
attackerKeys: string[];
|
|
|
|
|
bossKey: string;
|
|
|
|
|
recoveredTechnologyKeys: string[];
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
export type StarbridgeRuntimeRoleDocument = {
|
2026-06-25 18:09:33 +01:00
|
|
|
runtimeId: string;
|
|
|
|
|
role: string;
|
|
|
|
|
entityKey: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type GravityInfluence = {
|
|
|
|
|
bodyKey: string;
|
|
|
|
|
orbitKey: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
radius: number;
|
|
|
|
|
gravityDepth: number;
|
|
|
|
|
gravityDepthExponent: number;
|
|
|
|
|
waveRadius: number;
|
|
|
|
|
waveDepth: number;
|
|
|
|
|
waveSpeed: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type BodyView = {
|
|
|
|
|
bodyKey: string;
|
|
|
|
|
orbitKey: string;
|
|
|
|
|
name: string;
|
|
|
|
|
kind: string;
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
radius: number;
|
|
|
|
|
isAsteroidBelt: boolean;
|
2026-07-04 23:22:05 +01:00
|
|
|
iconAsset: AssetRef;
|
2026-07-05 15:12:29 +01:00
|
|
|
iconSize: number;
|
2026-06-25 18:09:33 +01:00
|
|
|
};
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderIpcChannelLines() {
|
|
|
|
|
return ipcChannels
|
|
|
|
|
.map(([name, channel]) => ` ${name}: "${channel}",`)
|
|
|
|
|
.join("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderPreload() {
|
|
|
|
|
const channelLines = renderIpcChannelLines();
|
2026-07-10 07:41:03 +01:00
|
|
|
const eveProviderMethods = new Set([
|
|
|
|
|
"eveSurface",
|
|
|
|
|
"eveProviderAdvertisement",
|
|
|
|
|
"eveDocument",
|
|
|
|
|
"submitEveCommand",
|
|
|
|
|
"windowControl",
|
|
|
|
|
]);
|
2026-06-25 18:09:33 +01:00
|
|
|
const apiLines = ipcChannels
|
2026-07-04 23:22:05 +01:00
|
|
|
.filter(([name]) => name !== "viewportFeedUpdate" &&
|
2026-07-10 07:41:03 +01:00
|
|
|
name !== "viewportFeedStop" &&
|
|
|
|
|
!eveProviderMethods.has(name))
|
2026-06-25 18:09:33 +01:00
|
|
|
.map(([name]) => {
|
|
|
|
|
if (name === "viewportFeed") {
|
|
|
|
|
return ` watchViewportFeed: (request, callback) => {
|
|
|
|
|
const subscriptionId = \`viewport-feed-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2)}\`;
|
|
|
|
|
const listener = (_event, message) => {
|
|
|
|
|
if (message?.subscriptionId === subscriptionId) {
|
|
|
|
|
callback(message.snapshot);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
ipcRenderer.on(channels.viewportFeedUpdate, listener);
|
|
|
|
|
void ipcRenderer.invoke(channels.viewportFeed, { subscriptionId, feed: request });
|
|
|
|
|
return () => {
|
|
|
|
|
ipcRenderer.off(channels.viewportFeedUpdate, listener);
|
|
|
|
|
void ipcRenderer.invoke(channels.viewportFeedStop, subscriptionId);
|
|
|
|
|
};
|
|
|
|
|
},`;
|
|
|
|
|
}
|
|
|
|
|
const hasNoArguments = name === "health" ||
|
|
|
|
|
name === "daemonHealth" ||
|
|
|
|
|
name === "authorityStatus" ||
|
|
|
|
|
name === "starbridgeSession" ||
|
2026-06-28 01:18:21 +01:00
|
|
|
name === "assetManifest" ||
|
2026-06-25 18:09:33 +01:00
|
|
|
name === "surfaceCatalog" ||
|
|
|
|
|
name === "surfaceCatalogIndex";
|
|
|
|
|
const parameters = hasNoArguments ? "()" : "request";
|
|
|
|
|
const invokeArguments = hasNoArguments ? `channels.${name}` : `channels.${name}, request`;
|
|
|
|
|
return ` ${name}: ${parameters} => ipcRenderer.invoke(${invokeArguments}),`;
|
|
|
|
|
})
|
|
|
|
|
.join("\n");
|
|
|
|
|
|
|
|
|
|
return `// <auto-generated />
|
|
|
|
|
// Generated by scripts/generate-rts-bindings.mjs from the RTS IPC surface contract.
|
|
|
|
|
|
|
|
|
|
const { contextBridge, ipcRenderer } = require("electron");
|
2026-07-10 07:41:03 +01:00
|
|
|
const { installEveProviderBridge } = require("@gamecult/eve-electron/preload");
|
2026-06-25 18:09:33 +01:00
|
|
|
|
|
|
|
|
const channels = {
|
|
|
|
|
${channelLines}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
contextBridge.exposeInMainWorld("aetheriaRts", {
|
|
|
|
|
${apiLines}
|
|
|
|
|
});
|
2026-07-10 07:41:03 +01:00
|
|
|
|
|
|
|
|
installEveProviderBridge({
|
|
|
|
|
contextBridge,
|
|
|
|
|
ipcRenderer,
|
|
|
|
|
channels: {
|
|
|
|
|
providerAdvertisement: channels.eveProviderAdvertisement,
|
|
|
|
|
surface: channels.eveSurface,
|
|
|
|
|
document: channels.eveDocument,
|
|
|
|
|
submitCommand: channels.submitEveCommand,
|
|
|
|
|
windowControl: channels.windowControl,
|
|
|
|
|
},
|
|
|
|
|
});
|
2026-06-25 18:09:33 +01:00
|
|
|
`;
|
|
|
|
|
}
|