Advertise Aetheria Unity world interaction metadata

This commit is contained in:
Meta Vens 2026-07-09 22:05:03 +01:00
commit 5d388948e8
5 changed files with 145 additions and 16 deletions

View file

@ -510,6 +510,23 @@ public class DaemonRuntimeDocumentTests
CollectionAssert.Contains(providerAdvertisement.PublishedSchemas, AetheriaRuntimeDaemonSchemas.CommandBoundary);
CollectionAssert.Contains(providerAdvertisement.PublishedSchemas, AetheriaRuntimeDaemonSchemas.GameSurface);
CollectionAssert.Contains(providerAdvertisement.PublishedSchemas, AetheriaRuntimeDaemonSchemas.EditorSurface);
var advertisedGameSurface = providerAdvertisement.EveSurfaces.Single(surface =>
surface.SurfaceId == AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId);
Assert.AreEqual("interactive-world", advertisedGameSurface.SurfaceKind);
Assert.AreEqual(AetheriaRuntimeVerseRecordKeys.DaemonGameSurface.ToString(), advertisedGameSurface.RecordRef);
Assert.IsNotNull(advertisedGameSurface.WorldInteraction);
Assert.AreEqual(
"provider-authored-world-surface",
advertisedGameSurface.WorldInteraction.ProjectionKind);
Assert.AreEqual("aetheria.daemon.commands", advertisedGameSurface.WorldInteraction.CommandBoundary);
Assert.AreEqual(
AetheriaRuntimeDaemonSchemas.EveCommandAcceptanceStatus,
advertisedGameSurface.WorldInteraction.ReceiptSchema);
Assert.AreEqual(
"provider-owns-world-state-assets-command-acceptance-and-receipts",
advertisedGameSurface.WorldInteraction.Ownership);
CollectionAssert.Contains(advertisedGameSurface.WorldInteraction.LoweringTargets, "unity-scene");
CollectionAssert.Contains(advertisedGameSurface.WorldInteraction.StateSchemas, AetheriaRuntimeDaemonSchemas.Frame);
var health = result.Health;
Assert.IsNotNull(health);
Assert.AreEqual(AetheriaRuntimeDaemonSchemas.Health, health.Schema);
@ -2899,10 +2916,25 @@ public class DaemonRuntimeDocumentTests
var presentation = runtime.Connect();
Assert.AreEqual("aetheria-cultmesh-cultcache-transport", liveBridge.TransportKind);
Assert.AreEqual(AetheriaRuntimeVerseRecordKeys.DaemonGameSurface.ToString(), liveBridge.SurfacePointer);
Assert.AreEqual(AetheriaRuntimeVerseRecordKeys.DaemonAssetManifest.ToString(), liveBridge.AssetManifestPointer);
Assert.AreEqual("interactive-world", liveBridge.CurrentSurfaceDocument.AdvertisedSurface.SurfaceKind);
Assert.AreEqual(
"provider-authored-world-surface",
liveBridge.CurrentSurfaceDocument.AdvertisedSurface.WorldInteraction.ProjectionKind);
Assert.AreEqual(
"aetheria.daemon.commands",
liveBridge.CurrentSurfaceDocument.AdvertisedSurface.WorldInteraction.CommandBoundary);
Assert.AreEqual(
AetheriaRuntimeDaemonSchemas.EveCommandAcceptanceStatus,
liveBridge.CurrentSurfaceDocument.AdvertisedSurface.WorldInteraction.ReceiptSchema);
Assert.AreEqual(1, presentation.ActiveEntities);
Assert.AreEqual(1, sceneSink.Upserts.Count);
Assert.IsNotNull(runtime.ActiveWorld);
Assert.IsNotNull(runtime.ActiveProjection);
Assert.AreEqual("provider-authored-world-surface", runtime.ActiveProjection.ProjectionKind);
Assert.AreEqual("aetheria.daemon.commands", runtime.ActiveProjection.CommandBoundary);
Assert.AreEqual(AetheriaRuntimeDaemonSchemas.EveCommandAcceptanceStatus, runtime.ActiveProjection.ReceiptSchema);
Assert.AreEqual(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest.ToString(),
runtime.ActiveWorld.StatePointerId);
@ -2938,7 +2970,7 @@ public class DaemonRuntimeDocumentTests
Assert.AreEqual("aetheria.daemon", moveIntent.ProviderId);
Assert.AreEqual(AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId, moveIntent.SurfaceId);
Assert.AreEqual("aetheria.daemon.commands", moveIntent.CommandBoundary);
Assert.AreEqual("aetheria.eve_command_acceptance_status.v1", moveIntent.ReceiptSchema);
Assert.AreEqual(AetheriaRuntimeDaemonSchemas.EveCommandAcceptanceStatus, moveIntent.ReceiptSchema);
Assert.AreEqual(
AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(AetheriaRuntimeDaemonCommandKinds.SetMoveVector),
moveIntent.Payload.GetString("commandId"));

