Use provider-native Eve surface access

This commit is contained in:
Meta Vens 2026-06-29 07:01:38 +01:00
commit 8154a8cfc9
7 changed files with 180 additions and 86 deletions

View file

@ -3212,25 +3212,25 @@ static void RequireEveRuntimeBootstrap(string root)
var requiredPresenterSymbols = new[]
{
"private string surfaceId = AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId",
"ReadDaemonSurface(statePath)",
"ReadDaemonSurface(stateBoot.StateFilePath)",
"AetheriaUnityRuntimeClientProvider.ResolveClient(",
"ReadDaemonSurface(stateBoot)",
"AetheriaUnityRuntimeClientProvider.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(",
"AetheriaUnityRuntimeClientProvider.EveSurfaceStateRefResolver(",
"CultMeshReactiveDocument<global::Aetheria.State.Documents.EveSurfaceState>",
"ResolveReactiveDaemonSurfaceState(",
"client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.Game)",
"client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.GameTui)",
"client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.Editor)",
"client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.EditorTui)",
"AetheriaClientEveSurface.Game",
"AetheriaClientEveSurface.GameTui",
"AetheriaClientEveSurface.Editor",
"AetheriaClientEveSurface.EditorTui",
"DisposeReactiveSurfaceState()",
"AetheriaRuntimeEveSurfaceAdapter.ToEveSurfaceDocument(",
"private bool ShouldMountSurface(",
"_mountedSurfaceVersion != surface.Version",
"private static readonly AetheriaEveUnitySurfaceChrome RootOnlyChrome",
"UseShell = false",
"MountSurface(string statePath, EveSurfaceDocument surface)",
"MountSurface(AetheriaRuntimeStateBootReport stateBoot, EveSurfaceDocument surface)",
"AetheriaEveUnitySurfaceHost.Render(",
"AetheriaRuntimeDaemonSurfaceCommands.TrySubmit(ResolveClient(statePath), request, out var daemonEnvelope)",
".Ui.SurfaceCommandAsync("
"control.TrySubmitSurfaceCommand(request, out var daemonEnvelope)",
".SurfaceCommandAsync("
};
var missingPresenterSymbols = requiredPresenterSymbols
.Where(symbol => !presenter.Contains(symbol, StringComparison.Ordinal))
@ -3271,11 +3271,20 @@ static void RequireEveRuntimeBootstrap(string root)
"Aetheria Eve presenter still lowers daemon-published surfaces directly instead of delegating to the shared Unity Eve surface host.");
}
if (presenter.Contains("ResolveClient(", StringComparison.Ordinal) ||
presenter.Contains("AetheriaRuntimeDaemonSurfaceCommands.TrySubmit(", StringComparison.Ordinal) ||
presenter.Contains("client.State.", StringComparison.Ordinal) ||
presenter.Contains("client.Ui.", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Aetheria Eve presenter must use provider-native typed state/control/UI access instead of raw client plumbing.");
}
var daemonSubmitIndex = presenter.IndexOf(
"AetheriaRuntimeDaemonSurfaceCommands.TrySubmit(ResolveClient(statePath), request, out var daemonEnvelope)",
"control.TrySubmitSurfaceCommand(request, out var daemonEnvelope)",
StringComparison.Ordinal);
var eveSubmitIndex = presenter.IndexOf(
".Ui.SurfaceCommandAsync(",
".SurfaceCommandAsync(",
StringComparison.Ordinal);
if (daemonSubmitIndex < 0 || eveSubmitIndex < 0 || daemonSubmitIndex > eveSubmitIndex)
{
@ -3287,12 +3296,8 @@ static void RequireEveRuntimeBootstrap(string root)
{
"public static class AetheriaRuntimeDaemonSurfaceCommands",
"EveSurfaceCommandRequest request",
"request.ProviderId, \"aetheria.daemon\"",
"AetheriaClient",
"client.CurrentDaemonFrame()",
"new AetheriaRuntimeDaemonOperationClient(",
"AetheriaRuntimeDaemonSurfaceCommandCatalog.TrySubmitArgumentless(",
"AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandPrefix"
"client.Control.TrySubmitSurfaceCommand(request, out envelope)"
};
var missingDaemonSurfaceCommandSymbols = requiredDaemonSurfaceCommandSymbols
.Where(symbol => !daemonSurfaceCommands.Contains(symbol, StringComparison.Ordinal))
@ -5297,7 +5302,7 @@ static void RequireInventoryCargoItemDetailsUseEveSurface(string root)
!eveUnitySurfaceHost.Contains("prop.Key.EndsWith(\"Ref\", StringComparison.Ordinal)", StringComparison.Ordinal) ||
!eveUnitySurfaceHost.Contains("CreateDefaultStateRefResolver()", StringComparison.Ordinal) ||
!eveUnitySurfaceHost.Contains("AetheriaUnityRuntimeClientProvider", StringComparison.Ordinal) ||
!eveUnitySurfaceHost.Contains(".CreateEveSurfaceCultMeshStateRefResolver()", StringComparison.Ordinal) ||
!eveUnitySurfaceHost.Contains("AetheriaUnityRuntimeClientProvider.EveSurfaceStateRefResolver(", StringComparison.Ordinal) ||
!runtimeEveSurfaceAdapter.Contains("public static EveSurfaceDocument ResolveStateRefs(", StringComparison.Ordinal) ||
!runtimeEveSurfaceAdapter.Contains("ResolvePropRefs(props, resolveStateRef)", StringComparison.Ordinal) ||
!runtimeEveSurfaceAdapter.Contains("ResolvePropRef(props, AetheriaRuntimeSurfaceStateRefs.Source, \"value\", resolveStateRef)", StringComparison.Ordinal) ||
@ -12578,7 +12583,7 @@ static void RequireTypedEveCommandBodies(string root)
"internal static class AetheriaRuntimeEveCommands",
"public static class AetheriaRuntimeEveCommandClient",
"namespace GameCult.Aetheria.State.Verse",
"AetheriaUnityRuntimeClientProvider.ResolveClient(",
"SurfaceCommandAsync(",
"internal async Task<AetheriaRuntimeDaemonCommandEnvelope> SubmitDaemonCommandAsync(",
"internal async Task<AetheriaRuntimeEveCommandEnvelope> SubmitEveCommandAsync(",
"CreatePlayerSettingsCommand(",
@ -16337,6 +16342,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root)
"public static AetheriaUi Ui(",
"public static CultMeshReactiveDocument<TDocument> Reactive<TDocument>(string runtimeId = \"\")",
"public static CultMeshReactiveDocument<TDocument> Reactive<TDocument>(",
"AetheriaClientEveSurface surface",
"public static CultMeshStateRefResolver EveSurfaceStateRefResolver(string runtimeId = \"\")",
"public static CultMeshStateRefResolver EveSurfaceStateRefResolver(",
"private static readonly Dictionary<string, AetheriaClient> RuntimeClients",
"RuntimeClients.TryGetValue(cacheKey, out var runtimeClient)",
"RuntimeClients[cacheKey] = runtimeClient",
@ -16890,18 +16898,26 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root)
string.Join(", ", mainMenuReaderHits));
}
if (!eveSurfacePresenter.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) ||
!eveSurfacePresenter.Contains("ReadDaemonSurface(statePath)", StringComparison.Ordinal))
if (!eveSurfacePresenter.Contains("AetheriaUnityRuntimeClientProvider.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(", StringComparison.Ordinal) ||
!eveSurfacePresenter.Contains("ReadDaemonSurface(stateBoot)", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Aetheria Eve surface presenter no longer routes daemon surface lookup through the shared AetheriaClient facade.");
"Aetheria Eve surface presenter must read daemon surface state through provider-native reactive typed documents.");
}
if (!eveSurfacePresenter.Contains("client.State.CreateEveSurfaceCultMeshStateRefResolver()", StringComparison.Ordinal) ||
!eveSurfacePresenter.Contains("ResolveClient(statePath).State.CreateEveSurfaceCultMeshStateRefResolver()", StringComparison.Ordinal))
if (!eveSurfacePresenter.Contains("AetheriaUnityRuntimeClientProvider.EveSurfaceStateRefResolver(", StringComparison.Ordinal) ||
!eveUnitySurfaceHost.Contains("AetheriaUnityRuntimeClientProvider.EveSurfaceStateRefResolver(", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Aetheria Eve surface presenter no longer resolves provider state refs through managed AetheriaClientState documents.");
"Unity Eve surface lowering must resolve provider state refs through provider-native managed AetheriaClientState documents.");
}
if (eveSurfacePresenter.Contains("AetheriaUnityRuntimeClientProvider.ResolveClient(", StringComparison.Ordinal) ||
eveSurfacePresenter.Contains("client.State.CreateEveSurfaceCultMeshStateRefResolver()", StringComparison.Ordinal) ||
eveSurfacePresenter.Contains("ResolveClient(statePath).State.CreateEveSurfaceCultMeshStateRefResolver()", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Aetheria Eve surface presenter still peels through the raw client instead of using provider-native state/ref handles.");
}
if (eveSurfacePresenter.Contains("AetheriaRuntimeStateReader.CreateEveSurfaceStateRefResolver", StringComparison.Ordinal))

View file

@ -119,6 +119,23 @@ public static class AetheriaUnityRuntimeClientProvider
return RuntimeState(stateBoot, runtimeId).Reactive<TDocument>();
}
public static CultMeshReactiveDocument<TDocument> Reactive<TDocument>(
AetheriaClientEveSurface surface,
string runtimeId = "")
where TDocument : class
{
return RuntimeState(runtimeId).Reactive<TDocument>(surface);
}
public static CultMeshReactiveDocument<TDocument> Reactive<TDocument>(
AetheriaRuntimeStateBootReport stateBoot,
AetheriaClientEveSurface surface,
string runtimeId = "")
where TDocument : class
{
return RuntimeState(stateBoot, runtimeId).Reactive<TDocument>(surface);
}
public static CultMeshReactiveDocument<TDocument> Reactive<TDocument>(
AetheriaRuntimeRtsViewportBounds viewport,
string runtimeId = "")
@ -153,6 +170,18 @@ public static class AetheriaUnityRuntimeClientProvider
return RuntimeState(stateBoot, runtimeId).Reactive<TDocument>(index);
}
public static CultMeshStateRefResolver EveSurfaceStateRefResolver(string runtimeId = "")
{
return RuntimeState(runtimeId).CreateEveSurfaceCultMeshStateRefResolver();
}
public static CultMeshStateRefResolver EveSurfaceStateRefResolver(
AetheriaRuntimeStateBootReport stateBoot,
string runtimeId = "")
{
return RuntimeState(stateBoot, runtimeId).CreateEveSurfaceCultMeshStateRefResolver();
}
public static void Dispose()
{
_playerSettingsDocument?.Dispose();

View file

@ -84,14 +84,14 @@ namespace GameCult.Aetheria.EveRuntime
return;
}
var surface = ReadDaemonSurface(statePath);
var surface = ReadDaemonSurface(stateBoot);
if (surface == null)
{
root.Add(BuildError($"Eve surface not found: {surfaceId}"));
return;
}
MountSurface(statePath, surface);
MountSurface(stateBoot, surface);
}
private void OnEnable()
@ -120,45 +120,47 @@ namespace GameCult.Aetheria.EveRuntime
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
return;
var surface = ReadDaemonSurface(stateBoot.StateFilePath);
var surface = ReadDaemonSurface(stateBoot);
if (surface == null || !ShouldMountSurface(stateBoot.StateFilePath, surface))
return;
MountSurface(stateBoot.StateFilePath, surface);
MountSurface(stateBoot, surface);
}
private EveSurfaceDocument? ReadDaemonSurface(string statePath)
private EveSurfaceDocument? ReadDaemonSurface(AetheriaRuntimeStateBootReport stateBoot)
{
var client = ResolveClient(statePath);
var surface = ReadDaemonSurfaceState(client);
var surface = ReadDaemonSurfaceState(stateBoot);
return surface == null
? null
: AetheriaRuntimeEveSurfaceAdapter.ToEveSurfaceDocument(
surface,
client.State.CreateEveSurfaceCultMeshStateRefResolver());
AetheriaUnityRuntimeClientProvider.EveSurfaceStateRefResolver(
stateBoot,
"unity-eve-surface-presenter"));
}
private global::Aetheria.State.Documents.EveSurfaceState? ReadDaemonSurfaceState(AetheriaClient client)
private global::Aetheria.State.Documents.EveSurfaceState? ReadDaemonSurfaceState(
AetheriaRuntimeStateBootReport stateBoot)
{
var reactive = ResolveReactiveDaemonSurfaceState(client);
var reactive = ResolveReactiveDaemonSurfaceState(stateBoot);
return reactive?.Current;
}
private CultMeshReactiveDocument<global::Aetheria.State.Documents.EveSurfaceState>? ResolveReactiveDaemonSurfaceState(
AetheriaClient client)
AetheriaRuntimeStateBootReport stateBoot)
{
if (_reactiveSurfaceState != null &&
string.Equals(_reactiveSurfaceStatePath, client.StatePath, StringComparison.Ordinal) &&
string.Equals(_reactiveSurfaceStatePath, stateBoot.StateFilePath, StringComparison.Ordinal) &&
string.Equals(_reactiveSurfaceId, surfaceId, StringComparison.Ordinal))
{
return _reactiveSurfaceState;
}
DisposeReactiveSurfaceState();
_reactiveSurfaceState = CreateReactiveDaemonSurfaceState(client);
_reactiveSurfaceState = CreateReactiveDaemonSurfaceState(stateBoot);
if (_reactiveSurfaceState != null)
{
_reactiveSurfaceStatePath = client.StatePath;
_reactiveSurfaceStatePath = stateBoot.StateFilePath;
_reactiveSurfaceId = surfaceId ?? "";
}
@ -166,30 +168,35 @@ namespace GameCult.Aetheria.EveRuntime
}
private CultMeshReactiveDocument<global::Aetheria.State.Documents.EveSurfaceState>? CreateReactiveDaemonSurfaceState(
AetheriaClient client)
AetheriaRuntimeStateBootReport stateBoot)
{
if (string.Equals(surfaceId, AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId, StringComparison.Ordinal))
return client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.Game);
return AetheriaUnityRuntimeClientProvider.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(
stateBoot,
AetheriaClientEveSurface.Game,
"unity-eve-surface-presenter");
if (string.Equals(surfaceId, AetheriaRuntimeDaemonGameSurfaceBuilder.TuiSurfaceId, StringComparison.Ordinal))
return client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.GameTui);
return AetheriaUnityRuntimeClientProvider.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(
stateBoot,
AetheriaClientEveSurface.GameTui,
"unity-eve-surface-presenter");
if (string.Equals(surfaceId, AetheriaRuntimeDaemonEditorSurfaceBuilder.SurfaceId, StringComparison.Ordinal))
return client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.Editor);
return AetheriaUnityRuntimeClientProvider.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(
stateBoot,
AetheriaClientEveSurface.Editor,
"unity-eve-surface-presenter");
if (string.Equals(surfaceId, AetheriaRuntimeDaemonEditorSurfaceBuilder.TuiSurfaceId, StringComparison.Ordinal))
return client.State.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(AetheriaClientEveSurface.EditorTui);
return AetheriaUnityRuntimeClientProvider.Reactive<global::Aetheria.State.Documents.EveSurfaceState>(
stateBoot,
AetheriaClientEveSurface.EditorTui,
"unity-eve-surface-presenter");
return null;
}
private AetheriaClient ResolveClient(string statePath)
{
return AetheriaUnityRuntimeClientProvider.ResolveClient(
statePath,
"unity-eve-surface-presenter");
}
private UIDocument ResolveDocument()
{
if (_document == null)
@ -222,25 +229,34 @@ namespace GameCult.Aetheria.EveRuntime
!string.Equals(_mountedSurfaceUpdatedAtUtc, surface.UpdatedAtUtc, StringComparison.Ordinal);
}
private void MountSurface(string statePath, EveSurfaceDocument surface)
private void MountSurface(AetheriaRuntimeStateBootReport stateBoot, EveSurfaceDocument surface)
{
var statePath = stateBoot.StateFilePath;
AetheriaEveUnitySurfaceHost.Render(
transform,
ResolveDocument(),
"Aetheria Eve Surface",
surface,
request => EmitCommand(statePath, request),
request => EmitCommand(stateBoot, request),
RootOnlyChrome,
ResolveClient(statePath).State.CreateEveSurfaceCultMeshStateRefResolver());
AetheriaUnityRuntimeClientProvider.EveSurfaceStateRefResolver(
stateBoot,
"unity-eve-surface-presenter"));
_mountedStatePath = statePath;
_mountedSurfaceId = surface.Surface.Id;
_mountedSurfaceVersion = surface.Version;
_mountedSurfaceUpdatedAtUtc = surface.UpdatedAtUtc ?? "";
}
private void EmitCommand(string statePath, EveSurfaceCommandRequest request)
private void EmitCommand(AetheriaRuntimeStateBootReport stateBoot, EveSurfaceCommandRequest request)
{
if (AetheriaRuntimeDaemonSurfaceCommands.TrySubmit(ResolveClient(statePath), request, out var daemonEnvelope))
var clientId = string.IsNullOrWhiteSpace(request.ClientId)
? "unity-eve-surface-presenter"
: request.ClientId;
var control = AetheriaUnityRuntimeClientProvider.Control(
stateBoot,
clientId);
if (control.TrySubmitSurfaceCommand(request, out var daemonEnvelope))
{
Debug.Log(
$"Submitted Aetheria daemon operation from Eve surface: {daemonEnvelope!.Kind} {daemonEnvelope.CommandId}");
@ -249,8 +265,9 @@ namespace GameCult.Aetheria.EveRuntime
try
{
var envelope = ResolveClient(statePath)
.Ui.SurfaceCommandAsync(
var envelope = AetheriaUnityRuntimeClientProvider
.Ui(stateBoot, "unity-eve-surface-presenter")
.SurfaceCommandAsync(
request,
string.IsNullOrWhiteSpace(request.ClientId) ? "unity-uitoolkit" : request.ClientId)
.GetAwaiter()

View file

@ -226,10 +226,9 @@ namespace GameCult.Aetheria.EveRuntime
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
return null;
return AetheriaUnityRuntimeClientProvider
.ResolveClient(stateBoot, "unity-eve-surface-host")
.State
.CreateEveSurfaceCultMeshStateRefResolver();
return AetheriaUnityRuntimeClientProvider.EveSurfaceStateRefResolver(
stateBoot,
"unity-eve-surface-host");
}
}

View file

@ -1,4 +1,5 @@
using System;
using GameCult.Eve.Surface;
using GameCult.Mesh;
#nullable enable
@ -245,5 +246,10 @@ namespace GameCult.Aetheria.State.Verse
destinationX,
destinationY,
hasDestinationPosition);
public bool TrySubmitSurfaceCommand(
EveSurfaceCommandRequest request,
out AetheriaRuntimeDaemonCommandEnvelope? envelope) =>
_operations.TrySubmitSurfaceCommand(request, out envelope);
}
}

