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"), resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssetDocuments.cs"), resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs"), resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTradeItemQueries.cs"), resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs"), resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeGameViewportDocuments.cs"), resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeRenderSplatDocuments.cs"), 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"], ["renderSplatsViewport", "aetheria-rts:render-splats-viewport"], ["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"], ["assetManifest", "aetheria-rts:asset-manifest"], ["setMoveVector", "aetheria-rts:set-move-vector"], ["setTarget", "aetheria-rts:set-target"], ["surfaceCatalog", "aetheria-rts:surface-catalog"], ["surfaceCatalogIndex", "aetheria-rts:surface-catalog-index"], ["eveSurface", "eve:surface"], ["eveProviderAdvertisement", "eve:provider-advertisement"], ["eveDocument", "eve:embedded-document"], ["submitEveCommand", "eve:submit-command"], ["windowControl", "eve-electron:window-control"], ["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", }, { exportName: "aetheriaRuntimeAssetRef", className: "AetheriaRuntimeAssetRef", }, { exportName: "aetheriaRuntimeAssetManifestDocument", className: "AetheriaRuntimeAssetManifestDocument", schemaName: "assetManifest", }, { exportName: "aetheriaRuntimeAssetManifestEntry", className: "AetheriaRuntimeAssetManifestEntry", }, { exportName: "aetheriaRuntimeGameViewportDocument", className: "AetheriaRuntimeGameViewportDocument", schemaName: "gameViewport", }, { exportName: "aetheriaRuntimeObjectsViewportDocument", className: "AetheriaRuntimeObjectsViewportDocument", schemaName: "objectsViewport", }, { exportName: "aetheriaRuntimeGravityViewportDocument", className: "AetheriaRuntimeGravityViewportDocument", schemaName: "gravityViewport", }, { exportName: "aetheriaRuntimeRenderSplatsViewportDocument", className: "AetheriaRuntimeRenderSplatsViewportDocument", schemaName: "renderSplatsViewport", }, { exportName: "aetheriaRuntimeRenderSplatLayerDefinition", className: "AetheriaRuntimeRenderSplatLayerDefinition", }, { exportName: "aetheriaRuntimeRenderSplatSoa", className: "AetheriaRuntimeRenderSplatSoa", }, { 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", }, { exportName: "aetheriaRuntimeProjectileCommit", className: "AetheriaRuntimeProjectileCommit", }, { exportName: "aetheriaRuntimeBodySnapshotCommit", className: "AetheriaRuntimeBodySnapshotCommit", }, { exportName: "aetheriaRuntimeSunVisualCommit", className: "AetheriaRuntimeSunVisualCommit", }, { exportName: "aetheriaRuntimeEntityStatGridCommit", className: "AetheriaRuntimeEntityStatGridCommit", }, { exportName: "aetheriaRuntimeCargoBayLoadoutCommit", className: "AetheriaRuntimeCargoBayLoadoutCommit", }, { exportName: "aetheriaRuntimeLoadoutItemSlotCommit", className: "AetheriaRuntimeLoadoutItemSlotCommit", }, { exportName: "aetheriaRuntimeLoadoutItemCommit", className: "AetheriaRuntimeLoadoutItemCommit", }, { exportName: "aetheriaRuntimeGameViewportBounds", className: "AetheriaRuntimeViewportBounds", }, { exportName: "aetheriaRuntimeGameViewportObject", className: "AetheriaRuntimeViewportObject", }, { exportName: "aetheriaRuntimeRtsEntityStatus", className: "AetheriaRuntimeEntityStatus", }, { exportName: "aetheriaRuntimeRtsInventoryItem", className: "AetheriaRuntimeInventoryItem", }, { exportName: "aetheriaRuntimeRtsGravityInfluence", className: "AetheriaRuntimeGravityInfluence", }, { exportName: "aetheriaRuntimeRtsBodyView", className: "AetheriaRuntimeBodyView", }, { 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) { if (existing !== generated) { 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 { return readFileSync(path, "utf8"); } 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"); return `// \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`; } 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; selectedObject: SelectedObjectDocument | null; inventory: InventoryDocument | null; daemonHealth: DaemonHealthDocument; authorityStatus: AuthorityStatusDocument; starbridgeSession: StarbridgeSessionDocument; assetManifest: AssetManifestDocument; receivedAtUtc: string; sampleMs: number; }; export type AetheriaEveSurfaceRequest = { surfaceId?: string; recordKey?: string; }; export type EveEmbeddedDocumentRequest = { documentId: string; schemaId?: string; slotId?: string; presentationKind?: string; context?: Record; }; export type EveResolvedDocument = { documentId: string; schemaId: string; document?: unknown; surface?: AetheriaMenuSurfaceTree; }; export type EveProviderAdvertisement = { providerId: string; title: string; kind: string; surfaces: Array<{ surfaceId: string; key: string; transport: string; status: string; }>; }; export type AetheriaEveCommandRequest = { providerId?: string; surfaceId?: string; command?: string; clientId?: string; issuedAtUtc?: string; payload?: Record; }; 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; layout?: Record; style?: Record; embeddedDocuments?: AetheriaMenuEmbeddedDocumentSlot[]; children: AetheriaMenuSurfaceComponent[]; }; export type AetheriaMenuEmbeddedDocumentSlot = { slotId: string; documentId: string; schemaId: string; presentationKind: string; }; export type AetheriaMenuStyleToken = { name: string; value: string; }; export type AetheriaMenuSurfaceCommand = { command: string; label: string; transport: string; }; export type AetheriaRtsMainClient = { mapViewport(request: CultMeshViewportRequest): Promise; objectsViewport(request: CultMeshViewportRequest): Promise; gravityViewport(request: CultMeshViewportRequest): Promise; renderSplatsViewport(request: CultMeshViewportRequest): Promise; selectedObject(request: AetheriaRuntimeSelectedObjectRequest): Promise; inventory(request: AetheriaRuntimeSelectedObjectRequest): Promise; daemonHealth(): Promise; authorityStatus(): Promise; starbridgeSession(): Promise; assetManifest(): Promise; watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): CultMeshUnsubscribe; setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise; setTarget(request: AetheriaRuntimeSetTargetRequest): Promise; surfaceCatalogDiagnostics(): CultMeshSurfaceCatalogDiagnostic; surfaceCatalogIndexDiagnostics(): CultMeshSurfaceCatalogIndexDiagnostic; }; export type AetheriaRtsHealthProvider = () => unknown; export function registerAetheriaRtsIpcHandlers( ipcMain: IpcMain, getClient: () => AetheriaRtsMainClient, getHealth: AetheriaRtsHealthProvider, ): void { const viewportFeedSubscriptions = new Map(); 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)); ipcMain.handle(AetheriaRtsIpcChannels.renderSplatsViewport, async (_event, request: CultMeshViewportRequest) => getClient().renderSplatsViewport(request)); 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()); ipcMain.handle(AetheriaRtsIpcChannels.assetManifest, async () => getClient().assetManifest()); 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; export type AetheriaRuntimeViewportQueryExecutor = ( request: CultMeshViewportRequest, context: CultMeshQueryContext, ) => Promise; export type AetheriaRuntimeQueryExecutor = ( request: TParameters, context: CultMeshQueryContext, ) => Promise; export type AetheriaRuntimeGameQueryExecutors = { mapViewport: AetheriaRuntimeViewportQueryExecutor; objectsViewport: AetheriaRuntimeViewportQueryExecutor; gravityViewport: AetheriaRuntimeViewportQueryExecutor; renderSplatsViewport: AetheriaRuntimeViewportQueryExecutor; selectedObject: AetheriaRuntimeQueryExecutor; inventory: AetheriaRuntimeQueryExecutor; daemonHealth: AetheriaRuntimeQueryExecutor; authorityStatus: AetheriaRuntimeQueryExecutor; starbridgeSession: AetheriaRuntimeQueryExecutor; assetManifest: AetheriaRuntimeQueryExecutor; }; export type AetheriaRuntimeGameQueryWatchers = Partial<{ mapViewport: CultMeshQueryWatcher; objectsViewport: CultMeshQueryWatcher; gravityViewport: CultMeshQueryWatcher; renderSplatsViewport: CultMeshQueryWatcher; selectedObject: CultMeshQueryWatcher; inventory: CultMeshQueryWatcher; daemonHealth: CultMeshQueryWatcher; authorityStatus: CultMeshQueryWatcher; starbridgeSession: CultMeshQueryWatcher; assetManifest: CultMeshQueryWatcher; }>; export type AetheriaRuntimeGameQueryDiagnostic = CultMeshQuerySurfaceDiagnostic; export type AetheriaRuntimeGameLiveFeedDiagnostic = CultMeshLiveFeedDiagnostic; export type AetheriaRuntimeGameSurfaceCatalogDiagnostic = CultMeshSurfaceCatalogDiagnostic; export type AetheriaRuntimeGameOperationHandles = ReturnType; export type AetheriaRuntimeGameDocuments = ReturnType; export type AetheriaRuntimeGameStatePointers = AetheriaRuntimeGameDocuments; export type AetheriaRuntimeGameDocumentResolvers = Partial<{ daemonFrame: (context: CultMeshQueryContext) => Promise; daemonHealth: (context: CultMeshQueryContext) => Promise; authorityPolicy: (context: CultMeshQueryContext) => Promise; starbridgeSession: (context: CultMeshQueryContext) => Promise; assetManifest: (context: CultMeshQueryContext) => Promise; }>; export type AetheriaRuntimeGameStatePointerResolvers = AetheriaRuntimeGameDocumentResolvers; export const AetheriaRuntimeGameDocumentSources = { daemonFrame: CultMesh.querySource("daemon:aetheria.frame.latest.v1", { schemaId: AetheriaRtsSchemas.daemonFrame, description: "latest daemon frame" }), daemonHealth: CultMesh.querySource("daemon:aetheria.health.latest.v1", { schemaId: AetheriaRtsSchemas.daemonHealth, description: "latest daemon health" }), authorityPolicy: CultMesh.querySource("daemon:aetheria.authority.policy.latest.v1", { schemaId: AetheriaRtsSchemas.verseAuthorityPolicy, description: "latest Verse authority policy" }), starbridgeSession: CultMesh.querySource("daemon:aetheria.starbridge.session.latest.v1", { schemaId: AetheriaRtsSchemas.starbridgeSessionSummary, description: "latest Starbridge session summary" }), assetManifest: CultMesh.querySource("daemon:aetheria.asset_manifest.latest.v1", { schemaId: AetheriaRtsSchemas.assetManifest, description: "latest daemon asset manifest" }) } as const; export function createAetheriaRuntimeGameDocuments( routeHint: CultMeshRouteHint = CultMesh.routeHint(), resolvers: AetheriaRuntimeGameDocumentResolvers = {}, ) { return { daemonFrame: CultMesh.document( AetheriaRuntimeGameDocumentSources.daemonFrame.sourceId, { schemaId: AetheriaRtsSchemas.daemonFrame }, async (context) => resolvers.daemonFrame?.(context), { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], }, ), daemonHealth: CultMesh.document( AetheriaRuntimeGameDocumentSources.daemonHealth.sourceId, { schemaId: AetheriaRtsSchemas.daemonHealth }, async (context) => resolvers.daemonHealth?.(context), { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonHealth], }, ), authorityPolicy: CultMesh.document( AetheriaRuntimeGameDocumentSources.authorityPolicy.sourceId, { schemaId: AetheriaRtsSchemas.verseAuthorityPolicy }, async (context) => resolvers.authorityPolicy?.(context), { routeHint, sources: [AetheriaRuntimeGameDocumentSources.authorityPolicy], }, ), starbridgeSession: CultMesh.document( AetheriaRuntimeGameDocumentSources.starbridgeSession.sourceId, { schemaId: AetheriaRtsSchemas.starbridgeSessionSummary }, async (context) => resolvers.starbridgeSession?.(context), { routeHint, sources: [AetheriaRuntimeGameDocumentSources.starbridgeSession], }, ), assetManifest: CultMesh.document( AetheriaRuntimeGameDocumentSources.assetManifest.sourceId, { schemaId: AetheriaRtsSchemas.assetManifest }, async (context) => resolvers.assetManifest?.(context), { routeHint, sources: [AetheriaRuntimeGameDocumentSources.assetManifest], }, ), } as const; } export const createAetheriaRuntimeGameStatePointers = createAetheriaRuntimeGameDocuments; export function createAetheriaRuntimeGameQueryHandles( executors: AetheriaRuntimeGameQueryExecutors, routeHint: CultMeshRouteHint = CultMesh.routeHint(), watchers: AetheriaRuntimeGameQueryWatchers = {}, ) { return { mapViewport: CultMesh.query( AetheriaRtsSchemas.gameViewport, executors.mapViewport, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.mapViewport }, ), objectsViewport: CultMesh.query( AetheriaRtsSchemas.objectsViewport, executors.objectsViewport, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.objectsViewport }, ), gravityViewport: CultMesh.query( AetheriaRtsSchemas.gravityViewport, executors.gravityViewport, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.gravityViewport }, ), renderSplatsViewport: CultMesh.query( AetheriaRtsSchemas.renderSplatsViewport, executors.renderSplatsViewport, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.renderSplatsViewport }, ), selectedObject: CultMesh.query( AetheriaRtsSchemas.selectedObject, executors.selectedObject, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.selectedObject }, ), inventory: CultMesh.query( AetheriaRtsSchemas.inventory, executors.inventory, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonFrame], watchQuery: watchers.inventory }, ), daemonHealth: CultMesh.query( AetheriaRtsSchemas.daemonHealth, executors.daemonHealth, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.daemonHealth], watchQuery: watchers.daemonHealth }, ), authorityStatus: CultMesh.query( AetheriaRtsSchemas.verseAuthorityPolicy, executors.authorityStatus, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.authorityPolicy], watchQuery: watchers.authorityStatus }, ), starbridgeSession: CultMesh.query( AetheriaRtsSchemas.starbridgeSessionSummary, executors.starbridgeSession, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.starbridgeSession], watchQuery: watchers.starbridgeSession }, ), assetManifest: CultMesh.query( AetheriaRtsSchemas.assetManifest, executors.assetManifest, { routeHint, sources: [AetheriaRuntimeGameDocumentSources.assetManifest], watchQuery: watchers.assetManifest }, ), } as const; } export function describeAetheriaRuntimeGameQueryHandles( handles: ReturnType, ) { return { 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), } as const; } export function describeAetheriaRuntimeGameSurfaceCatalog( handles: ReturnType, operations?: AetheriaRuntimeGameOperationHandles, documents: AetheriaRuntimeGameDocuments = createAetheriaRuntimeGameDocuments(), ): AetheriaRuntimeGameSurfaceCatalogDiagnostic { return CultMesh.describeSurfaceCatalog( "gamecult.aetheria.surfaces.v1", [ CultMesh.describeSurface(documents.daemonFrame), CultMesh.describeSurface(documents.daemonHealth), CultMesh.describeSurface(documents.authorityPolicy), CultMesh.describeSurface(documents.starbridgeSession), CultMesh.describeSurface(documents.assetManifest), CultMesh.describeSurface(handles.mapViewport), CultMesh.describeSurface(handles.objectsViewport), CultMesh.describeSurface(handles.gravityViewport), CultMesh.describeSurface(handles.renderSplatsViewport), CultMesh.describeSurface(handles.selectedObject), CultMesh.describeSurface(handles.inventory), CultMesh.describeSurface(handles.daemonHealth), CultMesh.describeSurface(handles.authorityStatus), CultMesh.describeSurface(handles.starbridgeSession), CultMesh.describeSurface(handles.assetManifest), ...(operations ? [ CultMesh.describeSurface(operations.setMoveVector), CultMesh.describeSurface(operations.setTarget), ] : []), ], ); } function describeAetheriaRuntimeGameQuerySurface(surface: { queryId: string; routeHint: CultMeshRouteHint; sources: readonly CultMeshQuerySource[]; }): AetheriaRuntimeGameQueryDiagnostic { return CultMesh.describeQuerySurface(surface); } export function describeAetheriaRuntimeGameLiveFeedSurface(surface: { feedId: string; routeHint: CultMeshRouteHint; sources: readonly CultMeshQuerySource[]; }): AetheriaRuntimeGameLiveFeedDiagnostic { return CultMesh.describeLiveFeed(surface); } export function createAetheriaRuntimeGameOperationHandles( sendCommand: AetheriaRuntimeDaemonCommandSender, createCommandId: () => string = createDefaultCommandId, ) { return { setMoveVector: CultMesh.operation( "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( "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; } export type AetheriaRuntimeGameVerseHandles = ReturnType; export function createAetheriaRuntimeGameVerseHandles( queryVerse: CultMeshVerseContext, commandVerse: CultMeshVerseContext, queries: ReturnType, operations: AetheriaRuntimeGameOperationHandles, documents: AetheriaRuntimeGameDocuments = createAetheriaRuntimeGameDocuments(), ) { const mapViewport = CultMesh.bindQuery(queryVerse, queries.mapViewport); const objectsViewport = CultMesh.bindQuery(queryVerse, queries.objectsViewport); const gravityViewport = CultMesh.bindQuery(queryVerse, queries.gravityViewport); const renderSplatsViewport = CultMesh.bindQuery(queryVerse, queries.renderSplatsViewport); 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); const assetManifest = CultMesh.bindQuery(queryVerse, queries.assetManifest); 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); const assetManifestDocument = CultMesh.bindDocument(queryVerse, documents.assetManifest); 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), }, renderSplats: { within: (request: CultMeshViewportRequest) => renderSplatsViewport.execute(request), watchWithin: ( request: CultMeshViewportRequest, callback: (value: RenderSplatsViewportResponse) => void, ) => renderSplatsViewport.watch(request, callback), }, }), 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: { frame: () => daemonFrameDocument.latest(), health: () => daemonHealthDocument.latest(), authorityPolicy: () => authorityPolicyDocument.latest(), starbridgeSession: () => starbridgeSessionDocument.latest(), assetManifest: () => assetManifestDocument.latest(), }, health: () => daemonHealth.execute(undefined), authorityStatus: () => authorityStatus.execute(undefined), starbridgeSession: () => starbridgeSession.execute(undefined), assetManifest: () => assetManifest.execute(undefined), }, } 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(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 `// // 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; selectedObject: SelectedObjectDocument | null; inventory: InventoryDocument | null; daemonHealth: DaemonHealthDocument; authorityStatus: AuthorityStatusDocument; starbridgeSession: StarbridgeSessionDocument; assetManifest: AssetManifestDocument; receivedAtUtc: string; sampleMs: number; }; ${renderRtsDocumentTypes("Viewport")} export type AetheriaEveSurfaceRequest = { surfaceId?: string; recordKey?: string; }; export type EveEmbeddedDocumentRequest = { documentId: string; schemaId?: string; slotId?: string; presentationKind?: string; context?: Record; }; export type EveResolvedDocument = { documentId: string; schemaId: string; document?: unknown; surface?: AetheriaMenuSurfaceTree; }; export type EveProviderAdvertisement = { providerId: string; title: string; kind: string; surfaces: Array<{ surfaceId: string; key: string; transport: string; status: string; }>; }; export type AetheriaEveCommandRequest = { providerId?: string; surfaceId?: string; command?: string; clientId?: string; issuedAtUtc?: string; payload?: Record; }; 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; embeddedDocuments?: AetheriaMenuEmbeddedDocumentSlot[]; children: AetheriaMenuSurfaceComponent[]; }; export type AetheriaMenuEmbeddedDocumentSlot = { slotId: string; documentId: string; schemaId: string; presentationKind: string; }; export type AetheriaMenuStyleToken = { name: string; value: string; }; export type AetheriaMenuSurfaceCommand = { command: string; label: string; transport: string; }; export type AetheriaRtsApi = { mapViewport(request: Viewport): Promise; objectsViewport(request: Viewport): Promise; gravityViewport(request: Viewport): Promise; renderSplatsViewport(request: Viewport): Promise; selectedObject(request: AetheriaRuntimeSelectedObjectRequest): Promise; inventory(request: AetheriaRuntimeSelectedObjectRequest): Promise; daemonHealth(): Promise; authorityStatus(): Promise; starbridgeSession(): Promise; assetManifest(): Promise; watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): () => void; setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise; setTarget(request: AetheriaRuntimeSetTargetRequest): Promise; surfaceCatalog(): Promise; surfaceCatalogIndex(): Promise; eveSurface(request: AetheriaEveSurfaceRequest): Promise; eveProviderAdvertisement(): Promise; eveDocument(request: EveEmbeddedDocumentRequest): Promise; submitEveCommand(request: AetheriaEveCommandRequest): Promise; windowControl(action: "minimize" | "maximize" | "close"): Promise; health(): Promise; }; `; } function renderRtsDocumentTypes(viewportTypeName) { 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[]; }; 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[]; }; 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; iconAsset: AssetRef; 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; iconAsset: AssetRef; }; export type SelectedObjectDocument = { schema: string; frameId: number; runId: string; zoneIndex: number; entityIndex: number; selected: ViewObject | null; }; export type InventoryDocument = { schema: string; frameId: number; runId: string; zoneIndex: number; entityIndex: number; entityKey: string; items: InventoryItem[]; equipment: InventoryItem[]; cargo: InventoryItem[]; }; export type DaemonHealthDocument = { 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; }; export type AuthorityStatusDocument = { schema: string; verseId: string; policyId: string; ruleVersion: string; hostRuntimeId: string; defaultMode: string; updatedAtUtc: string; rules: AuthorityRuleDocument[]; }; export type AuthorityRuleDocument = { ruleId: string; subjectPrefix: string; claimKinds: string[]; mode: string; runtimeIds: string[]; leaseScope: string; priority: number; }; export type StarbridgeSessionDocument = { schema: string; frameId: number; publishedAtUtc: string; sessionId: string; scenarioId: string; scenarioName: string; runId: string; zoneIndex: number; zoneName: string; phase: string; currentWaveIndex: number; baseStatus: StarbridgeBaseStatusDocument; stationStock: StarbridgeStationStockItemDocument[]; waveForecast: StarbridgeWaveForecastDocument[]; runtimeRoles: StarbridgeRuntimeRoleDocument[]; }; export type StarbridgeBaseStatusDocument = { entityKey: string; displayName: string; hull: number; shield: number; heat: number; isActive: boolean; }; export type StarbridgeStationStockItemDocument = { itemKey: string; quantity: number; quality: number; durability: number; source: string; iconAsset: AssetRef; }; export type AssetManifestDocument = { 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; }; export type StarbridgeWaveForecastDocument = { waveIndex: number; displayName: string; attackerKeys: string[]; bossKey: string; recoveredTechnologyKeys: string[]; }; export type StarbridgeRuntimeRoleDocument = { 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; iconAsset: AssetRef; iconSize: number; }; `; } function renderIpcChannelLines() { return ipcChannels .map(([name, channel]) => ` ${name}: "${channel}",`) .join("\n"); } function renderPreload() { const channelLines = renderIpcChannelLines(); const eveProviderMethods = new Set([ "eveSurface", "eveProviderAdvertisement", "eveDocument", "submitEveCommand", "windowControl", ]); const apiLines = ipcChannels .filter(([name]) => name !== "viewportFeedUpdate" && name !== "viewportFeedStop" && !eveProviderMethods.has(name)) .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" || name === "assetManifest" || 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 `// // Generated by scripts/generate-rts-bindings.mjs from the RTS IPC surface contract. const { contextBridge, ipcRenderer } = require("electron"); const { installEveProviderBridge } = require("@gamecult/eve-electron/preload"); const channels = { ${channelLines} }; contextBridge.exposeInMainWorld("aetheriaRts", { ${apiLines} }); installEveProviderBridge({ contextBridge, ipcRenderer, channels: { providerAdvertisement: channels.eveProviderAdvertisement, surface: channels.eveSurface, document: channels.eveDocument, submitCommand: channels.submitEveCommand, windowControl: channels.windowControl, }, }); `; }