View file

@ -115,7 +115,10 @@ namespace GameCult.Aetheria.EveRuntime
var surface = ReadSurface(runtimeState, stateBoot);
if (surface != null)
{
CurrentDocument = CreateSurfaceDocument(surface, surface.Version);
CurrentDocument = CreateSurfaceDocument(
surface,
surface.Version,
ReadAdvertisedSurface(runtimeState));
DocumentAvailable?.Invoke(CurrentDocument);
}
@ -198,26 +201,40 @@ namespace GameCult.Aetheria.EveRuntime
return ToUnityPlayableWorldAssetManifest(manifest);
}
private AetheriaRuntimeEveSurfaceAdvertisement? ReadAdvertisedSurface(AetheriaClientState runtimeState)
{
var provider = runtimeState.ProviderAdvertisement.Latest();
return (provider.EveSurfaces ?? Array.Empty<AetheriaRuntimeEveSurfaceAdvertisement>())
.FirstOrDefault(surface => surface != null && string.Equals(surface.SurfaceId, _surfaceId, StringComparison.Ordinal))
?? AetheriaRuntimeEveSurfaceCatalog.Find(_surfaceId);
}
private EveUnitySceneProviderSurfaceDocument CreateSurfaceDocument(
EveSurfaceDocument surface,
long version)
long version,
AetheriaRuntimeEveSurfaceAdvertisement? advertisedSurface = null)
{
advertisedSurface ??= AetheriaRuntimeEveSurfaceCatalog.Find(_surfaceId);
var worldInteraction = advertisedSurface?.WorldInteraction;
return new EveUnitySceneProviderSurfaceDocument(
surface,
new EveUnitySceneProviderSurfaceAdvertisement(
_surfaceId,
"interactive-world",
advertisedSurface?.SurfaceKind ?? "",
new EveUnitySceneWorldInteraction(
"provider-authored-world-surface",
"aetheria.daemon.commands",
"aetheria.eve_command_acceptance_status.v1",
"provider-owns-world-state-assets-command-acceptance-and-receipts")),
SourcePointer(),
worldInteraction?.ProjectionKind ?? "",
worldInteraction?.CommandBoundary ?? "",
worldInteraction?.ReceiptSchema ?? "",
worldInteraction?.Ownership ?? "")),
SourcePointer(advertisedSurface),
version);
}
private string SourcePointer()
private string SourcePointer(AetheriaRuntimeEveSurfaceAdvertisement? advertisedSurface)
{
if (!string.IsNullOrWhiteSpace(advertisedSurface?.RecordRef))
return advertisedSurface.RecordRef;
var path = _stateBoot?.StateFilePath ?? _stateFilePathOverride;
return string.IsNullOrWhiteSpace(path)
? AetheriaRuntimeVerseRecordKeys.DaemonGameSurface.ToString()

View file

@ -88,6 +88,7 @@ namespace GameCult.Aetheria.State.Verse
public const string ProviderAdvertisement = "gamecult.aetheria.daemon_provider_advertisement.v1";
public const string Health = "gamecult.aetheria.daemon_health.v1";
public const string CommandBoundary = "gamecult.aetheria.daemon_command_boundary.v1";
public const string EveCommandAcceptanceStatus = "aetheria.eve_command_acceptance_status.v1";
public const string GameSurface = "gamecult.aetheria.daemon_game_surface.v1";
public const string EditorSurface = "gamecult.aetheria.daemon_editor_surface.v1";
}

View file

@ -156,6 +156,9 @@ namespace GameCult.Aetheria.State.Verse
("title", surface.Title),
("surfaceId", surface.SurfaceId),
("record", surface.RecordRef),
("kind", surface.SurfaceKind),
("projection", surface.WorldInteraction?.ProjectionKind ?? ""),
("commandBoundary", surface.WorldInteraction?.CommandBoundary ?? ""),
("status", surface.Status),
("audience", surface.Audience),
("provider", surface.ProviderId)

View file

