From fdcff0f2d6a452580dff9fd015f0bd763bbb7cc7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 11 Jul 2026 00:51:50 +0100 Subject: [PATCH] feat(starbridge): publish commander and pilot surfaces --- Aetheria.Rts.Web/Electron/main.ts | 16 ++-- Aetheria.State.Daemon/Program.cs | 18 ++++- ...AetheriaRuntimeDaemonGameSurfaceBuilder.cs | 75 ++++++++++++++++++- .../AetheriaRuntimeEveSurfaceCatalog.cs | 3 +- .../Runtime/AetheriaRuntimeVerseClient.cs | 3 + .../eve/aetheria.provider-advertisement.json | 32 +++++++- scripts/verify-eve-conformance-pack.mjs | 12 ++- 7 files changed, 142 insertions(+), 17 deletions(-) diff --git a/Aetheria.Rts.Web/Electron/main.ts b/Aetheria.Rts.Web/Electron/main.ts index 8e05e5ba3..3082c68ca 100644 --- a/Aetheria.Rts.Web/Electron/main.ts +++ b/Aetheria.Rts.Web/Electron/main.ts @@ -75,7 +75,7 @@ app.whenReady().then(async () => { }, renderer: rendererIndex, runtimeId: "eve-electron-aetheria", - surfaceId: process.env.EVE_SURFACE_ID || "aetheria.game", + surfaceId: process.env.EVE_SURFACE_ID || "aetheria.starbridge.commander", window: { show: !electronSmoke, backgroundColor: "#0b1016", @@ -87,10 +87,10 @@ app.whenReady().then(async () => { if (electronSmoke) { await withTimeout(mainWindow.webContents.executeJavaScript("window.eveProvider.providerAdvertisement()"), 10000, "provider advertisement smoke"); writeElectronSmokeResult({ ok: false, stage: "provider-advertisement-readable" }); - await withTimeout(mainWindow.webContents.executeJavaScript("window.eveProvider.surface({ surfaceId: 'aetheria.game' })"), 10000, "surface smoke"); + await withTimeout(mainWindow.webContents.executeJavaScript("window.eveProvider.surface({ surfaceId: 'aetheria.starbridge.commander' })"), 10000, "surface smoke"); writeElectronSmokeResult({ ok: false, stage: "provider-surface-readable" }); await withTimeout(mainWindow.webContents.executeJavaScript(`window.eveProvider.submitCommand({ - providerId: 'aetheria.daemon', surfaceId: 'aetheria.game', command: 'aetheria.daemon.commands.SensorPing', + providerId: 'aetheria.daemon', surfaceId: 'aetheria.starbridge.commander', command: 'aetheria.daemon.commands.SensorPing', clientId: 'aetheria-electron-smoke-preflight', payload: {} })`), 10000, "command receipt smoke"); writeElectronSmokeResult({ ok: false, stage: "provider-command-receipt-readable" }); @@ -160,7 +160,7 @@ async function runElectronSmoke(window: BrowserWindow): Promise): boolean { result.worldSceneReady === true && Number(result.worldEntityCount) > 0 && Number(result.controlledWorldEntityCount) === 1 && - eveHostText.includes("Daemon Frame") && - eveHostText.includes("Typed Command Boundary") && - arrayValue(providerAdvertisement?.surfaces).some(surface => objectValue(surface)?.surfaceId === "aetheria.game") && - objectValue(eveSurface?.surface)?.id === "aetheria.game" && + eveHostText.includes("Station Stock") && + eveHostText.includes("Wave Forecast") && + arrayValue(providerAdvertisement?.surfaces).some(surface => objectValue(surface)?.surfaceId === "aetheria.starbridge.commander") && + objectValue(eveSurface?.surface)?.id === "aetheria.starbridge.commander" && stringValue(renderSplatsResolved?.documentId).length > 0 && stringValue(gravityResolved?.documentId).length > 0 && stringValue(objectsResolved?.documentId).length > 0 && diff --git a/Aetheria.State.Daemon/Program.cs b/Aetheria.State.Daemon/Program.cs index 5b8fc1c26..b001fd484 100644 --- a/Aetheria.State.Daemon/Program.cs +++ b/Aetheria.State.Daemon/Program.cs @@ -1348,6 +1348,14 @@ static async Task PublishDaemonApiDocumentsAsync( await node.MutableDocument(AetheriaRuntimeVerseRecordKeys.DaemonGameSurface) .ReplaceAsync(AetheriaRuntimeSurfaceDocuments.ToPortableSurface(gameSurface)) .ConfigureAwait(false); + var commanderSurface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildCommander( + result.Frame, + result.Health ?? new AetheriaRuntimeDaemonHealthDocument(), + result.CommandBoundary ?? AetheriaRuntimeDaemonCommandBoundaryDocument.Create(options.DaemonId), + result.StarbridgeSessionSummary); + await node.MutableDocument(AetheriaRuntimeVerseRecordKeys.StarbridgeCommanderSurface) + .ReplaceAsync(AetheriaRuntimeSurfaceDocuments.ToPortableSurface(commanderSurface)) + .ConfigureAwait(false); if (result.GameTuiSurface != null) await node.MutableDocument(AetheriaRuntimeVerseRecordKeys.DaemonGameTuiSurface) .ReplaceAsync(AetheriaRuntimeSurfaceDocuments.ToPortableSurface(result.GameTuiSurface)) @@ -1398,12 +1406,20 @@ static EveProviderAdvertisementDocument BuildCoreProviderAdvertisement( new[] { new EveAdvertisedSurface( - AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId, + AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId, EveSurfaceDocument.SchemaId, AetheriaRuntimeVerseRecordKeys.DaemonGameSurface.ToString(), "cultmesh-record", "active", "interactive-world", + interaction), + new EveAdvertisedSurface( + AetheriaRuntimeDaemonGameSurfaceBuilder.CommanderSurfaceId, + EveSurfaceDocument.SchemaId, + AetheriaRuntimeVerseRecordKeys.StarbridgeCommanderSurface.ToString(), + "cultmesh-record", + "active", + "interactive-world", interaction) }, Array.Empty()); diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs index 445fc0bc1..461e9f2ab 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs @@ -9,8 +9,10 @@ namespace GameCult.Aetheria.State.Verse { public static class AetheriaRuntimeDaemonGameSurfaceBuilder { - public const string SurfaceId = "aetheria.game"; - public const string TuiSurfaceId = "aetheria.game.tui"; + public const string PilotSurfaceId = "aetheria.starbridge.pilot"; + public const string CommanderSurfaceId = "aetheria.starbridge.commander"; + public const string SurfaceId = PilotSurfaceId; + public const string TuiSurfaceId = "aetheria.starbridge.pilot.tui"; public static AetheriaRuntimeSurfaceDocument Build( AetheriaRuntimeDaemonFrameDocument frame, @@ -126,6 +128,75 @@ namespace GameCult.Aetheria.State.Verse .ToArray()); } + public static AetheriaRuntimeSurfaceDocument BuildCommander( + AetheriaRuntimeDaemonFrameDocument frame, + AetheriaRuntimeDaemonHealthDocument health, + AetheriaRuntimeDaemonCommandBoundaryDocument commandBoundary, + AetheriaRuntimeStarbridgeSessionSummaryDocument? starbridge = null) + { + frame ??= new AetheriaRuntimeDaemonFrameDocument(); + health ??= new AetheriaRuntimeDaemonHealthDocument(); + commandBoundary ??= AetheriaRuntimeDaemonCommandBoundaryDocument.Create(frame.DaemonId); + starbridge ??= AetheriaRuntimeStarbridgeDocuments.SessionSummary(frame); + var run = frame.Run ?? new AetheriaRuntimeRunCheckpointCommit(); + var zone = FindCurrentZone(run); + + var root = SurfaceRoot( + "aetheria.starbridge.commander.root", + StrategicWorldSurface("aetheria.starbridge.commander.world", run, zone), + BuildStarbridgeStationStockCard(starbridge), + BuildStarbridgeWaveForecastCard(starbridge), + BuildStarbridgeRuntimeRolesCard(starbridge)); + + return new AetheriaRuntimeSurfaceDocument( + "aetheria.daemon", + "game.daemon", + "Starbridge Commander", + frame.FrameId, + frame.PublishedAtUtc, + new AetheriaRuntimeSurfaceTree( + CommanderSurfaceId, + root, + Array.Empty()), + commandBoundary.Commands + .Where(entry => AetheriaRuntimeDaemonSurfaceCommandCatalog.IsArgumentlessCommand(entry.Kind)) + .Select(entry => new AetheriaRuntimeSurfaceCommandTemplate( + AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(entry.Kind), + AetheriaRuntimeDaemonSurfaceCommandCatalog.Label(entry.Kind), + "cultmesh")) + .ToArray()); + } + + private static AetheriaRuntimeSurfaceComponent StrategicWorldSurface( + string id, + AetheriaRuntimeRunCheckpointCommit run, + AetheriaRuntimeZoneSnapshotCommit zone) + { + var props = new Dictionary(StringComparer.Ordinal) + { + ["label"] = string.IsNullOrWhiteSpace(zone.Name) ? "Starbridge" : zone.Name, + ["statePointerId"] = AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest.ToString(), + ["assetManifest"] = AetheriaRuntimeVerseRecordKeys.EveAssetCatalog.ToString(), + ["inputProfile"] = "rts.pointer-keyboard.v1", + ["cameraRig"] = "rts.top-down.v1", + ["zoneIndex"] = zone.ZoneIndex.ToString(CultureInfo.InvariantCulture), + ["runId"] = run.RunId ?? "" + }; + var entities = (zone.Entities ?? Array.Empty()) + .Where(entity => entity != null && entity.IsActive) + .Select(entity => PlayableWorldEntity(entity, run, zone, run.CurrentEntityKey)) + .ToArray(); + return new AetheriaRuntimeSurfaceComponent( + id, + "world.scene2d", + props, + entities, + AetheriaRuntimeSurfaceStateBindings.FromProps(props), + Array.Empty(), + Layout(("position", "absolute"), ("inset", "0"), ("width", "100%"), ("height", "100%")), + new Dictionary { ["background"] = "transparent" }); + } + private static AetheriaRuntimeSurfaceComponent BuildStarbridgeStationStockCard( AetheriaRuntimeStarbridgeSessionSummaryDocument summary) { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveSurfaceCatalog.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveSurfaceCatalog.cs index e7f1bf123..b5ae4a659 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveSurfaceCatalog.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveSurfaceCatalog.cs @@ -110,7 +110,8 @@ namespace GameCult.Aetheria.State.Verse AetheriaRuntimePlayerSettingsCommands.IncrementSignificantDigits, AetheriaRuntimePlayerSettingsCommands.CycleNebulaQuality, AetheriaRuntimePlayerSettingsCommands.ToggleShowAsteroidsInMinimap), - Surface(AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId, "Aetheria Daemon", AetheriaRuntimeVerseRecordKeys.DaemonGameSurface.ToString(), "player", "Authoritative daemon game surface.", providerId: "aetheria.daemon", providerKind: "game.daemon", transport: "cultmesh-record", surfaceKind: "interactive-world", worldInteraction: WorldInteraction("provider-authored-world-surface"), requiresPlugins: FieldPluginRequirements()), + Surface(AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId, "Starbridge Pilot", AetheriaRuntimeVerseRecordKeys.DaemonGameSurface.ToString(), "pilot", "Embodied Starbridge pilot projection.", providerId: "aetheria.daemon", providerKind: "game.daemon", transport: "cultmesh-record", surfaceKind: "interactive-world", worldInteraction: WorldInteraction("provider-authored-world-surface"), requiresPlugins: FieldPluginRequirements()), + Surface(AetheriaRuntimeDaemonGameSurfaceBuilder.CommanderSurfaceId, "Starbridge Commander", AetheriaRuntimeVerseRecordKeys.StarbridgeCommanderSurface.ToString(), "commander", "Strategic Starbridge commander projection.", providerId: "aetheria.daemon", providerKind: "game.daemon", transport: "cultmesh-record", surfaceKind: "interactive-world", worldInteraction: WorldInteraction("provider-authored-world-surface"), requiresPlugins: FieldPluginRequirements()), Surface(AetheriaRuntimeDaemonGameSurfaceBuilder.TuiSurfaceId, "Aetheria Daemon TUI", AetheriaRuntimeVerseRecordKeys.DaemonGameTuiSurface.ToString(), "agent", "Compact daemon game surface for dense inspection.", providerId: "aetheria.daemon", providerKind: "game.daemon", transport: "cultmesh-record", surfaceKind: "interactive-world", worldInteraction: WorldInteraction("provider-authored-world-surface")), Surface(AetheriaRuntimeDaemonEditorSurfaceBuilder.SurfaceId, "Aetheria Daemon Editor", AetheriaRuntimeVerseRecordKeys.DaemonEditorSurface.ToString(), "operator", "Daemon editor and surface inventory.", providerId: "aetheria.daemon", providerKind: "editor.daemon", transport: "cultmesh-record", surfaceKind: "interactive-world-editor", worldInteraction: WorldInteraction("provider-authored-world-editor-surface")), Surface(AetheriaRuntimeDaemonEditorSurfaceBuilder.TuiSurfaceId, "Aetheria Daemon Editor TUI", AetheriaRuntimeVerseRecordKeys.DaemonEditorTuiSurface.ToString(), "agent", "Compact daemon editor and surface inventory.", providerId: "aetheria.daemon", providerKind: "editor.daemon", transport: "cultmesh-record", surfaceKind: "interactive-world-editor", worldInteraction: WorldInteraction("provider-authored-world-editor-surface")), diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs index 6d4bd6ffd..0db1460d8 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs @@ -67,6 +67,9 @@ namespace GameCult.Aetheria.State.Verse public static CultRecordKey DaemonGameSurface { get; } = new CultRecordKey("eve:surface:aetheria.daemon.game"); + public static CultRecordKey StarbridgeCommanderSurface { get; } = + new CultRecordKey("eve:surface:aetheria.starbridge.commander"); + public static CultRecordKey DaemonGameTuiSurface { get; } = new CultRecordKey("eve:surface:aetheria.daemon.game.tui"); diff --git a/conformance/eve/aetheria.provider-advertisement.json b/conformance/eve/aetheria.provider-advertisement.json index 5a82a294f..ba98c1e5e 100644 --- a/conformance/eve/aetheria.provider-advertisement.json +++ b/conformance/eve/aetheria.provider-advertisement.json @@ -95,11 +95,11 @@ }, { "schema": "gamecult.eve.surface.v1", - "surfaceId": "aetheria.daemon.game", + "surfaceId": "aetheria.starbridge.pilot", "key": "AetheriaRuntimeVerseRecordKeys.DaemonGameSurface", "transport": "cultmesh-record", "status": "available", - "audience": "player", + "audience": "pilot", "mode": "interactive", "surfaceKind": "interactive-world", "interactionModel": "provider-command-receipts", @@ -115,11 +115,35 @@ "loweringTargets": ["web-reference", "unity-uitoolkit", "unity-scene", "electron-shell", "tui"], "ownership": "provider-owns-world-state-assets-command-acceptance-and-receipts" }, - "summary": "Authoritative daemon game surface consumed by Unity, Electron, and web lowerers." + "summary": "Embodied Starbridge pilot surface lowered by generic Eve runtimes." }, { "schema": "gamecult.eve.surface.v1", - "surfaceId": "aetheria.daemon.game.tui", + "surfaceId": "aetheria.starbridge.commander", + "key": "AetheriaRuntimeVerseRecordKeys.StarbridgeCommanderSurface", + "transport": "cultmesh-record", + "status": "available", + "audience": "commander", + "mode": "interactive", + "surfaceKind": "interactive-world", + "interactionModel": "provider-command-receipts", + "worldInteraction": { + "projectionKind": "provider-authored-world-surface", + "stateSchemas": [ + "aetheria.world_state.v1", + "aetheria.runtime.daemon.frame.v1", + "aetheria.runtime.daemon.soa_view.v1" + ], + "commandBoundary": "aetheria.daemon.commands", + "receiptSchema": "aetheria.eve_command_acceptance_status.v1", + "loweringTargets": ["web-reference", "unity-uitoolkit", "unity-scene", "electron-shell", "tui"], + "ownership": "provider-owns-world-state-assets-command-acceptance-and-receipts" + }, + "summary": "Strategic Starbridge surface lowered full-screen by Electron or embedded by Unity." + }, + { + "schema": "gamecult.eve.surface.v1", + "surfaceId": "aetheria.starbridge.pilot.tui", "key": "AetheriaRuntimeVerseRecordKeys.DaemonGameTuiSurface", "transport": "cultmesh-record", "status": "available", diff --git a/scripts/verify-eve-conformance-pack.mjs b/scripts/verify-eve-conformance-pack.mjs index 1cfed374d..6f53f00a5 100644 --- a/scripts/verify-eve-conformance-pack.mjs +++ b/scripts/verify-eve-conformance-pack.mjs @@ -10,7 +10,17 @@ assert.equal(pack.ownerRepo, "Aetheria"); const advertisement = read(pack.advertisementPath); assert.equal(advertisement.providerId, pack.providerId); -assert.ok(advertisement.surfaces.some((surface) => surface.surfaceId === "aetheria.daemon.game" && surface.worldInteraction?.projectionKind === "provider-authored-world-surface")); +const starbridgeSurfaces = advertisement.surfaces.filter((surface) => + surface.surfaceId === "aetheria.starbridge.commander" || + surface.surfaceId === "aetheria.starbridge.pilot"); +assert.equal(starbridgeSurfaces.length, 2); +assert.deepEqual(new Set(starbridgeSurfaces.map((surface) => surface.audience)), new Set(["commander", "pilot"])); +for (const surface of starbridgeSurfaces) { + assert.equal(surface.worldInteraction?.projectionKind, "provider-authored-world-surface"); + assert.equal(surface.worldInteraction?.commandBoundary, "aetheria.daemon.commands"); + assert.ok(surface.worldInteraction?.loweringTargets.includes("electron-shell")); + assert.ok(surface.worldInteraction?.loweringTargets.includes("unity-scene")); +} assert.ok(advertisement.surfaces.some((surface) => surface.surfaceId === "aetheria.daemon.editor" && surface.worldInteraction?.projectionKind === "provider-authored-world-editor-surface")); for (const fixture of pack.fixtures) {