Preserve Unity scene embedded slots

This commit is contained in:
Meta Vens 2026-07-09 12:49:30 +01:00
commit d2787b407b
8 changed files with 63 additions and 11 deletions

View file

@ -428,9 +428,11 @@ Recently cut:
`scripts/run-eveunity-scene-release-contract-smoke.ps1`. It also reuses the
shared EveUnity capture request builder through
`scripts/run-eveunity-scene-capture-contract-smoke.ps1`, while the actual
Unity scene PNG/frame artifact remains pending. Plugin projection adapters
stay unsupported until Sai, Norn, or TeX sidecar projection support exists in
the Unity scene runtime.
Unity scene PNG/frame artifact remains pending. The scene graph now preserves
embedded document slot identity for resolver-backed child surfaces without
owning the nested plugin semantics. Plugin projection adapters stay
unsupported until Sai, Norn, or TeX sidecar projection support exists in the
Unity scene runtime.
- Unity scene now has a direct split handoff smoke at
`scripts/run-eveunity-scene-split-handoff-smoke.ps1`. The smoke verifies the
scene runtime's current source paths for runtime body, scene graph lowering,

View file

@ -331,9 +331,9 @@ Plugin projection and repo graduation remain blockers.
EveUnity includes both the active `unity-uitoolkit` package proof
and the pending `unity-scene` runtime boundary; UI Toolkit support is not
treated as full Unity scene/world lowering. The `unity-scene` runtime now claims
a narrow scene graph command surface and exposes a typed capture request
contract, while actual scene capture, release, and plugin projection adapters
remain explicit blockers.
a narrow scene graph command surface, preserves embedded document slot identity,
and exposes a typed capture request contract, while actual scene capture,
release, and plugin projection adapters remain explicit blockers.
The Flutter widget smoke also exercises `EveProviderCatalog` and
`EveProviderPicker` against a conformance-export-shaped provider list. That is

View file

@ -84,9 +84,25 @@ namespace GameCult.Eve.UnityScene
component.Style,
component.StateBindings.Count,
component.EmbeddedDocuments.Count,
BuildEmbeddedDocumentSlots(component.EmbeddedDocuments),
children);
}
private static IReadOnlyList<EveUnitySceneEmbeddedDocumentSlot> BuildEmbeddedDocumentSlots(
IReadOnlyList<EveEmbeddedDocumentSlot> embeddedDocuments)
{
var slots = new List<EveUnitySceneEmbeddedDocumentSlot>(embeddedDocuments.Count);
foreach (var slot in embeddedDocuments)
{
slots.Add(new EveUnitySceneEmbeddedDocumentSlot(
slot.SlotId,
slot.DocumentId,
slot.SchemaId,
slot.PresentationKind));
}
return slots;
}
private static string SceneObjectKind(string componentKind)
{
if (string.IsNullOrWhiteSpace(componentKind))
@ -151,6 +167,7 @@ namespace GameCult.Eve.UnityScene
IReadOnlyDictionary<string, string> style,
int stateBindingCount,
int embeddedDocumentCount,
IReadOnlyList<EveUnitySceneEmbeddedDocumentSlot> embeddedDocuments,
IReadOnlyList<EveUnitySceneNode> children)
{
Id = id ?? "";
@ -161,6 +178,7 @@ namespace GameCult.Eve.UnityScene
Style = style ?? new Dictionary<string, string>(StringComparer.Ordinal);
StateBindingCount = stateBindingCount;
EmbeddedDocumentCount = embeddedDocumentCount;
EmbeddedDocuments = embeddedDocuments ?? Array.Empty<EveUnitySceneEmbeddedDocumentSlot>();
Children = children ?? Array.Empty<EveUnitySceneNode>();
}
@ -180,9 +198,34 @@ namespace GameCult.Eve.UnityScene
public int EmbeddedDocumentCount { get; }
public IReadOnlyList<EveUnitySceneEmbeddedDocumentSlot> EmbeddedDocuments { get; }
public IReadOnlyList<EveUnitySceneNode> Children { get; }
}
public sealed class EveUnitySceneEmbeddedDocumentSlot
{
public EveUnitySceneEmbeddedDocumentSlot(
string slotId,
string documentId,
string schemaId,
string presentationKind)
{
SlotId = slotId ?? "";
DocumentId = documentId ?? "";
SchemaId = schemaId ?? "";
PresentationKind = presentationKind ?? "";
}
public string SlotId { get; }
public string DocumentId { get; }
public string SchemaId { get; }
public string PresentationKind { get; }
}
public sealed class EveUnitySceneProviderSurfaceAdvertisement
{
public EveUnitySceneProviderSurfaceAdvertisement(

View file

@ -40,6 +40,11 @@ namespace GameCult.Eve.UnityScene.Tests
Assert.That(projection.Root.Children[1].Props["command"], Is.EqualTo("aetheria.daemon.focus"));
Assert.That(projection.Root.Children[2].SceneObjectKind, Is.EqualTo("plugin-placeholder"));
Assert.That(projection.Root.Children[2].EmbeddedDocumentCount, Is.EqualTo(1));
Assert.That(projection.Root.Children[2].EmbeddedDocuments.Count, Is.EqualTo(1));
Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].SlotId, Is.EqualTo("norn.map"));
Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].DocumentId, Is.EqualTo("cultmesh://aetheria/norn/map"));
Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].SchemaId, Is.EqualTo("gamecult.eve.surface.v1"));
Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].PresentationKind, Is.EqualTo("scene-overlay"));
}
[Test]

