From 5f29e2defc540a4324994c4dfbabf3a97b620679 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 18:08:34 +0100 Subject: [PATCH] Add Unity scene provider source boundary --- docs/eve-migration-roadmap.md | 10 +- docs/parity-testing-harness.md | 9 +- docs/world-state-lowering.md | 12 +- .../Runtime/EveUnitySceneClientSession.cs | 2 + .../EveUnitySceneProviderConnection.cs | 145 ++++++++++++++++++ .../EveUnitySceneSurfaceLowererTests.cs | 115 ++++++++++++++ .../eve-runtime-capability.json | 6 +- .../eveunity-scene-split-handoff.json | 7 +- .../run-eveunity-scene-lifecycle-smoke.ps1 | 2 +- ...un-eveunity-scene-provider-shell-smoke.ps1 | 14 +- ...ntime-owner-conformance-consumer-smoke.ps1 | 1 + tools/parity/parity-manifest.json | 12 +- 12 files changed, 314 insertions(+), 21 deletions(-) create mode 100644 runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 8d808aa..b8e1f2f 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -612,10 +612,12 @@ Recently cut: `playableWorld` shape under `gamecult.eve.unity_scene_projection.v1`. `EveUnitySceneClientSession` now consumes provider surface snapshots and emits movement/focus/target/action intents through the advertised command boundary, - so the Unity client boundary is generic before it learns live transport. The - next blocker is the live generic Unity player loop: feed that session from - CultMesh/CultNet, instantiate the projected world in Unity, and wait for - provider receipts/next frames rather than renderer-local simulation. + while `EveUnitySceneProviderConnection` defines the generic surface source + and command sink ports that a CultMesh/CultNet adapter must implement. The + Unity client boundary is generic before it learns live transport. The next + blocker is the live generic Unity player loop: implement that CultMesh/CultNet + adapter, instantiate the projected world in Unity, and wait for provider + receipts/next frames rather than renderer-local simulation. - EveElectron capture lifecycle now carries the same kind of structured pending capture contract. `captureContract` names the Electron shell runtime, capture kind, PNG artifact pattern, conformance attachment point, required Aetheria diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index f68699c..42463f5 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -481,9 +481,12 @@ entity transforms, asset refs, and daemon command affordances. The Unity scene runtime also declares `providerSurfaceSession` and carries `EveUnitySceneClientSession`, a provider-snapshot boundary that lowers the active surface and emits `gamecult.eve.command.v1` intents without Aetheria -types. The remaining blocker is the live CultMesh/CultNet subscription adapter, -Unity player instantiation, and Unity screenshot or frame-capture PNG -production from EveUnity. +types. It also declares `providerSurfaceSource` through +`EveUnitySceneProviderConnection`, `IEveUnitySceneProviderSurfaceSource`, and +`IEveUnitySceneCommandSink`, so the live CultMesh/CultNet adapter has a narrow +runtime-owned port to implement. The remaining blocker is that live adapter, +Unity player instantiation, and Unity screenshot or frame-capture PNG production +from EveUnity. Runtime capture probe coverage is exported as `runtimeCaptureProbeCoverage[]`. Each record joins runtime status, capture owner, lifecycle capture contract, diff --git a/docs/world-state-lowering.md b/docs/world-state-lowering.md index df416a2..e9fcfa5 100644 --- a/docs/world-state-lowering.md +++ b/docs/world-state-lowering.md @@ -161,13 +161,15 @@ movement/focus/target command names, `world.entity3d` rows, and a radii, asset refs, controllability, and command affordances. The generic `EveUnitySceneClientSession` consumes provider surface snapshots and emits `gamecult.eve.command.v1` intents through the advertised command boundary. -Aetheria-specific names appear only as provider-authored data and command ids; -the Unity client code does not import Aetheria runtime types or apply movement -locally. +`EveUnitySceneProviderConnection` adds the live-source port shape: +`IEveUnitySceneProviderSurfaceSource` supplies current and updated snapshots, +while `IEveUnitySceneCommandSink` receives command envelopes. Aetheria-specific +names appear only as provider-authored data and command ids; the Unity client +code does not import Aetheria runtime types or apply movement locally. This is not yet the final playable client. The remaining cut is a live -CultMesh/CultNet provider subscription adapter feeding `EveUnitySceneClientSession`, -Unity player instantiation of the projected world, and proof that daemon +CultMesh/CultNet provider subscription adapter implementing those source/sink +ports, Unity player instantiation of the projected world, and proof that daemon receipts drive the next frame rather than renderer-local state. ## Non-Goals diff --git a/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs b/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs index 465fe8d..9f0984a 100644 --- a/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs @@ -28,6 +28,8 @@ namespace GameCult.Eve.UnityScene public string ActiveSourcePointer => _activeSnapshot?.SourcePointer ?? ""; + public long ActiveVersion => _activeSnapshot?.Version ?? 0; + public EveUnitySceneProjection Connect(EveUnitySceneProviderSurfaceSnapshot snapshot) { if (snapshot == null) throw new ArgumentNullException(nameof(snapshot)); diff --git a/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs b/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs new file mode 100644 index 0000000..d3c2a28 --- /dev/null +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs @@ -0,0 +1,145 @@ +using System; +using GameCult.Eve.Surface; + +#nullable enable + +namespace GameCult.Eve.UnityScene +{ + public interface IEveUnitySceneProviderSurfaceSource + { + string ProviderId { get; } + + string SurfaceId { get; } + + string SourcePointer { get; } + + EveUnitySceneProviderSurfaceSnapshot CurrentSnapshot { get; } + + event Action SnapshotAvailable; + } + + public interface IEveUnitySceneCommandSink + { + string SinkKind { get; } + + void Submit(EveSurfaceCommandRequest request); + } + + public sealed class EveUnitySceneProviderConnection : IDisposable + { + private readonly IEveUnitySceneProviderSurfaceSource _surfaceSource; + private readonly IEveUnitySceneCommandSink _commandSink; + private readonly EveUnitySceneClientSession _session; + private bool _connected; + + public EveUnitySceneProviderConnection( + IEveUnitySceneProviderSurfaceSource surfaceSource, + IEveUnitySceneCommandSink commandSink) + : this(surfaceSource, commandSink, new EveUnitySceneClientSession()) + { + } + + public EveUnitySceneProviderConnection( + IEveUnitySceneProviderSurfaceSource surfaceSource, + IEveUnitySceneCommandSink commandSink, + EveUnitySceneClientSession session) + { + _surfaceSource = surfaceSource ?? throw new ArgumentNullException(nameof(surfaceSource)); + _commandSink = commandSink ?? throw new ArgumentNullException(nameof(commandSink)); + _session = session ?? throw new ArgumentNullException(nameof(session)); + } + + public string ProviderId => _surfaceSource.ProviderId; + + public string SurfaceId => _surfaceSource.SurfaceId; + + public string SourcePointer => _surfaceSource.SourcePointer; + + public EveUnitySceneProjection? ActiveProjection => _session.ActiveProjection; + + public long ActiveVersion => _session.ActiveVersion; + + public event Action? ProjectionUpdated; + + public EveUnitySceneProjection Connect() + { + if (!_connected) + { + _surfaceSource.SnapshotAvailable += OnSnapshotAvailable; + _connected = true; + } + + return ApplySnapshot(_surfaceSource.CurrentSnapshot); + } + + public EveUnitySceneProjection Refresh() + { + return ApplySnapshot(_surfaceSource.CurrentSnapshot); + } + + public EveSurfaceCommandRequest SubmitMoveIntent( + string entityId, + float targetX, + float targetY, + float targetZ, + DateTimeOffset? issuedAt = null) + { + return Submit(_session.CreateMoveIntent(entityId, targetX, targetY, targetZ, issuedAt)); + } + + public EveSurfaceCommandRequest SubmitFocusIntent( + string entityId, + DateTimeOffset? issuedAt = null) + { + return Submit(_session.CreateFocusIntent(entityId, issuedAt)); + } + + public EveSurfaceCommandRequest SubmitTargetIntent( + string sourceEntityId, + string targetEntityId, + DateTimeOffset? issuedAt = null) + { + return Submit(_session.CreateTargetIntent(sourceEntityId, targetEntityId, issuedAt)); + } + + public EveSurfaceCommandRequest SubmitActionIntent( + string entityId, + string actionId, + DateTimeOffset? issuedAt = null) + { + return Submit(_session.CreateActionIntent(entityId, actionId, issuedAt)); + } + + public void Disconnect() + { + if (!_connected) + return; + + _surfaceSource.SnapshotAvailable -= OnSnapshotAvailable; + _connected = false; + } + + public void Dispose() + { + Disconnect(); + } + + private EveSurfaceCommandRequest Submit(EveSurfaceCommandRequest request) + { + _commandSink.Submit(request); + return request; + } + + private EveUnitySceneProjection ApplySnapshot(EveUnitySceneProviderSurfaceSnapshot snapshot) + { + var projection = _session.ApplySnapshot(snapshot); + ProjectionUpdated?.Invoke(projection); + return projection; + } + + private void OnSnapshotAvailable(EveUnitySceneProviderSurfaceSnapshot snapshot) + { + ApplySnapshot(snapshot); + } + } +} diff --git a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs index d4b382a..d509abd 100644 --- a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs +++ b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs @@ -131,6 +131,73 @@ namespace GameCult.Eve.UnityScene.Tests 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 SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState() { @@ -490,5 +557,53 @@ namespace GameCult.Eve.UnityScene.Tests "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? SnapshotAvailable; + + public void Publish(EveUnitySceneProviderSurfaceSnapshot snapshot) + { + CurrentSnapshot = snapshot; + SnapshotAvailable?.Invoke(snapshot); + } + } + + private sealed class FakeCommandSink : IEveUnitySceneCommandSink + { + public FakeCommandSink(string sinkKind) + { + SinkKind = sinkKind; + } + + public string SinkKind { get; } + + public List Submitted { get; } = new List(); + + public void Submit(EveSurfaceCommandRequest request) + { + Submitted.Add(request); + } + } } } diff --git a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json index cf9b32a..8f5ba2b 100644 --- a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json +++ b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json @@ -17,6 +17,7 @@ "providerAdvertisements", "commandTransport", "providerSurfaceSession", + "providerSurfaceSource", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments" @@ -61,6 +62,7 @@ "evidencePaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -108,6 +110,7 @@ "runtimes/incubating/eve-unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -144,6 +147,7 @@ "runtimes/incubating/eve-unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -155,7 +159,7 @@ ], "pendingProofs": [ "Unity scene provider-advertisement smoke runs from EveUnity with real Unity scene/frame objects and no Aetheria imports.", - "Live CultMesh/CultNet provider subscription adapter feeds EveUnitySceneClientSession snapshots instead of Eve-local fixture documents." + "Live CultMesh/CultNet provider subscription adapter implements IEveUnitySceneProviderSurfaceSource and feeds EveUnitySceneClientSession snapshots instead of Eve-local fixture documents." ] }, "capture": { diff --git a/runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json b/runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json index 63ed1bc..5e5e1e6 100644 --- a/runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json +++ b/runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json @@ -17,6 +17,7 @@ "runtimes/incubating/eve-unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -34,6 +35,7 @@ "runtimes/incubating/eve-unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Tests/Editor/GameCult.Eve.UnityScene.Tests.asmdef", "tools/eveunity/eveunity-release-contract.mjs", "tools/eveunity/eveunity-release-contract.test.mjs", @@ -51,6 +53,7 @@ "currentPaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -102,11 +105,12 @@ "currentPaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs", "runtimes/incubating/eve-unity-scene/eve-runtime-capability.json" ], "destinationOwner": "EveUnity", - "replacementProof": "Unity scene input emits gamecult.eve.command.v1 intents through a provider-snapshot client session and advertised command boundaries, then renders provider receipts instead of mutating local world truth." + "replacementProof": "Unity scene input emits gamecult.eve.command.v1 intents through a provider-snapshot client session, IEveUnitySceneProviderSurfaceSource, IEveUnitySceneCommandSink, and advertised command boundaries, then renders provider receipts instead of mutating local world truth." }, { "id": "unity-scene-capture-lifecycle", @@ -145,6 +149,7 @@ "EveUnity builds the provider-agnostic Unity scene UPM package artifact.", "EveUnity owns a provider-agnostic scene graph lowerer that consumes advertised worldInteraction boundaries without importing Aetheria internals.", "EveUnity owns a provider-snapshot Unity scene client session that lowers Aetheria's surface and emits command intents without importing Aetheria project code.", + "EveUnity owns provider surface source and command sink ports that a live CultMesh/CultNet adapter can implement without changing scene lowering.", "EveUnity owns the provider-agnostic playable-world scene lowerer outside Eve incubation.", "EveUnity owns the Unity scene Sai projection adapter outside Eve incubation without importing Sai story state or VN/Ink internals.", "EveUnity owns the Unity scene Norn projection adapter outside Eve incubation without importing Norn implementation state.", diff --git a/scripts/run-eveunity-scene-lifecycle-smoke.ps1 b/scripts/run-eveunity-scene-lifecycle-smoke.ps1 index a6b7fda..8eeee91 100644 --- a/scripts/run-eveunity-scene-lifecycle-smoke.ps1 +++ b/scripts/run-eveunity-scene-lifecycle-smoke.ps1 @@ -39,7 +39,7 @@ if ($manifest.incubation.splitHandoff.manifestPath -ne "runtimes/incubating/eve- throw "EveUnity scene manifest missing split handoff path" } -foreach ($feature in @("providerAdvertisements", "commandTransport", "providerSurfaceSession", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "providerSurfaceSession", "providerSurfaceSource", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments")) { if (-not (@($manifest.supportedFeatures) -contains $feature)) { throw "EveUnity scene manifest missing provider-shell feature: $feature" } diff --git a/scripts/run-eveunity-scene-provider-shell-smoke.ps1 b/scripts/run-eveunity-scene-provider-shell-smoke.ps1 index 668afa9..e99e10a 100644 --- a/scripts/run-eveunity-scene-provider-shell-smoke.ps1 +++ b/scripts/run-eveunity-scene-provider-shell-smoke.ps1 @@ -54,7 +54,7 @@ foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) { } } -foreach ($feature in @("providerAdvertisements", "commandTransport", "providerSurfaceSession", "sceneGraphProjection", "playableWorldProjection")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "providerSurfaceSession", "providerSurfaceSource", "sceneGraphProjection", "playableWorldProjection")) { if (-not (@($manifest.supportedFeatures) -contains $feature)) { throw "EveUnity scene provider shell missing supported feature: $feature" } @@ -65,6 +65,7 @@ $expectedFiles = @( "runtimes\incubating\eve-unity-scene\Runtime\GameCult.Eve.UnityScene.asmdef", "runtimes\incubating\eve-unity-scene\Runtime\EveUnitySceneSurfaceLowerer.cs", "runtimes\incubating\eve-unity-scene\Runtime\EveUnitySceneClientSession.cs", + "runtimes\incubating\eve-unity-scene\Runtime\EveUnitySceneProviderConnection.cs", "runtimes\incubating\eve-unity-scene\Runtime\SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes\incubating\eve-unity-scene\Runtime\NornGraphUnitySceneProjectionAdapter.cs", "runtimes\incubating\eve-unity-scene\Runtime\TeXMathUnitySceneProjectionAdapter.cs", @@ -87,12 +88,19 @@ foreach ($symbol in @("EveUnitySceneSurfaceLowerer", "EveUnitySceneProviderSurfa } $clientSessionSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-unity-scene\Runtime\EveUnitySceneClientSession.cs") -Raw -foreach ($symbol in @("EveUnitySceneClientSession", "EveUnitySceneProviderSurfaceSnapshot", "Connect", "ApplySnapshot", "CreateMoveIntent", "CreateFocusIntent", "CreateTargetIntent", "CreateActionIntent", "CreatePlayableWorldIntent", "commandId", "targetPosition", "unity-scene")) { +foreach ($symbol in @("EveUnitySceneClientSession", "EveUnitySceneProviderSurfaceSnapshot", "ActiveVersion", "Connect", "ApplySnapshot", "CreateMoveIntent", "CreateFocusIntent", "CreateTargetIntent", "CreateActionIntent", "CreatePlayableWorldIntent", "commandId", "targetPosition", "unity-scene")) { if (-not $clientSessionSource.Contains($symbol)) { throw "EveUnity scene client session missing symbol: $symbol" } } +$providerConnectionSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-unity-scene\Runtime\EveUnitySceneProviderConnection.cs") -Raw +foreach ($symbol in @("IEveUnitySceneProviderSurfaceSource", "IEveUnitySceneCommandSink", "EveUnitySceneProviderConnection", "SnapshotAvailable", "CurrentSnapshot", "Connect", "Refresh", "SubmitMoveIntent", "SubmitFocusIntent", "SubmitTargetIntent", "SubmitActionIntent", "Disconnect")) { + if (-not $providerConnectionSource.Contains($symbol)) { + throw "EveUnity scene provider connection missing symbol: $symbol" + } +} + $saiAdapterSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-unity-scene\Runtime\SaiVisualNovelUnitySceneProjectionAdapter.cs") -Raw foreach ($symbol in @("SaiVisualNovelUnitySceneProjectionAdapter", "sai.vn", "vn.stage", "story.choose", "story.continue", "story.jump", "gamecult.eve.plugin_abi.v1", "sidecar-advertised-plugin-abi", "Sai")) { if (-not $saiAdapterSource.Contains($symbol)) { @@ -115,7 +123,7 @@ foreach ($symbol in @("TeXMathUnitySceneProjectionAdapter", "tex.math", "embed.t } $testSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-unity-scene\Tests\Editor\EveUnitySceneSurfaceLowererTests.cs") -Raw -foreach ($symbol in @("LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "LowerExtractsPlayableArpgWorldFromGenericScene3dSurface", "GenericClientSessionConsumesAetheriaPlayableWorldSnapshotWithoutAetheriaTypes", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "playable-world-root", "playable-world-entity", "world-field-3d", "arpg-third-person", "cultmesh://aetheria/assets/manifest", "cultmesh://aetheria/eve/surfaces/aetheria.daemon.game", "aetheria.daemon.move_intent", "sai-vn-scene-stage", "norn-graph-scene-projection", "tex-math-scene-projection", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1")) { +foreach ($symbol in @("LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "LowerExtractsPlayableArpgWorldFromGenericScene3dSurface", "GenericClientSessionConsumesAetheriaPlayableWorldSnapshotWithoutAetheriaTypes", "GenericProviderConnectionAppliesLiveSnapshotsAndSubmitsCommandsThroughSink", "FakeProviderSurfaceSource", "FakeCommandSink", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "playable-world-root", "playable-world-entity", "world-field-3d", "arpg-third-person", "cultmesh://aetheria/assets/manifest", "cultmesh://aetheria/eve/surfaces/aetheria.daemon.game", "aetheria.daemon.move_intent", "sai-vn-scene-stage", "norn-graph-scene-projection", "tex-math-scene-projection", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1")) { if (-not $testSource.Contains($symbol)) { throw "EveUnity scene provider shell tests missing symbol: $symbol" } diff --git a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 index cd3f997..1e704f2 100644 --- a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 +++ b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 @@ -65,6 +65,7 @@ node $consumerScript $consumerExport ` --expect-runtime-feature unity-scene:providerAdvertisements ` --expect-runtime-feature unity-scene:commandTransport ` --expect-runtime-feature unity-scene:providerSurfaceSession ` + --expect-runtime-feature unity-scene:providerSurfaceSource ` --expect-runtime-feature unity-scene:sceneGraphProjection ` --expect-runtime-feature unity-scene:playableWorldProjection ` --expect-runtime-feature electron-shell:providerAdvertisements ` diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index 784426f..7892f01 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -1040,7 +1040,7 @@ "splitHandoffPath": "runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json", "requiredFixtures": ["aetheria-world", "eve-world-smoke"], "pluginFixtures": ["sai-vn"], - "supportedFeatures": ["providerAdvertisements", "commandTransport", "providerSurfaceSession", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments"], + "supportedFeatures": ["providerAdvertisements", "commandTransport", "providerSurfaceSession", "providerSurfaceSource", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments"], "supportedPlugins": [ { "pluginId": "sai.vn", @@ -1069,6 +1069,7 @@ "evidencePaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -1083,6 +1084,7 @@ "runtimes/incubating/eve-unity-scene/Runtime/GameCult.Eve.UnityScene.asmdef", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -1112,7 +1114,11 @@ }, { "path": "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", - "contains": ["EveUnitySceneClientSession", "EveUnitySceneProviderSurfaceSnapshot", "Connect", "ApplySnapshot", "CreateMoveIntent", "CreateFocusIntent", "CreateTargetIntent", "CreateActionIntent", "CreatePlayableWorldIntent", "commandId", "targetPosition", "unity-scene"] + "contains": ["EveUnitySceneClientSession", "EveUnitySceneProviderSurfaceSnapshot", "ActiveVersion", "Connect", "ApplySnapshot", "CreateMoveIntent", "CreateFocusIntent", "CreateTargetIntent", "CreateActionIntent", "CreatePlayableWorldIntent", "commandId", "targetPosition", "unity-scene"] + }, + { + "path": "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", + "contains": ["IEveUnitySceneProviderSurfaceSource", "IEveUnitySceneCommandSink", "EveUnitySceneProviderConnection", "SnapshotAvailable", "CurrentSnapshot", "Connect", "Refresh", "SubmitMoveIntent", "SubmitFocusIntent", "SubmitTargetIntent", "SubmitActionIntent", "Disconnect"] }, { "path": "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", @@ -1128,7 +1134,7 @@ }, { "path": "runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs", - "contains": ["LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "LowerExtractsPlayableArpgWorldFromGenericScene3dSurface", "GenericClientSessionConsumesAetheriaPlayableWorldSnapshotWithoutAetheriaTypes", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "playable-world-root", "playable-world-entity", "world-field-3d", "arpg-third-person", "cultmesh://aetheria/assets/manifest", "cultmesh://aetheria/eve/surfaces/aetheria.daemon.game", "aetheria.daemon.move_intent", "sai-vn-scene-stage", "norn-graph-scene-projection", "tex-math-scene-projection", "norn.map", "cultmesh://aetheria/norn/map", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"] + "contains": ["LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "LowerExtractsPlayableArpgWorldFromGenericScene3dSurface", "GenericClientSessionConsumesAetheriaPlayableWorldSnapshotWithoutAetheriaTypes", "GenericProviderConnectionAppliesLiveSnapshotsAndSubmitsCommandsThroughSink", "FakeProviderSurfaceSource", "FakeCommandSink", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "playable-world-root", "playable-world-entity", "world-field-3d", "arpg-third-person", "cultmesh://aetheria/assets/manifest", "cultmesh://aetheria/eve/surfaces/aetheria.daemon.game", "aetheria.daemon.move_intent", "sai-vn-scene-stage", "norn-graph-scene-projection", "tex-math-scene-projection", "norn.map", "cultmesh://aetheria/norn/map", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"] }, { "path": "tools/eveunity/eveunity-capture-contract.test.mjs",