Thing/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs
2026-07-09 18:55:17 +01:00

1041 lines
51 KiB
C#

using System;
using System.Collections.Generic;
using GameCult.Eve.Surface;
using GameCult.Mesh;
using NUnit.Framework;
#nullable enable
namespace GameCult.Eve.UnityScene.Tests
{
public sealed class EveUnitySceneSurfaceLowererTests
{
[Test]
public void LowerCarriesProviderAdvertisedWorldBoundary()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(Document("aetheria.daemon.game"), Advertisement("aetheria.daemon.game"));
Assert.That(projection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(projection.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(projection.ProjectionKind, Is.EqualTo("provider-authored-world-surface"));
Assert.That(projection.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(projection.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
Assert.That(projection.Ownership, Is.EqualTo("provider-owns-world-state-assets-command-acceptance-and-receipts"));
Assert.That(projection.Root.Id, Is.EqualTo("aetheria.daemon.game.root"));
Assert.That(projection.Root.SceneObjectKind, Is.EqualTo("scene-node"));
}
[Test]
public void LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(Document("aetheria.daemon.game"), Advertisement("aetheria.daemon.game"));
Assert.That(projection.Root.Children.Count, Is.EqualTo(3));
Assert.That(projection.Root.Children[0].SceneObjectKind, Is.EqualTo("world-projection-node"));
Assert.That(projection.Root.Children[0].Props["binding"], Is.EqualTo("cultmesh://aetheria/world/entities"));
Assert.That(projection.Root.Children[0].StateBindingCount, Is.EqualTo(1));
Assert.That(projection.Root.Children[1].SceneObjectKind, Is.EqualTo("command-control"));
Assert.That(projection.Root.Children[1].Props["command"], Is.EqualTo("aetheria.daemon.focus"));
var nornNode = projection.Root.Children[2];
Assert.That(nornNode.SceneObjectKind, Is.EqualTo("norn-graph-scene-projection"));
Assert.That(nornNode.EmbeddedDocumentCount, Is.EqualTo(1));
Assert.That(nornNode.EmbeddedDocuments.Count, Is.EqualTo(1));
Assert.That(nornNode.EmbeddedDocuments[0].SlotId, Is.EqualTo("norn.map"));
Assert.That(nornNode.EmbeddedDocuments[0].DocumentId, Is.EqualTo("cultmesh://aetheria/norn/map"));
Assert.That(nornNode.EmbeddedDocuments[0].SchemaId, Is.EqualTo("gamecult.eve.surface.v1"));
Assert.That(nornNode.EmbeddedDocuments[0].PresentationKind, Is.EqualTo("scene-overlay"));
Assert.That(nornNode.PluginProjection, Is.Not.Null);
Assert.That(nornNode.PluginProjection!.PluginId, Is.EqualTo("norn.graph"));
Assert.That(nornNode.PluginProjection.ProjectionKind, Is.EqualTo("norn-scene-embedded-graph-shell"));
Assert.That(nornNode.PluginProjection.AbiSchema, Is.EqualTo("gamecult.eve.plugin_abi.v1"));
Assert.That(nornNode.PluginProjection.CommandBoundary, Is.EqualTo("sidecar-advertised-plugin-abi"));
Assert.That(nornNode.PluginProjection.Capabilities, Does.Contain("embed.norn"));
Assert.That(nornNode.PluginProjection.Command, Is.EqualTo("graph.focus"));
Assert.That(nornNode.PluginProjection.DocumentId, Is.EqualTo("cultmesh://aetheria/norn/map"));
Assert.That(nornNode.PluginProjection.SemanticOwner, Is.EqualTo("Norn"));
}
[Test]
public void LowerExtractsPlayableArpgWorldFromGenericScene3dSurface()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
Assert.That(projection.Root.Children[0].SceneObjectKind, Is.EqualTo("playable-world-root"));
Assert.That(projection.Root.Children[0].Children[0].SceneObjectKind, Is.EqualTo("playable-world-entity"));
Assert.That(projection.Root.Children[0].Children[3].SceneObjectKind, Is.EqualTo("world-field-3d"));
Assert.That(projection.PlayableWorld, Is.Not.Null);
Assert.That(projection.PlayableWorld!.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
Assert.That(projection.PlayableWorld.StatePointerId, Is.EqualTo("cultmesh://aetheria/run/current"));
Assert.That(projection.PlayableWorld.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(projection.PlayableWorld.InputProfile, Is.EqualTo("arpg-third-person"));
Assert.That(projection.PlayableWorld.CameraRig, Is.EqualTo("third-person-orbit"));
Assert.That(projection.PlayableWorld.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(projection.PlayableWorld.MovementCommand, Is.EqualTo("aetheria.daemon.move_intent"));
Assert.That(projection.PlayableWorld.FocusCommand, Is.EqualTo("aetheria.daemon.focus"));
Assert.That(projection.PlayableWorld.TargetCommand, Is.EqualTo("aetheria.daemon.target"));
Assert.That(projection.PlayableWorld.EntityCount, Is.EqualTo(3));
var player = FindEntity(projection.PlayableWorld, "player-vanguard");
Assert.That(player.EntityKind, Is.EqualTo("player"));
Assert.That(player.AssetRef, Is.EqualTo("cultmesh://aetheria/assets/map/entity/player"));
Assert.That(player.PositionX, Is.EqualTo(0f));
Assert.That(player.PositionY, Is.EqualTo(0f));
Assert.That(player.PositionZ, Is.EqualTo(0f));
Assert.That(player.Controllable, Is.True);
Assert.That(player.MoveCommand, Is.EqualTo("aetheria.daemon.move_intent"));
var raider = FindEntity(projection.PlayableWorld, "raider-scout");
Assert.That(raider.EntityKind, Is.EqualTo("enemy"));
Assert.That(raider.PositionX, Is.EqualTo(9f));
Assert.That(raider.PositionZ, Is.EqualTo(14f));
Assert.That(raider.TargetCommand, Is.EqualTo("aetheria.daemon.target"));
}
[Test]
public void GenericClientSessionConsumesAetheriaPlayableWorldSnapshotWithoutAetheriaTypes()
{
var session = new EveUnitySceneClientSession();
var projection = session.Connect(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
42));
Assert.That(session.ActiveSourcePointer, Is.EqualTo("cultmesh://aetheria/eve/surfaces/aetheria.daemon.game"));
Assert.That(session.ActiveProjection, Is.SameAs(projection));
Assert.That(projection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(projection.PlayableWorld, Is.Not.Null);
Assert.That(projection.PlayableWorld!.InputProfile, Is.EqualTo("arpg-third-person"));
Assert.That(projection.PlayableWorld.EntityCount, Is.EqualTo(3));
var moveIntent = session.CreateMoveIntent(
"player-vanguard",
12f,
0f,
8f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(moveIntent.Schema, Is.EqualTo(EveSurfaceCommandRequest.SchemaId));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveIntent.ClientId, Is.EqualTo("unity-scene"));
Assert.That(moveIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
var focusIntent = session.CreateFocusIntent("anchor-station");
Assert.That(focusIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(focusIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
}
[Test]
public void GenericProviderConnectionAppliesLiveSnapshotsAndSubmitsCommandsThroughSink()
{
var source = new FakeProviderSurfaceSource(
"aetheria",
"aetheria.daemon.game",
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
var sink = new FakeCommandSink("cultmesh-command-sink");
using var connection = new EveUnitySceneProviderConnection(source, sink);
var projectionUpdates = 0;
connection.ProjectionUpdated += _ => projectionUpdates++;
var initialProjection = connection.Connect();
Assert.That(initialProjection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(connection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(connection.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(connection.SourcePointer, Is.EqualTo("cultmesh://aetheria/eve/surfaces/aetheria.daemon.game"));
Assert.That(connection.ActiveVersion, Is.EqualTo(1));
Assert.That(projectionUpdates, Is.EqualTo(1));
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(connection.ActiveVersion, Is.EqualTo(2));
Assert.That(connection.ActiveProjection, Is.Not.Null);
Assert.That(connection.ActiveProjection!.PlayableWorld, Is.Not.Null);
Assert.That(connection.ActiveProjection.PlayableWorld!.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(projectionUpdates, Is.EqualTo(2));
var moveIntent = connection.SubmitMoveIntent(
"player-vanguard",
20f,
0f,
15f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(sink.SinkKind, Is.EqualTo("cultmesh-command-sink"));
Assert.That(sink.Submitted.Count, Is.EqualTo(1));
Assert.That(sink.Submitted[0], Is.SameAs(moveIntent));
Assert.That(moveIntent.Schema, Is.EqualTo(EveSurfaceCommandRequest.SchemaId));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
connection.Disconnect();
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
3));
Assert.That(connection.ActiveVersion, Is.EqualTo(2));
Assert.That(projectionUpdates, Is.EqualTo(2));
}
[Test]
public void PlayableWorldPresenterInstantiatesUpdatesAndDespawnsProviderEntities()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var sink = new FakePlayableWorldSceneSink();
var presenter = new EveUnityPlayableWorldPresenter(sink, new EveUnityAssetRefResolver());
var firstProjection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
var firstPresentation = presenter.Apply(firstProjection);
Assert.That(firstPresentation.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
Assert.That(firstPresentation.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(firstPresentation.InputProfile, Is.EqualTo("arpg-third-person"));
Assert.That(firstPresentation.CameraRig, Is.EqualTo("third-person-orbit"));
Assert.That(firstPresentation.UpsertedEntities, Is.EqualTo(3));
Assert.That(firstPresentation.RemovedEntities, Is.EqualTo(0));
Assert.That(firstPresentation.ActiveEntities, Is.EqualTo(3));
Assert.That(sink.ConfiguredWorlds.Count, Is.EqualTo(1));
Assert.That(sink.Upserts.Count, Is.EqualTo(3));
Assert.That(sink.Upserts[0].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sink.Upserts[0].entity.PositionX, Is.EqualTo(0f));
Assert.That(sink.Upserts[0].asset.AssetRef, Is.EqualTo("cultmesh://aetheria/assets/map/entity/player"));
Assert.That(sink.Upserts[0].asset.PresentationKind, Is.EqualTo("provider-asset-ref"));
var secondProjection = lowerer.Lower(
PlayableArpgDocument(includeRaider: false, playerPosition: "5,0,2"),
Advertisement("aetheria.daemon.game"));
var secondPresentation = presenter.Apply(secondProjection);
Assert.That(secondPresentation.UpsertedEntities, Is.EqualTo(2));
Assert.That(secondPresentation.RemovedEntities, Is.EqualTo(1));
Assert.That(secondPresentation.ActiveEntities, Is.EqualTo(2));
Assert.That(sink.ConfiguredWorlds.Count, Is.EqualTo(2));
Assert.That(sink.RemovedEntityIds.Count, Is.EqualTo(1));
Assert.That(sink.RemovedEntityIds[0], Is.EqualTo("raider-scout"));
Assert.That(sink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sink.Upserts[3].entity.PositionX, Is.EqualTo(5f));
Assert.That(sink.Upserts[3].entity.PositionZ, Is.EqualTo(2f));
}
[Test]
public void LivePlayableWorldClientPresentsProviderSnapshotsAndKeepsCommandsProviderOwned()
{
var source = new FakeProviderSurfaceSource(
"aetheria",
"aetheria.daemon.game",
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
var commandSink = new FakeCommandSink("cultmesh-command-sink");
var sceneSink = new FakePlayableWorldSceneSink();
using var connection = new EveUnitySceneProviderConnection(source, commandSink);
using var client = new EveUnityPlayableWorldLiveClient(
connection,
new EveUnityPlayableWorldPresenter(sceneSink, new EveUnityAssetRefResolver()));
var initialPresentation = client.Connect();
Assert.That(initialPresentation.ActiveEntities, Is.EqualTo(3));
Assert.That(initialPresentation.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(client.ActiveWorld, Is.Not.Null);
Assert.That(client.ActiveWorld!.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(1));
Assert.That(sceneSink.Upserts.Count, Is.EqualTo(3));
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(includeRaider: false, playerPosition: "7,0,4"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(client.LastPresentation, Is.Not.Null);
Assert.That(client.LastPresentation!.ActiveEntities, Is.EqualTo(2));
Assert.That(client.LastPresentation.RemovedEntities, Is.EqualTo(1));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(2));
Assert.That(sceneSink.RemovedEntityIds[0], Is.EqualTo("raider-scout"));
Assert.That(sceneSink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sceneSink.Upserts[3].entity.PositionX, Is.EqualTo(7f));
Assert.That(sceneSink.Upserts[3].entity.PositionZ, Is.EqualTo(4f));
var moveIntent = client.SubmitMoveIntent(
"player-vanguard",
12f,
0f,
8f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(commandSink.Submitted.Count, Is.EqualTo(1));
Assert.That(commandSink.Submitted[0], Is.SameAs(moveIntent));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
client.Disconnect();
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(playerPosition: "99,0,99"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
3));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(2));
}
[Test]
public void LivePlayableWorldClientRefreshesFromProviderSnapshotAfterReceiptWithoutOwningMovement()
{
var source = new FakeProviderSurfaceSource(
"aetheria",
"aetheria.daemon.game",
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
var commandSink = new FakeCommandSink("cultmesh-command-sink");
var receiptSource = new FakeCommandReceiptSource();
var sceneSink = new FakePlayableWorldSceneSink();
using var connection = new EveUnitySceneProviderConnection(source, commandSink);
using var client = new EveUnityPlayableWorldLiveClient(
connection,
new EveUnityPlayableWorldPresenter(sceneSink, new EveUnityAssetRefResolver()),
receiptSource);
client.Connect();
client.SubmitMoveIntent("player-vanguard", 30f, 0f, 30f);
Assert.That(sceneSink.Upserts[0].entity.PositionX, Is.EqualTo(0f));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(1));
receiptSource.Publish(new EveUnitySceneCommandReceipt(
"aetheria.daemon.move_intent.pending",
"aetheria.daemon.commands",
"aetheria.daemon.move_intent",
"pending",
"Aetheria",
"AetheriaRuntimeDaemonCommandBoundaryDocument",
"aetheria.eve_command_acceptance_status.v1",
"aetheria",
"aetheria.daemon.game"));
Assert.That(client.LastReceipt, Is.Not.Null);
Assert.That(client.LastReceipt!.State, Is.EqualTo("pending"));
Assert.That(client.LastReceipt.IsProviderOwned, Is.True);
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(1));
source.Stage(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(includeRaider: false, playerPosition: "30,0,30"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
receiptSource.Publish(new EveUnitySceneCommandReceipt(
"aetheria.daemon.move_intent.reconciled",
"aetheria.daemon.commands",
"aetheria.daemon.move_intent",
"reconciled",
"Aetheria",
"AetheriaRuntimeDaemonCommandBoundaryDocument",
"aetheria.eve_command_acceptance_status.v1",
"aetheria",
"aetheria.daemon.game"));
Assert.That(client.LastReceipt.State, Is.EqualTo("reconciled"));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(client.LastPresentation, Is.Not.Null);
Assert.That(client.LastPresentation!.ActiveEntities, Is.EqualTo(2));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(2));
Assert.That(sceneSink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sceneSink.Upserts[3].entity.PositionX, Is.EqualTo(30f));
Assert.That(sceneSink.Upserts[3].entity.PositionZ, Is.EqualTo(30f));
}
[Test]
public void AssetManifestMapsProviderAssetRefsToUnityLoadKeysWithoutAetheriaTypes()
{
var manifest = new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard"),
new EveUnityPlayableWorldAssetManifestEntry(
"",
"enemy",
"Resources/Aetheria/Entities/Raider.prefab",
"aetheria.raider")
});
var player = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(player, Is.Not.Null);
Assert.That(player!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Vanguard"));
Assert.That(player.PrefabKey, Is.EqualTo("aetheria.vanguard"));
var enemy = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/missing-raider",
"enemy",
"provider-asset-ref"));
Assert.That(enemy, Is.Not.Null);
Assert.That(enemy!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Raider"));
Assert.That(enemy.PrefabKey, Is.EqualTo("aetheria.raider"));
}
[Test]
public void AssetManifestCacheTracksPlayableWorldManifestPointerAndLiveUpdates()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
Assert.That(projection.PlayableWorld, Is.Not.Null);
var source = new FakeAssetManifestSource(new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard")
}));
var cache = new EveUnityPlayableWorldAssetManifestCache();
cache.Connect(source);
var manifest = cache.GetForWorld(projection.PlayableWorld!);
Assert.That(manifest, Is.Not.Null);
Assert.That(manifest!.ManifestRef, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(cache.Count, Is.EqualTo(1));
var player = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(player, Is.Not.Null);
Assert.That(player!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Vanguard"));
source.Publish(new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/VanguardMk2.prefab",
"aetheria.vanguard.mk2")
}));
var updated = cache.GetForWorld(projection.PlayableWorld);
Assert.That(updated, Is.Not.Null);
var updatedPlayer = updated!.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(updatedPlayer, Is.Not.Null);
Assert.That(updatedPlayer!.ResourcesPath, Is.EqualTo("Aetheria/Entities/VanguardMk2"));
cache.Disconnect(source);
source.Publish(new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/VanguardIgnored.prefab",
"aetheria.vanguard.ignored")
}));
var afterDisconnect = cache.GetForWorld(projection.PlayableWorld);
var afterDisconnectPlayer = afterDisconnect!.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(afterDisconnectPlayer!.ResourcesPath, Is.EqualTo("Aetheria/Entities/VanguardMk2"));
}
[Test]
public void AssetManifestDocumentSourceFeedsCacheWithoutChangingSceneLowering()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
Assert.That(projection.PlayableWorld, Is.Not.Null);
var documentSource = new FakeAssetManifestDocumentSource(new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestDocumentEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard")
},
"aetheria"));
using var manifestSource = new EveUnityPlayableWorldAssetManifestDocumentSource(documentSource);
var cache = new EveUnityPlayableWorldAssetManifestCache();
manifestSource.Connect();
cache.Connect(manifestSource);
var manifest = cache.GetForWorld(projection.PlayableWorld!);
Assert.That(manifest, Is.Not.Null);
Assert.That(manifest!.ManifestRef, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
var player = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(player, Is.Not.Null);
Assert.That(player!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Vanguard"));
Assert.That(player.PrefabKey, Is.EqualTo("aetheria.vanguard"));
documentSource.Publish(new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestDocumentEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"Resources/Aetheria/Entities/VanguardAuthority.prefab",
"aetheria.vanguard.authority")
},
"aetheria"));
var updated = cache.GetForWorld(projection.PlayableWorld);
var updatedPlayer = updated!.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(updatedPlayer, Is.Not.Null);
Assert.That(updatedPlayer!.ResourcesPath, Is.EqualTo("Aetheria/Entities/VanguardAuthority"));
Assert.That(updatedPlayer.PrefabKey, Is.EqualTo("aetheria.vanguard.authority"));
}
[Test]
public void SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(SaiDocument(), Advertisement("sai.visual_novel.surface"));
Assert.That(projection.ProviderId, Is.EqualTo("gamecult.home.vn"));
Assert.That(projection.SurfaceId, Is.EqualTo("sai.visual_novel.surface"));
Assert.That(projection.Root.SceneObjectKind, Is.EqualTo("sai-vn-scene-stage"));
Assert.That(projection.Root.PluginProjection, Is.Not.Null);
Assert.That(projection.Root.PluginProjection!.PluginId, Is.EqualTo("sai.vn"));
Assert.That(projection.Root.PluginProjection.ProjectionKind, Is.EqualTo("sai-vn-scene-stage-shell"));
Assert.That(projection.Root.PluginProjection.AbiSchema, Is.EqualTo("gamecult.eve.plugin_abi.v1"));
Assert.That(projection.Root.PluginProjection.CommandBoundary, Is.EqualTo("sidecar-advertised-plugin-abi"));
Assert.That(projection.Root.PluginProjection.Capabilities, Does.Contain("vn.stage"));
Assert.That(projection.Root.PluginProjection.Capabilities, Does.Contain("story.choose"));
Assert.That(projection.Root.PluginProjection.DocumentId, Is.EqualTo("gamecult-compound"));
Assert.That(projection.Root.PluginProjection.SemanticOwner, Is.EqualTo("Sai"));
var dialogue = projection.Root.Children[0];
Assert.That(dialogue.SceneObjectKind, Is.EqualTo("sai-vn-scene-dialogue"));
Assert.That(dialogue.PluginProjection!.ProjectionKind, Is.EqualTo("sai-vn-scene-dialogue-shell"));
Assert.That(dialogue.PluginProjection.SemanticOwner, Is.EqualTo("Sai"));
var choice = projection.Root.Children[1].Children[0];
Assert.That(choice.SceneObjectKind, Is.EqualTo("command-control"));
Assert.That(choice.PluginProjection, Is.Not.Null);
Assert.That(choice.PluginProjection!.PluginId, Is.EqualTo("sai.vn"));
Assert.That(choice.PluginProjection.ProjectionKind, Is.EqualTo("sai-vn-scene-story-command-shell"));
Assert.That(choice.PluginProjection.Command, Is.EqualTo("story.choose"));
Assert.That(choice.PluginProjection.SemanticOwner, Is.EqualTo("Sai"));
var tex = projection.Root.Children[2];
Assert.That(tex.SceneObjectKind, Is.EqualTo("tex-math-scene-projection"));
Assert.That(tex.PluginProjection, Is.Not.Null);
Assert.That(tex.PluginProjection!.PluginId, Is.EqualTo("tex.math"));
Assert.That(tex.PluginProjection.ProjectionKind, Is.EqualTo("tex-math-scene-block-fallback-shell"));
Assert.That(tex.PluginProjection.Capabilities, Does.Contain("embed.tex"));
Assert.That(tex.PluginProjection.Capabilities, Does.Contain("tex.scene-placement"));
Assert.That(tex.PluginProjection.DocumentId, Is.EqualTo("\\\\mathrm{votes}(p)=1+\\\\lfloor\\\\log_b(1+p)\\\\rfloor"));
Assert.That(tex.PluginProjection.SemanticOwner, Is.EqualTo("EvePlugins"));
}
[Test]
public void CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var intent = lowerer.CreateCommandIntent(
Document("aetheria.daemon.game"),
Advertisement("aetheria.daemon.game"),
"aetheria.daemon.commands",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["commandId"] = "aetheria.daemon.focus"
},
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(intent.Schema, Is.EqualTo(EveSurfaceCommandRequest.SchemaId));
Assert.That(intent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(intent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(intent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(intent.ClientId, Is.EqualTo("unity-scene"));
Assert.That(intent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(intent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
}
private static EveSurfaceDocument Document(string surfaceId)
{
return new EveSurfaceDocument(
"aetheria",
"game.provider",
"Aetheria world",
1,
"2026-07-09T00:00:00Z",
new EveSurfaceTree(
surfaceId,
new EveSurfaceComponent(
$"{surfaceId}.root",
"surface",
new Dictionary<string, string>(StringComparer.Ordinal),
new[]
{
new EveSurfaceComponent(
$"{surfaceId}.entities",
"world.entities",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["binding"] = "cultmesh://aetheria/world/entities"
},
Array.Empty<EveSurfaceComponent>(),
new[]
{
new CultMeshStateBindingDescriptor("entities", "cultmesh://aetheria/world/entities")
}),
new EveSurfaceComponent(
$"{surfaceId}.focus",
"control.button",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["command"] = "aetheria.daemon.focus"
},
Array.Empty<EveSurfaceComponent>()),
new EveSurfaceComponent(
$"{surfaceId}.norn",
"embed.norn",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["graph.document"] = "cultmesh://aetheria/norn/map",
["interaction.nodeAction"] = "graph.focus"
},
Array.Empty<EveSurfaceComponent>(),
Array.Empty<CultMeshStateBindingDescriptor>(),
new[]
{
new EveEmbeddedDocumentSlot(
"norn.map",
"cultmesh://aetheria/norn/map",
"gamecult.eve.surface.v1",
"scene-overlay")
})
}),
Array.Empty<EveStyleToken>()),
Array.Empty<EveCommandTemplate>());
}
private static EveSurfaceDocument PlayableArpgDocument(
bool includeRaider = true,
string playerPosition = "0,0,0")
{
var playableChildren = new List<EveSurfaceComponent>
{
PlayableEntity(
"aetheria.daemon.game.entity.player",
"player-vanguard",
"player",
"Vanguard",
"player",
"cultmesh://aetheria/assets/map/entity/player",
playerPosition,
"35",
"1.15",
true,
true,
"aetheria.daemon.focus",
"aetheria.daemon.move_intent",
"",
"")
};
if (includeRaider)
{
playableChildren.Add(PlayableEntity(
"aetheria.daemon.game.entity.raider",
"raider-scout",
"enemy",
"Raider Scout",
"raider",
"cultmesh://aetheria/assets/map/entity/ship",
"9,0,14",
"220",
"0.9",
true,
false,
"",
"",
"aetheria.daemon.target",
""));
}
playableChildren.Add(PlayableEntity(
"aetheria.daemon.game.entity.station",
"anchor-station",
"station",
"Anchor Station",
"neutral",
"cultmesh://aetheria/assets/map/entity/station",
"-18,0,6",
"0",
"3.4",
true,
false,
"aetheria.daemon.focus",
"",
"",
""));
playableChildren.Add(new EveSurfaceComponent(
"aetheria.daemon.game.flow",
"field.vector3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["fieldId"] = "aetheria.zone.flow",
["bind"] = "aetheria.daemon.soaView.flowField3d",
["visualizer"] = "particles",
["bounds"] = "-32,0,-32,32,24,32"
},
Array.Empty<EveSurfaceComponent>()));
return new EveSurfaceDocument(
"aetheria",
"game.runtime",
"Aetheria daemon game",
1,
"2026-07-09T00:00:00Z",
new EveSurfaceTree(
"aetheria.daemon.game",
new EveSurfaceComponent(
"aetheria.daemon.game.root",
"surface",
new Dictionary<string, string>(StringComparer.Ordinal),
new[]
{
new EveSurfaceComponent(
"aetheria.daemon.game.playable",
"world.scene3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["statePointerId"] = "cultmesh://aetheria/run/current",
["assetManifest"] = "cultmesh://aetheria/assets/manifest",
["inputProfile"] = "arpg-third-person",
["cameraRig"] = "third-person-orbit",
["playerEntityId"] = "player-vanguard",
["movementCommand"] = "aetheria.daemon.move_intent",
["focusCommand"] = "aetheria.daemon.focus",
["targetCommand"] = "aetheria.daemon.target",
["actionCommand"] = "aetheria.daemon.use_equipment"
},
playableChildren,
new[]
{
new CultMeshStateBindingDescriptor("run", "cultmesh://aetheria/run/current"),
new CultMeshStateBindingDescriptor("assets", "cultmesh://aetheria/assets/manifest")
})
}),
Array.Empty<EveStyleToken>()),
new[]
{
new EveCommandTemplate(CultMesh.OperationBinding("aetheria.daemon.commands", "Aetheria daemon commands"))
});
}
private static EveSurfaceComponent PlayableEntity(
string nodeId,
string entityId,
string entityKind,
string label,
string faction,
string assetRef,
string position,
string rotationY,
string radius,
bool selectable,
bool controllable,
string focusCommand,
string moveCommand,
string targetCommand,
string actionCommand)
{
return new EveSurfaceComponent(
nodeId,
"world.entity3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["entityId"] = entityId,
["entityKind"] = entityKind,
["label"] = label,
["faction"] = faction,
["assetRef"] = assetRef,
["position"] = position,
["rotationY"] = rotationY,
["radius"] = radius,
["selectable"] = selectable ? "true" : "false",
["controllable"] = controllable ? "true" : "false",
["focusCommand"] = focusCommand,
["moveCommand"] = moveCommand,
["targetCommand"] = targetCommand,
["actionCommand"] = actionCommand
},
Array.Empty<EveSurfaceComponent>());
}
private static EveUnityPlayableWorldEntity FindEntity(
EveUnityPlayableWorldProjection playableWorld,
string entityId)
{
foreach (var entity in playableWorld.Entities)
{
if (string.Equals(entity.EntityId, entityId, StringComparison.Ordinal))
return entity;
}
throw new AssertionException($"Playable entity not found: {entityId}");
}
private static EveSurfaceDocument SaiDocument()
{
return new EveSurfaceDocument(
"gamecult.home.vn",
"sai.visual_novel",
"GameCult Compound VN",
1,
"2026-07-09T00:00:00Z",
new EveSurfaceTree(
"sai.visual_novel.surface",
new EveSurfaceComponent(
"sai.root",
"vn.stage",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["storyId"] = "gamecult-compound",
["currentPath"] = "hub"
},
new[]
{
new EveSurfaceComponent(
"sai.dialogue",
"panel.dialogue",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["speaker"] = "Void",
["text"] = "Pick a door."
},
Array.Empty<EveSurfaceComponent>()),
new EveSurfaceComponent(
"sai.choices",
"rail.actions",
new Dictionary<string, string>(StringComparer.Ordinal),
new[]
{
new EveSurfaceComponent(
"sai.choice.eve",
"control.button",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["label"] = "What is Eve?",
["action.command"] = "story.choose",
["targetPath"] = "eve"
},
Array.Empty<EveSurfaceComponent>())
}),
new EveSurfaceComponent(
"sai.tex.log-power",
"embed.tex",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["label"] = "Bifrost voting weight",
["source"] = "\\\\mathrm{votes}(p)=1+\\\\lfloor\\\\log_b(1+p)\\\\rfloor",
["format"] = "latex",
["display"] = "block"
},
Array.Empty<EveSurfaceComponent>())
}),
Array.Empty<EveStyleToken>()),
new[]
{
new EveCommandTemplate(CultMesh.OperationBinding("story.choose", "Choose"))
});
}
private static EveUnitySceneProviderSurfaceAdvertisement Advertisement(string surfaceId)
{
return new EveUnitySceneProviderSurfaceAdvertisement(
surfaceId,
"interactive-world",
new EveUnitySceneWorldInteraction(
"provider-authored-world-surface",
"aetheria.daemon.commands",
"aetheria.eve_command_acceptance_status.v1",
"provider-owns-world-state-assets-command-acceptance-and-receipts"));
}
private sealed class FakeProviderSurfaceSource : IEveUnitySceneProviderSurfaceSource
{
public FakeProviderSurfaceSource(
string providerId,
string surfaceId,
string sourcePointer,
EveUnitySceneProviderSurfaceSnapshot currentSnapshot)
{
ProviderId = providerId;
SurfaceId = surfaceId;
SourcePointer = sourcePointer;
CurrentSnapshot = currentSnapshot;
}
public string ProviderId { get; }
public string SurfaceId { get; }
public string SourcePointer { get; }
public EveUnitySceneProviderSurfaceSnapshot CurrentSnapshot { get; private set; }
public event Action<EveUnitySceneProviderSurfaceSnapshot>? SnapshotAvailable;
public void Stage(EveUnitySceneProviderSurfaceSnapshot snapshot)
{
CurrentSnapshot = snapshot;
}
public void Publish(EveUnitySceneProviderSurfaceSnapshot snapshot)
{
CurrentSnapshot = snapshot;
SnapshotAvailable?.Invoke(snapshot);
}
}
private sealed class FakeCommandReceiptSource : IEveUnitySceneCommandReceiptSource
{
public event Action<EveUnitySceneCommandReceipt>? ReceiptAvailable;
public void Publish(EveUnitySceneCommandReceipt receipt)
{
ReceiptAvailable?.Invoke(receipt);
}
}
private sealed class FakeCommandSink : IEveUnitySceneCommandSink
{
public FakeCommandSink(string sinkKind)
{
SinkKind = sinkKind;
}
public string SinkKind { get; }
public List<EveSurfaceCommandRequest> Submitted { get; } = new List<EveSurfaceCommandRequest>();
public void Submit(EveSurfaceCommandRequest request)
{
Submitted.Add(request);
}
}
private sealed class FakePlayableWorldSceneSink : IEveUnityPlayableWorldSceneSink
{
public List<EveUnityPlayableWorldProjection> ConfiguredWorlds { get; } = new List<EveUnityPlayableWorldProjection>();
public List<(EveUnityPlayableWorldEntity entity, EveUnityPlayableWorldAssetBinding asset)> Upserts { get; } =
new List<(EveUnityPlayableWorldEntity entity, EveUnityPlayableWorldAssetBinding asset)>();
public List<string> RemovedEntityIds { get; } = new List<string>();
public void ConfigureWorld(EveUnityPlayableWorldProjection world)
{
ConfiguredWorlds.Add(world);
}
public void UpsertEntity(EveUnityPlayableWorldEntity entity, EveUnityPlayableWorldAssetBinding asset)
{
Upserts.Add((entity, asset));
}
public void RemoveEntity(string entityId)
{
RemovedEntityIds.Add(entityId);
}
}
private sealed class FakeAssetManifestSource : IEveUnityPlayableWorldAssetManifestSource
{
public FakeAssetManifestSource(EveUnityPlayableWorldAssetManifest currentManifest)
{
CurrentManifest = currentManifest;
}
public string ManifestRef => CurrentManifest.ManifestRef;
public EveUnityPlayableWorldAssetManifest CurrentManifest { get; private set; }
public event Action<EveUnityPlayableWorldAssetManifest>? ManifestAvailable;
public void Publish(EveUnityPlayableWorldAssetManifest manifest)
{
CurrentManifest = manifest;
ManifestAvailable?.Invoke(manifest);
}
}
private sealed class FakeAssetManifestDocumentSource : IEveUnityPlayableWorldAssetManifestDocumentSource
{
public FakeAssetManifestDocumentSource(EveUnityPlayableWorldAssetManifestDocument currentDocument)
{
CurrentDocument = currentDocument;
}
public string ManifestRef => CurrentDocument.ManifestRef;
public EveUnityPlayableWorldAssetManifestDocument CurrentDocument { get; private set; }
public event Action<EveUnityPlayableWorldAssetManifestDocument>? DocumentAvailable;
public void Publish(EveUnityPlayableWorldAssetManifestDocument document)
{
CurrentDocument = document;
DocumentAvailable?.Invoke(document);
}
}
}
}