View file

@ -1,4 +1,5 @@
using System;
using GameCult.Eve.Surface;
using GameCult.Mesh;
#nullable enable
@ -347,6 +348,37 @@ public sealed class AetheriaRuntimeDaemonOperationsClient
hasDestinationPosition));
}
public bool TrySubmitSurfaceCommand(
EveSurfaceCommandRequest request,
out AetheriaRuntimeDaemonCommandEnvelope? envelope)
{
envelope = null;
if (request == null ||
!string.Equals(request.ProviderId, "aetheria.daemon", StringComparison.Ordinal) ||
!TryResolveSurfaceCommandKind(request, out var kind))
{
return false;
}
try
{
envelope = Submit((client, frame) =>
AetheriaRuntimeDaemonSurfaceCommandCatalog.TrySubmitArgumentless(
client,
frame,
kind,
out var submitted)
? submitted!
: throw new UnsupportedSurfaceCommandException());
return true;
}
catch (UnsupportedSurfaceCommandException)
{
envelope = null;
return false;
}
}
private CultMeshOperationReceipt Send(
Func<AetheriaRuntimeDaemonOperationClient, AetheriaRuntimeDaemonFrameDocument?, AetheriaRuntimeDaemonCommandEnvelope> submit)
{
@ -358,5 +390,22 @@ public sealed class AetheriaRuntimeDaemonOperationsClient
{
return _submit(submit);
}
private static bool TryResolveSurfaceCommandKind(
EveSurfaceCommandRequest request,
out AetheriaRuntimeDaemonCommandKinds kind)
{
kind = AetheriaRuntimeDaemonCommandKinds.None;
var command = request.Operation?.OperationId ?? "";
if (command.StartsWith(AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandPrefix, StringComparison.Ordinal))
command = command.Substring(AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandPrefix.Length);
return Enum.TryParse(command, ignoreCase: false, out kind) &&
kind != AetheriaRuntimeDaemonCommandKinds.None;
}
private sealed class UnsupportedSurfaceCommandException : Exception
{
}
}
}

