From d40be46a48164fcfb625da7194222c5fe3adf89a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 19:34:18 +0100 Subject: [PATCH] Add generic EveUnity playable world host --- .../EveUnityPlayableWorldClientHost.cs | 184 ++++++++++++++++++ .../EveUnityPlayableWorldClientHost.cs.meta | 2 + .../Runtime/EveUnityProviderRefreshSource.cs | 7 + .../EveUnityProviderRefreshSource.cs.meta | 2 + .../EveUnitySceneSurfaceLowererTests.cs | 122 ++++++++++++ .../eve-runtime-capability.json | 7 + scripts/run-aetheria-unity-package-smoke.ps1 | 26 +++ ...un-eveunity-scene-provider-shell-smoke.ps1 | 23 ++- 8 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs create mode 100644 runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs.meta create mode 100644 runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs create mode 100644 runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs.meta diff --git a/runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs new file mode 100644 index 0000000..e5ef5cc --- /dev/null +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs @@ -0,0 +1,184 @@ +using System; +using GameCult.Eve.Surface; +using UnityEngine; + +#nullable enable + +namespace GameCult.Eve.UnityScene +{ + public sealed class EveUnityPlayableWorldClientHost : MonoBehaviour + { + [SerializeField] private Transform? sceneRoot; + [SerializeField] private MonoBehaviour? providerSurfaceDocuments; + [SerializeField] private MonoBehaviour? commandSink; + [SerializeField] private MonoBehaviour? assetManifestDocuments; + [SerializeField] private MonoBehaviour? receiptSource; + [SerializeField] private MonoBehaviour? fallbackAssetProvider; + [SerializeField] private bool connectOnEnable = true; + [SerializeField] private bool refreshInUpdate = true; + [SerializeField] private float refreshIntervalSeconds = 0.1f; + + private float _nextRefreshAt; + + public EveUnityPlayableWorldRuntime? Runtime { get; private set; } + + public EveUnityPlayableWorldProjection? ActiveWorld => Runtime?.ActiveWorld; + + public EveUnitySceneProjection? ActiveProjection => Runtime?.ActiveProjection; + + public EveUnityPlayableWorldPresentation? LastPresentation => Runtime?.LastPresentation; + + public EveUnitySceneCommandReceipt? LastReceipt => Runtime?.LastReceipt; + + public long ActiveVersion => Runtime?.ActiveVersion ?? 0; + + public void Configure( + Transform? sceneRoot, + MonoBehaviour providerSurfaceDocuments, + MonoBehaviour commandSink, + MonoBehaviour? assetManifestDocuments = null, + MonoBehaviour? receiptSource = null, + MonoBehaviour? fallbackAssetProvider = null) + { + this.sceneRoot = sceneRoot; + this.providerSurfaceDocuments = providerSurfaceDocuments; + this.commandSink = commandSink; + this.assetManifestDocuments = assetManifestDocuments; + this.receiptSource = receiptSource; + this.fallbackAssetProvider = fallbackAssetProvider; + } + + public EveUnityPlayableWorldPresentation Connect() + { + Disconnect(); + RefreshProviderSources(); + + Runtime = EveUnityPlayableWorldRuntime.CreateForGameObjectScene( + sceneRoot == null ? transform : sceneRoot, + Required( + providerSurfaceDocuments, + nameof(providerSurfaceDocuments)), + Required( + commandSink, + nameof(commandSink)), + Optional(assetManifestDocuments), + Optional(receiptSource), + Optional(fallbackAssetProvider)); + + return Runtime.Connect(); + } + + public EveUnityPlayableWorldPresentation Refresh() + { + RefreshProviderSources(); + if (Runtime == null) + return Connect(); + + return Runtime.Refresh(); + } + + public EveSurfaceCommandRequest SubmitMoveIntent( + string entityId, + float targetX, + float targetY, + float targetZ, + DateTimeOffset? issuedAt = null) + { + return RequireRuntime().SubmitMoveIntent(entityId, targetX, targetY, targetZ, issuedAt); + } + + public EveSurfaceCommandRequest SubmitFocusIntent( + string entityId, + DateTimeOffset? issuedAt = null) + { + return RequireRuntime().SubmitFocusIntent(entityId, issuedAt); + } + + public EveSurfaceCommandRequest SubmitTargetIntent( + string sourceEntityId, + string targetEntityId, + DateTimeOffset? issuedAt = null) + { + return RequireRuntime().SubmitTargetIntent(sourceEntityId, targetEntityId, issuedAt); + } + + public EveSurfaceCommandRequest SubmitActionIntent( + string entityId, + string actionId, + DateTimeOffset? issuedAt = null) + { + return RequireRuntime().SubmitActionIntent(entityId, actionId, issuedAt); + } + + public void Disconnect() + { + Runtime?.Dispose(); + Runtime = null; + } + + private void OnEnable() + { + if (connectOnEnable) + Connect(); + } + + private void Update() + { + if (!refreshInUpdate || Runtime == null) + return; + + if (Time.unscaledTime < _nextRefreshAt) + return; + + _nextRefreshAt = Time.unscaledTime + Math.Max(0.01f, refreshIntervalSeconds); + Refresh(); + } + + private void OnDisable() + { + Disconnect(); + } + + private void OnDestroy() + { + Disconnect(); + } + + private EveUnityPlayableWorldRuntime RequireRuntime() + { + if (Runtime == null) + throw new InvalidOperationException("Eve Unity playable world client host is not connected."); + + return Runtime; + } + + private void RefreshProviderSources() + { + RefreshProvider(providerSurfaceDocuments); + RefreshProvider(assetManifestDocuments); + RefreshProvider(receiptSource); + RefreshProvider(commandSink); + } + + private static void RefreshProvider(MonoBehaviour? behaviour) + { + if (behaviour is IEveUnityProviderRefreshSource refreshSource) + refreshSource.Refresh(); + } + + private static T Required(MonoBehaviour? behaviour, string fieldName) where T : class + { + var resolved = behaviour as T; + if (resolved == null) + throw new InvalidOperationException( + $"Eve Unity playable world client host requires '{fieldName}' to implement {typeof(T).Name}."); + + return resolved; + } + + private static T? Optional(MonoBehaviour? behaviour) where T : class + { + return behaviour as T; + } + } +} diff --git a/runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs.meta b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs.meta new file mode 100644 index 0000000..83701ad --- /dev/null +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: df3f3a1abd3f14541bf6d7ec59343661 \ No newline at end of file diff --git a/runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs new file mode 100644 index 0000000..06f8937 --- /dev/null +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs @@ -0,0 +1,7 @@ +namespace GameCult.Eve.UnityScene +{ + public interface IEveUnityProviderRefreshSource + { + void Refresh(); + } +} diff --git a/runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs.meta b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs.meta new file mode 100644 index 0000000..29cdeb8 --- /dev/null +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: babddb726f9747e43a7789eb6b584fad \ No newline at end of file diff --git a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs index 967e555..f2d0ded 100644 --- a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs +++ b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using GameCult.Eve.Surface; using GameCult.Mesh; using NUnit.Framework; +using UnityEngine; #nullable enable @@ -332,6 +333,57 @@ namespace GameCult.Eve.UnityScene.Tests Assert.That(runtime.LastReceipt.ShouldRefreshProviderSurface, Is.True); } + [Test] + public void PlayableWorldClientHostMountsInterfaceProviderWithoutProviderTypes() + { + var rootObject = new GameObject("generic-eve-world-root"); + var hostObject = new GameObject("generic-eve-client"); + hostObject.SetActive(false); + + try + { + var provider = hostObject.AddComponent(); + provider.Set( + new EveUnitySceneProviderSurfaceDocument( + PlayableArpgDocument(), + Advertisement("aetheria.daemon.game"), + "cultmesh://aetheria/eve/surfaces/aetheria.daemon.game", + 1), + new EveUnityPlayableWorldAssetManifestDocument( + "cultmesh://aetheria/assets/manifest", + new[] + { + new EveUnityPlayableWorldAssetManifestDocumentEntry( + "cultmesh://aetheria/assets/map/entity/player", + "player", + "", + "aetheria.vanguard") + }, + "aetheria")); + + var host = hostObject.AddComponent(); + host.Configure(rootObject.transform, provider, provider, provider, provider); + + var presentation = host.Connect(); + var moveIntent = host.SubmitMoveIntent("player-vanguard", 3f, 0f, 4f); + + Assert.That(presentation.ActiveEntities, Is.EqualTo(3)); + Assert.That(host.ActiveWorld, Is.Not.Null); + Assert.That(host.ActiveWorld!.PlayerEntityId, Is.EqualTo("player-vanguard")); + Assert.That(host.ActiveWorld.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest")); + Assert.That(rootObject.transform.childCount, Is.EqualTo(3)); + Assert.That(provider.RefreshCount, Is.EqualTo(1)); + Assert.That(provider.Submitted.Count, Is.EqualTo(1)); + Assert.That(provider.Submitted[0], Is.SameAs(moveIntent)); + Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands")); + } + finally + { + UnityEngine.Object.DestroyImmediate(hostObject); + UnityEngine.Object.DestroyImmediate(rootObject); + } + } + [Test] public void PlayableWorldPresenterInstantiatesUpdatesAndDespawnsProviderEntities() { @@ -1197,5 +1249,75 @@ namespace GameCult.Eve.UnityScene.Tests DocumentAvailable?.Invoke(document); } } + + private sealed class FakePlayableWorldProviderComponent : + MonoBehaviour, + IEveUnitySceneProviderSurfaceDocumentSource, + IEveUnityPlayableWorldAssetManifestDocumentSource, + IEveUnitySceneCommandSink, + IEveUnitySceneCommandReceiptSource, + IEveUnityProviderRefreshSource + { + public List Submitted { get; } = new List(); + + public int RefreshCount { get; private set; } + + public string SinkKind => "fake-provider-command-sink"; + + public string ManifestRef => CurrentDocument.ManifestRef; + + public EveUnitySceneProviderSurfaceDocument CurrentSurfaceDocument { get; private set; } = + new EveUnitySceneProviderSurfaceDocument( + PlayableArpgDocument(), + Advertisement("aetheria.daemon.game"), + "cultmesh://aetheria/eve/surfaces/aetheria.daemon.game", + 1); + + public EveUnityPlayableWorldAssetManifestDocument CurrentDocument { get; private set; } = + new EveUnityPlayableWorldAssetManifestDocument( + "cultmesh://aetheria/assets/manifest", + Array.Empty(), + "aetheria"); + + EveUnitySceneProviderSurfaceDocument IEveUnitySceneProviderSurfaceDocumentSource.CurrentDocument => + CurrentSurfaceDocument; + + public event Action? DocumentAvailable; + + public event Action? AssetManifestDocumentAvailable; + + event Action IEveUnityPlayableWorldAssetManifestDocumentSource.DocumentAvailable + { + add => AssetManifestDocumentAvailable += value; + remove => AssetManifestDocumentAvailable -= value; + } + + public event Action? ReceiptAvailable; + + public void Set( + EveUnitySceneProviderSurfaceDocument surfaceDocument, + EveUnityPlayableWorldAssetManifestDocument assetManifest) + { + CurrentSurfaceDocument = surfaceDocument; + CurrentDocument = assetManifest; + } + + public void Refresh() + { + RefreshCount++; + DocumentAvailable?.Invoke(CurrentSurfaceDocument); + AssetManifestDocumentAvailable?.Invoke(CurrentDocument); + } + + public void Submit(EveSurfaceCommandRequest request) + { + Submitted.Add(request); + } + + public void PublishReceipt(EveUnitySceneCommandReceipt receipt) + { + ReceiptAvailable?.Invoke(receipt); + } + } } } diff --git a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json index 5102539..46318ce 100644 --- a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json +++ b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json @@ -22,6 +22,7 @@ "livePlayableWorldClient", "providerCommandReceipts", "providerAssetManifestDocumentSource", + "playableWorldClientHost", "sceneGraphProjection", "playableWorldProjection", "playableWorldRuntimeHost", @@ -74,6 +75,8 @@ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldLiveClient.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldRuntime.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldPresenter.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityGameObjectPlayableWorldSceneSink.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldAssetManifest.cs", @@ -127,6 +130,8 @@ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldLiveClient.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldRuntime.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldPresenter.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityGameObjectPlayableWorldSceneSink.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldAssetManifest.cs", @@ -169,6 +174,8 @@ "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneProviderConnection.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldLiveClient.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldRuntime.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs", + "runtimes/incubating/eve-unity-scene/Runtime/EveUnityProviderRefreshSource.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldPresenter.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityGameObjectPlayableWorldSceneSink.cs", "runtimes/incubating/eve-unity-scene/Runtime/EveUnityPlayableWorldAssetManifest.cs", diff --git a/scripts/run-aetheria-unity-package-smoke.ps1 b/scripts/run-aetheria-unity-package-smoke.ps1 index 4af9e42..98db945 100644 --- a/scripts/run-aetheria-unity-package-smoke.ps1 +++ b/scripts/run-aetheria-unity-package-smoke.ps1 @@ -15,6 +15,7 @@ $surfaceProject = Join-Path $AetheriaRoot "GameCult.Eve.Surface.csproj" $aetheriaEveRuntimePackage = Join-Path $AetheriaRoot "Packages\org.gamecult.aetheria.eve-runtime\package.json" $aetheriaEveRuntimeAsmdef = Join-Path $AetheriaRoot "Packages\org.gamecult.aetheria.eve-runtime\Runtime\GameCult.Aetheria.EveRuntime.asmdef" $aetheriaSceneBridge = Join-Path $AetheriaRoot "Packages\org.gamecult.aetheria.eve-runtime\Runtime\AetheriaEveUnitySceneProviderBridge.cs" +$aetheriaSceneProviderComponent = Join-Path $AetheriaRoot "Packages\org.gamecult.aetheria.eve-runtime\Runtime\AetheriaEveUnitySceneProviderComponent.cs" $aetheriaGameSurfaceBuilder = Join-Path $AetheriaRoot "Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonGameSurfaceBuilder.cs" if (-not (Test-Path $manifestPath)) { throw "Aetheria Unity package manifest not found: $manifestPath" @@ -34,6 +35,9 @@ if (-not (Test-Path $aetheriaEveRuntimeAsmdef)) { if (-not (Test-Path $aetheriaSceneBridge)) { throw "Aetheria Eve Unity scene provider bridge not found: $aetheriaSceneBridge" } +if (-not (Test-Path $aetheriaSceneProviderComponent)) { + throw "Aetheria Eve Unity scene provider component not found: $aetheriaSceneProviderComponent" +} if (-not (Test-Path $aetheriaGameSurfaceBuilder)) { throw "Aetheria daemon game surface builder not found: $aetheriaGameSurfaceBuilder" } @@ -85,6 +89,8 @@ foreach ($symbol in @( "IEveUnitySceneProviderSurfaceDocumentSource", "IEveUnityPlayableWorldAssetManifestDocumentSource", "IEveUnitySceneCommandSink", + "IEveUnityProviderRefreshSource", + "ManifestRef", "AetheriaEveRuntimeUnityHooks.RequireControl" )) { if (-not $sceneBridge.Contains($symbol)) { @@ -92,6 +98,26 @@ foreach ($symbol in @( } } +$sceneProviderComponent = Get-Content -Raw -LiteralPath $aetheriaSceneProviderComponent +foreach ($symbol in @( + "AetheriaEveUnitySceneProviderComponent", + "MonoBehaviour", + "IEveUnitySceneProviderSurfaceDocumentSource", + "IEveUnityPlayableWorldAssetManifestDocumentSource", + "IEveUnitySceneCommandSink", + "IEveUnityProviderRefreshSource", + "AetheriaEveUnitySceneProviderBridge", + "stateFilePathOverride", + "surfaceId", + "runtimeId", + "Refresh", + "Submit" +)) { + if (-not $sceneProviderComponent.Contains($symbol)) { + throw "Aetheria Eve Unity scene provider component missing symbol: $symbol" + } +} + $gameSurfaceBuilder = Get-Content -Raw -LiteralPath $aetheriaGameSurfaceBuilder foreach ($symbol in @( '"world.scene3d"', diff --git a/scripts/run-eveunity-scene-provider-shell-smoke.ps1 b/scripts/run-eveunity-scene-provider-shell-smoke.ps1 index 81ea2f0..dbfe287 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", "providerSurfaceSource", "providerSurfaceDocumentSource", "livePlayableWorldClient", "providerCommandReceipts", "providerAssetManifestDocumentSource", "sceneGraphProjection", "playableWorldProjection", "playableWorldRuntimeHost", "playableWorldScenePresentation", "unityGameObjectSceneSink", "providerAssetManifestResolution", "providerAssetManifestSource")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "providerSurfaceSession", "providerSurfaceSource", "providerSurfaceDocumentSource", "livePlayableWorldClient", "providerCommandReceipts", "providerAssetManifestDocumentSource", "playableWorldClientHost", "sceneGraphProjection", "playableWorldProjection", "playableWorldRuntimeHost", "playableWorldScenePresentation", "unityGameObjectSceneSink", "providerAssetManifestResolution", "providerAssetManifestSource")) { if (-not (@($manifest.supportedFeatures) -contains $feature)) { throw "EveUnity scene provider shell missing supported feature: $feature" } @@ -68,6 +68,8 @@ $expectedFiles = @( "runtimes\incubating\eve-unity-scene\Runtime\EveUnitySceneProviderConnection.cs", "runtimes\incubating\eve-unity-scene\Runtime\EveUnityPlayableWorldLiveClient.cs", "runtimes\incubating\eve-unity-scene\Runtime\EveUnityPlayableWorldRuntime.cs", + "runtimes\incubating\eve-unity-scene\Runtime\EveUnityPlayableWorldClientHost.cs", + "runtimes\incubating\eve-unity-scene\Runtime\EveUnityProviderRefreshSource.cs", "runtimes\incubating\eve-unity-scene\Runtime\EveUnityPlayableWorldPresenter.cs", "runtimes\incubating\eve-unity-scene\Runtime\EveUnityGameObjectPlayableWorldSceneSink.cs", "runtimes\incubating\eve-unity-scene\Runtime\EveUnityPlayableWorldAssetManifest.cs", @@ -120,6 +122,23 @@ foreach ($symbol in @("EveUnityPlayableWorldRuntime", "CreateForGameObjectScene" } } +$clientHostSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-unity-scene\Runtime\EveUnityPlayableWorldClientHost.cs") -Raw +foreach ($symbol in @("EveUnityPlayableWorldClientHost", "MonoBehaviour", "providerSurfaceDocuments", "IEveUnitySceneProviderSurfaceDocumentSource", "IEveUnitySceneCommandSink", "IEveUnityPlayableWorldAssetManifestDocumentSource", "IEveUnitySceneCommandReceiptSource", "IEveUnityGameObjectAssetProvider", "IEveUnityProviderRefreshSource", "Configure", "CreateForGameObjectScene", "Connect", "Refresh", "SubmitMoveIntent", "SubmitFocusIntent", "SubmitTargetIntent", "SubmitActionIntent", "Disconnect")) { + if (-not $clientHostSource.Contains($symbol)) { + throw "EveUnity playable world client host missing symbol: $symbol" + } +} +if ($clientHostSource.Contains("Aetheria")) { + throw "EveUnity playable world client host must remain provider-agnostic and cannot reference Aetheria" +} + +$refreshSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-unity-scene\Runtime\EveUnityProviderRefreshSource.cs") -Raw +foreach ($symbol in @("IEveUnityProviderRefreshSource", "Refresh")) { + if (-not $refreshSource.Contains($symbol)) { + throw "EveUnity provider refresh source missing symbol: $symbol" + } +} + $playableWorldPresenterSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-unity-scene\Runtime\EveUnityPlayableWorldPresenter.cs") -Raw foreach ($symbol in @("IEveUnityPlayableWorldSceneSink", "IEveUnityPlayableWorldAssetResolver", "EveUnityPlayableWorldPresenter", "EveUnityPlayableWorldAssetBinding", "EveUnityPlayableWorldPresentation", "EveUnityAssetRefResolver", "ConfigureWorld", "UpsertEntity", "RemoveEntity", "provider-asset-ref", "unity-generated-placeholder")) { if (-not $playableWorldPresenterSource.Contains($symbol)) { @@ -163,7 +182,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", "GenericProviderConnectionAppliesLiveSnapshotsAndSubmitsCommandsThroughSink", "ProviderSurfaceDocumentSourceFeedsPlayableClientWithoutAetheriaTypes", "PlayableWorldRuntimeComposesProviderDocumentsAssetsReceiptsAndSceneSink", "PlayableWorldPresenterInstantiatesUpdatesAndDespawnsProviderEntities", "LivePlayableWorldClientPresentsProviderSnapshotsAndKeepsCommandsProviderOwned", "LivePlayableWorldClientRefreshesFromProviderSnapshotAfterReceiptWithoutOwningMovement", "AssetManifestMapsProviderAssetRefsToUnityLoadKeysWithoutAetheriaTypes", "AssetManifestCacheTracksPlayableWorldManifestPointerAndLiveUpdates", "AssetManifestDocumentSourceFeedsCacheWithoutChangingSceneLowering", "FakeProviderSurfaceSource", "FakeProviderSurfaceDocumentSource", "FakeCommandReceiptSource", "FakeAssetManifestSource", "FakeAssetManifestDocumentSource", "FakeCommandSink", "FakePlayableWorldSceneSink", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "pending", "reconciled", "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", "ProviderSurfaceDocumentSourceFeedsPlayableClientWithoutAetheriaTypes", "PlayableWorldRuntimeComposesProviderDocumentsAssetsReceiptsAndSceneSink", "PlayableWorldClientHostMountsInterfaceProviderWithoutProviderTypes", "PlayableWorldPresenterInstantiatesUpdatesAndDespawnsProviderEntities", "LivePlayableWorldClientPresentsProviderSnapshotsAndKeepsCommandsProviderOwned", "LivePlayableWorldClientRefreshesFromProviderSnapshotAfterReceiptWithoutOwningMovement", "AssetManifestMapsProviderAssetRefsToUnityLoadKeysWithoutAetheriaTypes", "AssetManifestCacheTracksPlayableWorldManifestPointerAndLiveUpdates", "AssetManifestDocumentSourceFeedsCacheWithoutChangingSceneLowering", "FakeProviderSurfaceSource", "FakeProviderSurfaceDocumentSource", "FakeCommandReceiptSource", "FakeAssetManifestSource", "FakeAssetManifestDocumentSource", "FakeCommandSink", "FakePlayableWorldSceneSink", "FakePlayableWorldProviderComponent", "SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "pending", "reconciled", "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" }