From 84e71955882d7d3e5524f0bcf9cc0b42a98a6db0 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 18:03:11 +0100 Subject: [PATCH] Add Unity scene provider session boundary --- docs/eve-migration-roadmap.md | 11 +- docs/parity-testing-harness.md | 10 +- docs/world-state-lowering.md | 30 +-- .../Runtime/EveUnitySceneClientSession.cs | 193 ++++++++++++++++++ .../EveUnitySceneSurfaceLowererTests.cs | 37 ++++ .../eve-runtime-capability.json | 7 +- .../eveunity-scene-split-handoff.json | 9 +- .../run-eveunity-scene-lifecycle-smoke.ps1 | 2 +- ...un-eveunity-scene-provider-shell-smoke.ps1 | 12 +- ...ntime-owner-conformance-consumer-smoke.ps1 | 1 + tools/parity/parity-manifest.json | 10 +- 11 files changed, 293 insertions(+), 29 deletions(-) create mode 100644 runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 867981d..8d808aa 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -609,10 +609,13 @@ Recently cut: `world.entity3d`, asset-manifest, input-profile, camera, movement, focus, and target command affordances; `EveUnitySceneSurfaceLowerer` exposes them as a `PlayableWorld` projection, and the JSON capture artifact exports the same - `playableWorld` shape under `gamecult.eve.unity_scene_projection.v1`. The - next blocker is the live generic Unity client loop: subscribe to the Aetheria - daemon surface over CultMesh/CultNet, instantiate the projected world, emit - `gamecult.eve.command.v1` input, and wait for provider receipts/next frames. + `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. - 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 cab7fe6..f68699c 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -477,9 +477,13 @@ and writes `artifacts/eveunity-scene-capture/latest/unity-scene-projection.json` It records scene graph projection, embedded slots, sidecar plugin projection metadata, and a `playableWorld` extraction for generic `world.scene3d` / `world.entity3d` ARPG data: state pointer, asset manifest, camera/input profile, -entity transforms, asset refs, and daemon command affordances. The remaining -blocker is a live generic Unity client loop and Unity screenshot or -frame-capture PNG production from EveUnity. +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. 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 47d76cb..df416a2 100644 --- a/docs/world-state-lowering.md +++ b/docs/world-state-lowering.md @@ -152,21 +152,23 @@ Toolkit, Unity scene, Electron shell, and TUI lowerers are lowering targets; Aetheria remains the owner of world state, assets, command acceptance, and receipts. -The Unity scene lowerer now has a first playable-world extraction proof. The -fixture carries a generic `world.scene3d` node with a daemon state pointer, -asset manifest ref, third-person input/camera profile, movement/focus/target -command names, `world.entity3d` rows, and a `field.vector3d` volume field. The -Unity scene runtime lowers that into `playableWorld` projection evidence: -entity ids, kinds, labels, positions, radii, asset refs, controllability, and -command affordances. 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. +The Unity scene lowerer now has a playable-world extraction and client-session +proof. The fixture carries a generic `world.scene3d` node with a daemon state +pointer, asset manifest ref, third-person input/camera profile, +movement/focus/target command names, `world.entity3d` rows, and a +`field.vector3d` volume field. The Unity scene runtime lowers that into +`playableWorld` projection evidence: entity ids, kinds, labels, positions, +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. -This is not yet the final playable client. The remaining cut is a generic -EveUnity scene client that subscribes to the live Aetheria daemon surface over -CultMesh/CultNet, instantiates the projected world in a Unity player, emits -input as `gamecult.eve.command.v1`, and proves daemon receipts drive the next -frame rather than renderer-local state. +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 +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 new file mode 100644 index 0000000..465fe8d --- /dev/null +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using GameCult.Eve.Surface; + +#nullable enable + +namespace GameCult.Eve.UnityScene +{ + public sealed class EveUnitySceneClientSession + { + public const string RuntimeClientId = "unity-scene"; + + private readonly EveUnitySceneSurfaceLowerer _lowerer; + private EveUnitySceneProviderSurfaceSnapshot? _activeSnapshot; + + public EveUnitySceneClientSession() + : this(new EveUnitySceneSurfaceLowerer()) + { + } + + public EveUnitySceneClientSession(EveUnitySceneSurfaceLowerer lowerer) + { + _lowerer = lowerer ?? throw new ArgumentNullException(nameof(lowerer)); + } + + public EveUnitySceneProjection? ActiveProjection { get; private set; } + + public string ActiveSourcePointer => _activeSnapshot?.SourcePointer ?? ""; + + public EveUnitySceneProjection Connect(EveUnitySceneProviderSurfaceSnapshot snapshot) + { + if (snapshot == null) throw new ArgumentNullException(nameof(snapshot)); + + var projection = _lowerer.Lower(snapshot.Document, snapshot.AdvertisedSurface); + _activeSnapshot = snapshot; + ActiveProjection = projection; + return projection; + } + + public EveUnitySceneProjection ApplySnapshot(EveUnitySceneProviderSurfaceSnapshot snapshot) + { + return Connect(snapshot); + } + + public EveSurfaceCommandRequest CreateMoveIntent( + string entityId, + float targetX, + float targetY, + float targetZ, + DateTimeOffset? issuedAt = null) + { + var playableWorld = RequirePlayableWorld(); + return CreatePlayableWorldIntent( + playableWorld.MovementCommand, + new Dictionary(StringComparer.Ordinal) + { + ["entityId"] = entityId ?? "", + ["targetPosition"] = FormatVector3(targetX, targetY, targetZ) + }, + issuedAt); + } + + public EveSurfaceCommandRequest CreateFocusIntent( + string entityId, + DateTimeOffset? issuedAt = null) + { + var playableWorld = RequirePlayableWorld(); + return CreatePlayableWorldIntent( + playableWorld.FocusCommand, + new Dictionary(StringComparer.Ordinal) + { + ["entityId"] = entityId ?? "" + }, + issuedAt); + } + + public EveSurfaceCommandRequest CreateTargetIntent( + string sourceEntityId, + string targetEntityId, + DateTimeOffset? issuedAt = null) + { + var playableWorld = RequirePlayableWorld(); + return CreatePlayableWorldIntent( + playableWorld.TargetCommand, + new Dictionary(StringComparer.Ordinal) + { + ["sourceEntityId"] = sourceEntityId ?? "", + ["targetEntityId"] = targetEntityId ?? "" + }, + issuedAt); + } + + public EveSurfaceCommandRequest CreateActionIntent( + string entityId, + string actionId, + DateTimeOffset? issuedAt = null) + { + var playableWorld = RequirePlayableWorld(); + return CreatePlayableWorldIntent( + playableWorld.ActionCommand, + new Dictionary(StringComparer.Ordinal) + { + ["entityId"] = entityId ?? "", + ["actionId"] = actionId ?? "" + }, + issuedAt); + } + + public EveSurfaceCommandRequest CreatePlayableWorldIntent( + string commandId, + IReadOnlyDictionary? payload = null, + DateTimeOffset? issuedAt = null) + { + if (string.IsNullOrWhiteSpace(commandId)) + throw new InvalidOperationException("Playable world command id is missing from the active surface."); + + var snapshot = RequireSnapshot(); + var projection = RequireProjection(); + var envelopePayload = new Dictionary(StringComparer.Ordinal) + { + ["commandId"] = commandId + }; + + if (payload != null) + { + foreach (var entry in payload) + envelopePayload[entry.Key] = entry.Value; + } + + return _lowerer.CreateCommandIntent( + snapshot.Document, + snapshot.AdvertisedSurface, + projection.CommandBoundary, + envelopePayload, + issuedAt); + } + + private EveUnityPlayableWorldProjection RequirePlayableWorld() + { + var projection = RequireProjection(); + if (projection.PlayableWorld == null) + throw new InvalidOperationException("The active Unity scene surface does not expose a playable world projection."); + return projection.PlayableWorld; + } + + private EveUnitySceneProjection RequireProjection() + { + if (ActiveProjection == null) + throw new InvalidOperationException("No provider surface snapshot has been connected to the Unity scene client session."); + return ActiveProjection; + } + + private EveUnitySceneProviderSurfaceSnapshot RequireSnapshot() + { + if (_activeSnapshot == null) + throw new InvalidOperationException("No provider surface snapshot has been connected to the Unity scene client session."); + return _activeSnapshot; + } + + private static string FormatVector3(float x, float y, float z) + { + return string.Join( + ",", + x.ToString("R", CultureInfo.InvariantCulture), + y.ToString("R", CultureInfo.InvariantCulture), + z.ToString("R", CultureInfo.InvariantCulture)); + } + } + + public sealed class EveUnitySceneProviderSurfaceSnapshot + { + public EveUnitySceneProviderSurfaceSnapshot( + EveSurfaceDocument document, + EveUnitySceneProviderSurfaceAdvertisement advertisedSurface, + string sourcePointer, + long version = 0) + { + Document = document ?? throw new ArgumentNullException(nameof(document)); + AdvertisedSurface = advertisedSurface ?? throw new ArgumentNullException(nameof(advertisedSurface)); + SourcePointer = sourcePointer ?? ""; + Version = version; + } + + public EveSurfaceDocument Document { get; } + + public EveUnitySceneProviderSurfaceAdvertisement AdvertisedSurface { get; } + + public string SourcePointer { get; } + + public long Version { get; } + } +} diff --git a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs index 37a144a..d4b382a 100644 --- a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs +++ b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs @@ -94,6 +94,43 @@ namespace GameCult.Eve.UnityScene.Tests 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 SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState() { diff --git a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json index d22e45e..cf9b32a 100644 --- a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json +++ b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json @@ -16,6 +16,7 @@ "supportedFeatures": [ "providerAdvertisements", "commandTransport", + "providerSurfaceSession", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments" @@ -59,6 +60,7 @@ "ownership": "runtime-lowers-provider-world-surface-without-owning-world-state", "evidencePaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.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", @@ -105,6 +107,7 @@ "runtimes/incubating/eve-unity-scene/package.json", "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/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -140,6 +143,7 @@ "runtimes/incubating/eve-unity-scene/package.json", "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/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -150,7 +154,8 @@ "scripts/run-eveunity-scene-provider-shell-smoke.ps1" ], "pendingProofs": [ - "Unity scene provider-advertisement smoke runs from EveUnity with real Unity scene/frame objects and no Aetheria imports." + "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." ] }, "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 2d03310..63ed1bc 100644 --- a/runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json +++ b/runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json @@ -16,6 +16,7 @@ "runtimes/incubating/eve-unity-scene/package.json", "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/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -32,6 +33,7 @@ "runtimes/incubating/eve-unity-scene/package.json", "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/Tests/Editor/GameCult.Eve.UnityScene.Tests.asmdef", "tools/eveunity/eveunity-release-contract.mjs", "tools/eveunity/eveunity-release-contract.test.mjs", @@ -48,6 +50,7 @@ "stage": "world-surface-lowering", "currentPaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.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", @@ -55,7 +58,7 @@ "runtimes/incubating/eve-unity-scene/eve-runtime-capability.json" ], "destinationOwner": "EveUnity", - "replacementProof": "EveUnity claims the provider-advertised unity-scene lowering target with provider-agnostic playable-world projection, scene graph projection, and command boundary evidence; live transport and capture remain separate lifecycle proofs." + "replacementProof": "EveUnity claims the provider-advertised unity-scene lowering target with provider-agnostic playable-world projection, scene graph projection, provider snapshot session, and command boundary evidence; live transport and capture remain separate lifecycle proofs." }, { "id": "unity-scene-sai-plugin-projection", @@ -98,11 +101,12 @@ "stage": "command", "currentPaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.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 provider-advertised command boundaries and 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 and advertised command boundaries, then renders provider receipts instead of mutating local world truth." }, { "id": "unity-scene-capture-lifecycle", @@ -140,6 +144,7 @@ "EveUnity publishes a Unity scene runtime capability manifest.", "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 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 c38e96f..a6b7fda 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", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "providerSurfaceSession", "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 d684b5e..668afa9 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", "sceneGraphProjection", "playableWorldProjection")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "providerSurfaceSession", "sceneGraphProjection", "playableWorldProjection")) { if (-not (@($manifest.supportedFeatures) -contains $feature)) { throw "EveUnity scene provider shell missing supported feature: $feature" } @@ -64,6 +64,7 @@ $expectedFiles = @( "runtimes\incubating\eve-unity-scene\package.json", "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\SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes\incubating\eve-unity-scene\Runtime\NornGraphUnitySceneProjectionAdapter.cs", "runtimes\incubating\eve-unity-scene\Runtime\TeXMathUnitySceneProjectionAdapter.cs", @@ -85,6 +86,13 @@ 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")) { + if (-not $clientSessionSource.Contains($symbol)) { + throw "EveUnity scene client session 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)) { @@ -107,7 +115,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", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "playable-world-root", "playable-world-entity", "world-field-3d", "arpg-third-person", "cultmesh://aetheria/assets/manifest", "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", "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 c6f2512..cd3f997 100644 --- a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 +++ b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 @@ -64,6 +64,7 @@ node $consumerScript $consumerExport ` --expect-runtime-feature unity-uitoolkit:embeddedDocuments ` --expect-runtime-feature unity-scene:providerAdvertisements ` --expect-runtime-feature unity-scene:commandTransport ` + --expect-runtime-feature unity-scene:providerSurfaceSession ` --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 a922b4d..784426f 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", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments"], + "supportedFeatures": ["providerAdvertisements", "commandTransport", "providerSurfaceSession", "sceneGraphProjection", "playableWorldProjection", "embeddedDocuments"], "supportedPlugins": [ { "pluginId": "sai.vn", @@ -1068,6 +1068,7 @@ "ownership": "runtime-lowers-provider-world-surface-without-owning-world-state", "evidencePaths": [ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.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", @@ -1081,6 +1082,7 @@ "runtimes/incubating/eve-unity-scene/package.json", "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/SaiVisualNovelUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", @@ -1108,6 +1110,10 @@ "path": "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", "contains": ["EveUnitySceneSurfaceLowerer", "EveUnitySceneProviderSurfaceAdvertisement", "WorldInteraction", "CommandBoundary", "ReceiptSchema", "EveSurfaceCommandRequest", "BuildSceneGraph", "BuildPlayableWorld", "EveUnityPlayableWorldProjection", "EveUnityPlayableWorldEntity", "BuildEmbeddedDocumentSlots", "BuildPluginProjection", "SaiVisualNovelUnitySceneProjectionAdapter", "TeXMathUnitySceneProjectionAdapter", "EveUnitySceneEmbeddedDocumentSlot", "EveUnityScenePluginProjection", "EveUnitySceneNode", "SceneObjectKind", "playable-world-root", "unity-scene"] }, + { + "path": "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneClientSession.cs", + "contains": ["EveUnitySceneClientSession", "EveUnitySceneProviderSurfaceSnapshot", "Connect", "ApplySnapshot", "CreateMoveIntent", "CreateFocusIntent", "CreateTargetIntent", "CreateActionIntent", "CreatePlayableWorldIntent", "commandId", "targetPosition", "unity-scene"] + }, { "path": "runtimes/incubating/eve-unity-scene/Runtime/SaiVisualNovelUnitySceneProjectionAdapter.cs", "contains": ["SaiVisualNovelUnitySceneProjectionAdapter", "sai.vn", "vn.stage", "story.choose", "story.continue", "story.jump", "gamecult.eve.plugin_abi.v1", "sidecar-advertised-plugin-abi", "Sai"] @@ -1122,7 +1128,7 @@ }, { "path": "runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs", - "contains": ["LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "LowerExtractsPlayableArpgWorldFromGenericScene3dSurface", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "playable-world-root", "playable-world-entity", "world-field-3d", "arpg-third-person", "cultmesh://aetheria/assets/manifest", "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", "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",