Advertise Aetheria Eve surface catalog

This commit is contained in:
Meta Vens 2026-07-02 20:36:28 +01:00
commit f13f70d92b
6 changed files with 194 additions and 42 deletions

View file

@ -2,6 +2,7 @@ using Aetheria.State;
using Aetheria.State.Documents;
using GameCult.Aetheria.State.Verse;
using GameCult.Caching;
using GameCult.Eve.Surface;
using GameCult.Mesh;
using GameCult.Networking;
using MessagePack;

View file

@ -323,44 +323,9 @@ public static class AetheriaEveSurfaceDocuments
Summary = "Aetheria daemon editor Eve TUI surface record"
}
],
Surfaces =
[
new EveProviderSurfaceRef
{
SurfaceId = CatalogSurfaceId,
Key = CatalogSurfaceKey
},
new EveProviderSurfaceRef
{
SurfaceId = OperationsSurfaceId,
Key = OperationsSurfaceKey
},
new EveProviderSurfaceRef
{
SurfaceId = PlayerSettingsSurfaceId,
Key = PlayerSettingsSurfaceKey
},
new EveProviderSurfaceRef
{
SurfaceId = AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId,
Key = DaemonGameSurfaceKey
},
new EveProviderSurfaceRef
{
SurfaceId = AetheriaRuntimeDaemonGameSurfaceBuilder.TuiSurfaceId,
Key = DaemonGameTuiSurfaceKey
},
new EveProviderSurfaceRef
{
SurfaceId = AetheriaRuntimeDaemonEditorSurfaceBuilder.SurfaceId,
Key = DaemonEditorSurfaceKey
},
new EveProviderSurfaceRef
{
SurfaceId = AetheriaRuntimeDaemonEditorSurfaceBuilder.TuiSurfaceId,
Key = DaemonEditorTuiSurfaceKey
}
],
Surfaces = AetheriaRuntimeEveSurfaceCatalog.All
.Select(ToProviderSurfaceRef)
.ToArray(),
Commands =
[
new EveProviderCommandRef
@ -413,6 +378,17 @@ public static class AetheriaEveSurfaceDocuments
};
}
private static EveProviderSurfaceRef ToProviderSurfaceRef(AetheriaRuntimeEveSurfaceAdvertisement surface)
{
return new EveProviderSurfaceRef
{
SurfaceId = surface.SurfaceId,
Key = surface.RecordRef,
Transport = surface.Transport,
Status = surface.Status
};
}
private static string LatestTimestamp(params string?[] timestamps)
{
var latest = "";

View file

@ -263,6 +263,10 @@ namespace GameCult.Aetheria.State.Verse
[Key(21)]
public string AssetManifestRecordRef { get; set; } = "";
[Key(22)]
public IReadOnlyList<AetheriaRuntimeEveSurfaceAdvertisement> EveSurfaces { get; set; } =
Array.Empty<AetheriaRuntimeEveSurfaceAdvertisement>();
public static AetheriaRuntimeDaemonProviderAdvertisementDocument Create(
string stateFilePath,
string daemonId,
@ -291,6 +295,7 @@ namespace GameCult.Aetheria.State.Verse
CultMeshAddress = string.IsNullOrWhiteSpace(cultMeshAddress)
? "cultmesh://aetheria.local/eve/providers/aetheria.daemon"
: cultMeshAddress,
EveSurfaces = AetheriaRuntimeEveSurfaceCatalog.All,
PublishedSchemas = new[]
{
AetheriaRuntimeDaemonSchemas.Frame,
@ -313,6 +318,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeDaemonSchemas.Inventory,
AetheriaRuntimeDaemonSchemas.GameSurface,
AetheriaRuntimeDaemonSchemas.EditorSurface,
"gamecult.eve.surface.v1",
AetheriaRuntimeDaemonSchemas.Command,
AetheriaRuntimeDaemonSchemas.StarbridgeScenario,
AetheriaRuntimeDaemonSchemas.StarbridgeSession,

View file

@ -106,6 +106,16 @@ namespace GameCult.Aetheria.State.Verse
designerSurfaces
.Where(surface => surface != null)
.Select((surface, index) => DesignerSurfaceRow(index, surface))
.ToArray())),
Node(
"aetheria.daemon.editor.eve_surfaces",
"card",
new[] { ("title", "Advertised Eve Surfaces") },
Row(
"aetheria.daemon.editor.eve_surfaces.rows",
provider.EveSurfaces
.Where(surface => surface != null)
.Select((surface, index) => AdvertisedSurfaceRow(index, surface))
.ToArray()))),
Array.Empty<AetheriaRuntimeSurfaceStyleToken>()),
commands: commandBoundary.Commands
@ -134,6 +144,24 @@ namespace GameCult.Aetheria.State.Verse
});
}
private static AetheriaRuntimeSurfaceComponent AdvertisedSurfaceRow(
int index,
AetheriaRuntimeEveSurfaceAdvertisement surface)
{
return Node(
$"aetheria.daemon.editor.eve_surfaces.{index}",
"inspector.kv",
new[]
{
("title", surface.Title),
("surfaceId", surface.SurfaceId),
("record", surface.RecordRef),
("status", surface.Status),
("audience", surface.Audience),
("provider", surface.ProviderId)
});
}
private static AetheriaRuntimeSurfaceComponent CommandRow(
int index,
AetheriaRuntimeDaemonCommandBoundaryEntry entry)