View file

@ -15,34 +15,12 @@ namespace GameCult.Aetheria.State.Verse
envelope = null;
if (request == null ||
client == null ||
!string.Equals(request.ProviderId, "aetheria.daemon", StringComparison.Ordinal) ||
!TryResolveKind(request, out var kind))
!client.Control.TrySubmitSurfaceCommand(request, out envelope))
{
return false;
}
var frame = client.CurrentDaemonFrame();
var operationClient = new AetheriaRuntimeDaemonOperationClient(
client.StatePath,
string.IsNullOrWhiteSpace(request.ClientId) ? AetheriaRuntimeDaemonOperationClient.DefaultClientId : request.ClientId,
frame?.SessionId ?? "local",
client.SubmitDaemonCommandDocument);
return AetheriaRuntimeDaemonSurfaceCommandCatalog.TrySubmitArgumentless(
operationClient,
frame,
kind,
out envelope);
}
private static bool TryResolveKind(EveSurfaceCommandRequest request, out AetheriaRuntimeDaemonCommandKinds kind)
{
kind = AetheriaRuntimeDaemonCommandKinds.None;
var command = request.Operation?.OperationId ?? "";
if (command.StartsWith(AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandPrefix, StringComparison.Ordinal))
command = command.Substring(AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandPrefix.Length);
return Enum.TryParse(command, ignoreCase: false, out kind) &&
kind != AetheriaRuntimeDaemonCommandKinds.None;
return true;
}
}
}