View file

@ -16,7 +16,8 @@
"supportedFeatures": [
"providerAdvertisements",
"commandTransport",
"sceneGraphProjection"
"sceneGraphProjection",
"embeddedDocuments"
],
"supportedPlugins": [],
"unsupportedPlugins": [

View file

@ -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")) {
foreach ($feature in @("providerAdvertisements", "commandTransport", "sceneGraphProjection", "embeddedDocuments")) {
if (-not (@($manifest.supportedFeatures) -contains $feature)) {
throw "EveUnity scene manifest missing provider-shell feature: $feature"
}

View file

@ -67,6 +67,7 @@ node $consumerScript $consumerExport `
--expect-runtime-feature electron-shell:commandTransport `
--expect-runtime-feature electron-shell:surfaceTreeProjection `
--expect-runtime-feature electron-shell:embeddedDocuments `
--expect-runtime-feature unity-scene:embeddedDocuments `
--expect-runtime-feature tui:providerAdvertisements `
--expect-runtime-feature tui:commandTransport `
--expect-runtime-feature tui:terminalGridSummary `

View file

@ -942,7 +942,7 @@
"splitHandoffPath": "runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json",
"requiredFixtures": ["aetheria-world"],
"pluginFixtures": [],
"supportedFeatures": ["providerAdvertisements", "commandTransport", "sceneGraphProjection"],
"supportedFeatures": ["providerAdvertisements", "commandTransport", "sceneGraphProjection", "embeddedDocuments"],
"supportedPlugins": [],
"unsupportedPlugins": [
{
@ -992,11 +992,11 @@
"expectedSourceSymbols": [
{
"path": "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs",
"contains": ["EveUnitySceneSurfaceLowerer", "EveUnitySceneProviderSurfaceAdvertisement", "WorldInteraction", "CommandBoundary", "ReceiptSchema", "EveSurfaceCommandRequest", "BuildSceneGraph", "EveUnitySceneNode", "SceneObjectKind", "unity-scene"]
"contains": ["EveUnitySceneSurfaceLowerer", "EveUnitySceneProviderSurfaceAdvertisement", "WorldInteraction", "CommandBoundary", "ReceiptSchema", "EveSurfaceCommandRequest", "BuildSceneGraph", "BuildEmbeddedDocumentSlots", "EveUnitySceneEmbeddedDocumentSlot", "EveUnitySceneNode", "SceneObjectKind", "unity-scene"]
},
{
"path": "runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs",
"contains": ["LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "plugin-placeholder", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"]
"contains": ["LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "plugin-placeholder", "norn.map", "cultmesh://aetheria/norn/map", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"]
},
{
"path": "tools/eveunity/eveunity-capture-contract.test.mjs",