View file

@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MessagePack;
#nullable enable
namespace GameCult.Aetheria.State.Verse
{
[MessagePackObject]
public sealed class AetheriaRuntimeEveSurfaceAdvertisement
{
[Key(0)]
public string SurfaceId { get; set; } = "";
[Key(1)]
public string Title { get; set; } = "";
[Key(2)]
public string ProviderId { get; set; } = "aetheria";
[Key(3)]
public string ProviderKind { get; set; } = "game.runtime";
[Key(4)]
public string RecordRef { get; set; } = "";
[Key(5)]
public string Transport { get; set; } = "cultmesh-managed";
[Key(6)]
public string Status { get; set; } = "available";
[Key(7)]
public string Audience { get; set; } = "operator";
[Key(8)]
public string Mode { get; set; } = "interactive";
[Key(9)]
public string Summary { get; set; } = "";
[Key(10)]
public IReadOnlyList<string> Commands { get; set; } = Array.Empty<string>();
}
public static class AetheriaRuntimeEveSurfaceCatalog
{
private const string SurfaceSchema = "gamecult.eve.surface.v1";
private static readonly AetheriaRuntimeEveSurfaceAdvertisement[] Surfaces =
{
Surface(AetheriaRuntimeCatalogCommands.SurfaceId, "Aetheria Catalog", "eve:surface:aetheria.catalog.operator", "operator", "Aetheria catalog operator surface.", AetheriaRuntimeCatalogCommands.Refresh),
Surface(AetheriaRuntimeOperationsCommands.SurfaceId, "Aetheria Operations", "eve:surface:aetheria.operations", "operator", "Daemon operation and Eve command acceptance surface.", AetheriaRuntimeOperationsCommands.Refresh),
Surface(AetheriaRuntimePlayerSettingsCommands.SurfaceId, "Player Settings", "eve:surface:aetheria.player_settings", "player", "Typed player settings surface.",
AetheriaRuntimePlayerSettingsCommands.Refresh,
AetheriaRuntimePlayerSettingsCommands.CycleTemperatureUnit,
AetheriaRuntimePlayerSettingsCommands.DecrementSignificantDigits,
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"),
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"),
Surface(AetheriaRuntimeDaemonEditorSurfaceBuilder.SurfaceId, "Aetheria Daemon Editor", AetheriaRuntimeVerseRecordKeys.DaemonEditorSurface.ToString(), "operator", "Daemon editor and surface inventory.", providerId: "aetheria.daemon", providerKind: "editor.daemon", transport: "cultmesh-record"),
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"),
Surface(AetheriaRuntimeMainMenuCommands.RootSurfaceId, "Main Menu", "", "player", "Runtime main menu root.", status: "client-contextual"),
Surface(AetheriaRuntimeMainMenuCommands.SettingsSurfaceId, "Main Menu Settings", "", "player", "Runtime main menu settings shell.", status: "client-contextual"),
Surface(AetheriaRuntimeMainMenuCommands.InputSettingsSurfaceId, "Main Menu Input Settings", "", "player", "Main menu input settings handoff shell.", status: "client-contextual"),
Surface(AetheriaRuntimeMainMenuCommands.PlayerSettingsShellSurfaceId, "Main Menu Player Settings", "", "player", "Main menu player settings shell.", status: "client-contextual"),
Surface(AetheriaRuntimeMainMenuCommands.VerseSettingsShellSurfaceId, "Main Menu Verse Settings", "", "player", "Main menu Verse settings shell.", status: "client-contextual"),
Surface(AetheriaRuntimeInputSettingsCommands.SurfaceId, "Input Settings", "", "player", "Runtime input binding surface.", status: "client-contextual"),
Surface(AetheriaRuntimeVerseHostCommands.SurfaceId, "Verse Host Settings", "", "operator", "Verse host visibility and discovery surface.", status: "client-contextual"),
Surface(AetheriaRuntimeLoadoutTemplateCommands.SurfaceId, "Loadout Templates", "", "player", "Loadout template save surface.", status: "client-contextual"),
Surface(AetheriaRuntimeClientTargetCommands.SurfaceId, "Client Target", "", "operator", "Local client target and Verse discovery surface.", status: "client-contextual"),
Surface(AetheriaRuntimeLocalStorySurfaceBuilder.SurfaceId, "Local Story", "", "player", "Local runtime story/menu surface.", status: "client-contextual"),
Surface(AetheriaRuntimeZoneDetailsSurfaceBuilder.SurfaceId, "Zone Details", "", "player", "Selected zone details surface.", status: "contextual"),
Surface(AetheriaRuntimeInventoryPanelSurfaceBuilder.SurfaceId, "Inventory Panel", "", "player", "Current inventory panel surface.", status: "contextual"),
Surface(AetheriaRuntimeInventoryDropdownSurfaceBuilder.SurfaceId, "Inventory Dropdown", "", "player", "Inventory navigation dropdown surface.", status: "contextual"),
Surface(AetheriaRuntimeShipSettingsSurfaceBuilder.SurfaceId, "Current Ship Settings", "", "player", "Current ship settings surface.", status: "contextual"),
Surface(AetheriaRuntimeCargoItemDetailsSurfaceBuilder.SurfaceId, "Cargo Item Details", "", "player", "Selected cargo item details surface.", status: "contextual"),
Surface(AetheriaRuntimeEquippedItemDetailsSurfaceBuilder.SurfaceId, "Equipped Item Details", "", "player", "Selected equipped item details surface.", status: "contextual"),
Surface(AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.SurfaceId, "Trade Cargo Selector", "", "player", "Trade cargo target selector surface.", status: "contextual"),
Surface(AetheriaRuntimeTradeInteractionSurfaceBuilder.FilterSurfaceId, "Trade Filter Selector", "", "player", "Trade filter selector surface.", status: "contextual"),
Surface(AetheriaRuntimeTradeInteractionSurfaceBuilder.RowActionSurfaceId, "Trade Row Actions", "", "player", "Trade row action selector surface.", status: "contextual"),
Surface(AetheriaRuntimeTradeItemDetailsSurfaceBuilder.SurfaceId, "Trade Item Details", "", "player", "Selected trade item details surface.", status: "contextual"),
Surface(AetheriaRuntimeStatRecipeCommands.SurfaceId, "Stat Recipes", "", "designer", "Designer stat recipe surface.", status: "designer-preview"),
Surface(AetheriaRuntimeTradeValuePolicySurfaceBuilder.SurfaceId, "Trade Value Policy", "", "designer", "Designer trade value policy surface.", status: "designer-preview")
};
public static IReadOnlyList<AetheriaRuntimeEveSurfaceAdvertisement> All => Surfaces;
public static IReadOnlyList<string> SurfaceSchemas { get; } = new[] { SurfaceSchema };
private static AetheriaRuntimeEveSurfaceAdvertisement Surface(
string surfaceId,
string title,
string recordRef,
string audience,
string summary,
params string[] commands)
{
return Surface(surfaceId, title, recordRef, audience, summary, "aetheria", "game.runtime", "cultmesh-managed", "available", commands);
}
private static AetheriaRuntimeEveSurfaceAdvertisement Surface(
string surfaceId,
string title,
string recordRef,
string audience,
string summary,
string providerId = "aetheria",
string providerKind = "game.runtime",
string transport = "cultmesh-managed",
string status = "available",
params string[] commands)
{
return new AetheriaRuntimeEveSurfaceAdvertisement
{
SurfaceId = surfaceId,
Title = title,
ProviderId = providerId,
ProviderKind = providerKind,
RecordRef = recordRef ?? "",
Transport = transport,
Status = status,
Audience = audience,
Mode = "interactive",
Summary = summary,
Commands = commands?.Where(command => !string.IsNullOrWhiteSpace(command)).ToArray() ?? Array.Empty<string>()
};
}
}
}

View file

@ -527,10 +527,15 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
narrow typed command buttons, not renderer-local fields.
- `AetheriaProviderAdvertisementProjector` publishes
`gamecult.eve.provider_advertisement.v1` for the `aetheria` provider,
advertising the catalog, operations, and player-settings surfaces, command
boundaries, schemas, and `.cc` witness path. Its Verse/service identity now
comes from typed daemon host settings instead of hardcoded projector fields.
This is the discovery map for Odin/Eve, not a health page.
advertising the shared runtime Eve surface catalog, command boundaries,
schemas, and `.cc` witness path. Its Verse/service identity now comes from
typed daemon host settings instead of hardcoded projector fields. This is the
discovery map for Odin/Eve, not a health page.
- `AetheriaRuntimeEveSurfaceCatalog` is the single inventory of known Aetheria
Eve panels. The daemon advertisement and Odin-facing provider advertisement
both derive from it, so daemon GUI/TUI records, operator records, menu shells,
inventory/trade panels, contextual inspectors, and designer previews are
visible without making Unity or Electron the owner.
- `AetheriaCatalogSurfaceProjector` now emits the first provider-owned Eve
surface from typed catalog state. The importer materializes a
`gamecult.eve.surface.v1` catalog operator document at
@ -1678,6 +1683,9 @@ First Aetheria surfaces to publish:
surface and advertise their command boundary through the provider ad.
- Done: publish an Eve provider advertisement so Odin/Eve can discover
Aetheria surfaces and command boundaries through typed state.
- Done: make the daemon/provider advertisements derive from the shared
`AetheriaRuntimeEveSurfaceCatalog`, so Odin/Eir can index every known
Aetheria Eve panel instead of only the daemon game/editor surfaces.
- Done: stop legacy catalog pull/read paths from writing entries back to
their source backing store.
- Done: delete legacy catalog backing-store write/realtime APIs.