@ -42,6 +42,34 @@ namespace GameCult.Aetheria.State.Verse
[Key(10)]
public IReadOnlyList<string> Commands { get; set; } = Array.Empty<string>();
[Key(11)]
public string SurfaceKind { get; set; } = "";
[Key(12)]
public AetheriaRuntimeEveWorldInteractionAdvertisement? WorldInteraction { get; set; }
}
[MessagePackObject]
public sealed class AetheriaRuntimeEveWorldInteractionAdvertisement
{
[Key(0)]
public string ProjectionKind { get; set; } = "";
[Key(1)]
public IReadOnlyList<string> StateSchemas { get; set; } = Array.Empty<string>();
[Key(2)]
public IReadOnlyList<string> LoweringTargets { get; set; } = Array.Empty<string>();
[Key(3)]
public string CommandBoundary { get; set; } = "";
[Key(4)]
public string ReceiptSchema { get; set; } = "";
[Key(5)]
public string Ownership { get; set; } = "";
}
public static class AetheriaRuntimeEveSurfaceCatalog
@ -59,10 +87,10 @@ 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"),
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(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")),
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")),
Surface(AetheriaRuntimeMainMenuCommands.RootSurfaceId, "Main Menu", AetheriaRuntimeVerseRecordKeys.MainMenuSurface.ToString(), "player", "Runtime main menu root.", transport: "cultmesh-record"),
Surface(AetheriaRuntimeMainMenuCommands.SettingsSurfaceId, "Main Menu Settings", AetheriaRuntimeVerseRecordKeys.MainMenuSettingsSurface.ToString(), "player", "Runtime main menu settings panel.", transport: "cultmesh-record"),
Surface(AetheriaRuntimeMainMenuCommands.InputSettingsSurfaceId, "Main Menu Input Settings", AetheriaRuntimeVerseRecordKeys.MainMenuInputSettingsSurface.ToString(), "player", "Main menu input settings panel.", transport: "cultmesh-record"),
@ -92,6 +120,16 @@ namespace GameCult.Aetheria.State.Verse
public static IReadOnlyList<string> SurfaceSchemas { get; } = new[] { SurfaceSchema };
public static AetheriaRuntimeEveSurfaceAdvertisement? Find(string surfaceId)
{
if (string.IsNullOrWhiteSpace(surfaceId))
return null;
return Surfaces.FirstOrDefault(surface =>
surface != null &&
string.Equals(surface.SurfaceId, surfaceId, StringComparison.Ordinal));
}
private static AetheriaRuntimeEveSurfaceAdvertisement Surface(
string surfaceId,
string title,
@ -100,7 +138,17 @@ namespace GameCult.Aetheria.State.Verse
string summary,
params string[] commands)
{
return Surface(surfaceId, title, recordRef, audience, summary, "aetheria", "game.runtime", "cultmesh-managed", "available", commands);
return Surface(
surfaceId,
title,
recordRef,
audience,
summary,
"aetheria",
"game.runtime",
"cultmesh-managed",
"available",
commands: commands);
}
private static AetheriaRuntimeEveSurfaceAdvertisement Surface(
@ -113,6 +161,8 @@ namespace GameCult.Aetheria.State.Verse
string providerKind = "game.runtime",
string transport = "cultmesh-managed",
string status = "available",
string surfaceKind = "",
AetheriaRuntimeEveWorldInteractionAdvertisement? worldInteraction = null,
params string[] commands)
{
return new AetheriaRuntimeEveSurfaceAdvertisement
@ -127,7 +177,33 @@ namespace GameCult.Aetheria.State.Verse
Audience = audience,
Mode = "interactive",
Summary = summary,
Commands = commands?.Where(command => !string.IsNullOrWhiteSpace(command)).ToArray() ?? Array.Empty<string>()
Commands = commands?.Where(command => !string.IsNullOrWhiteSpace(command)).ToArray() ?? Array.Empty<string>(),
SurfaceKind = surfaceKind ?? "",
WorldInteraction = worldInteraction
};
}
private static AetheriaRuntimeEveWorldInteractionAdvertisement WorldInteraction(string projectionKind)
{
return new AetheriaRuntimeEveWorldInteractionAdvertisement
{
ProjectionKind = projectionKind ?? "",
StateSchemas = new[]
{
AetheriaRuntimeDaemonSchemas.Frame,
AetheriaRuntimeDaemonSchemas.AssetManifest
},
LoweringTargets = new[]
{
"web-reference",
"unity-uitoolkit",
"unity-scene",
"electron-shell",
"tui"
},
CommandBoundary = "aetheria.daemon.commands",
ReceiptSchema = AetheriaRuntimeDaemonSchemas.EveCommandAcceptanceStatus,
Ownership = "provider-owns-world-state-assets-command-acceptance-and-receipts"
};
}
}