Select live Eve surfaces by runtime intent

This commit is contained in:
Meta Vens 2026-07-12 21:15:04 +01:00
commit 10ba7e5b07
4 changed files with 175 additions and 0 deletions

View file

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GameCult.Mesh;
#nullable enable
namespace GameCult.Eve.Surface
{
public sealed class EveSurfaceRequest
{
public string ProviderId { get; set; } = "";
public string SurfaceId { get; set; } = "";
public string SurfaceKind { get; set; } = "";
public string LoweringTarget { get; set; } = "";
public bool RequireActive { get; set; } = true;
}
public sealed class EveSurfaceSelection
{
internal EveSurfaceSelection(
EveProviderAdvertisementDocument provider,
EveAdvertisedSurface advertisement,
CultMeshDocumentHandle<EveSurfaceDocument> surface)
{
Provider = provider;
Advertisement = advertisement;
Surface = surface;
}
public EveProviderAdvertisementDocument Provider { get; }
public EveAdvertisedSurface Advertisement { get; }
public CultMeshDocumentHandle<EveSurfaceDocument> Surface { get; }
}
public static class EveCultMeshSurfaceSelection
{
public static async Task<EveSurfaceSelection> SurfaceAsync(
this CultMeshClient mesh,
string endpointId,
EveSurfaceRequest? request = null,
CancellationToken cancellationToken = default)
{
if (mesh == null) throw new ArgumentNullException(nameof(mesh));
var resolved = request ?? new EveSurfaceRequest();
var providers = await mesh.CollectionAsync<EveProviderAdvertisementDocument>(endpointId, cancellationToken)
.ConfigureAwait(false);
var match = Select(await providers.LatestAsync().ConfigureAwait(false), resolved);
var surface = await mesh.DocumentAsync<EveSurfaceDocument>(
endpointId,
match.Surface.RecordRef,
cancellationToken)
.ConfigureAwait(false);
return new EveSurfaceSelection(match.Provider, match.Surface, surface);
}
public static (EveProviderAdvertisementDocument Provider, EveAdvertisedSurface Surface) Select(
IEnumerable<EveProviderAdvertisementDocument> providers,
EveSurfaceRequest request)
{
if (providers == null) throw new ArgumentNullException(nameof(providers));
if (request == null) throw new ArgumentNullException(nameof(request));
var match = providers
.Where(provider => Matches(request.ProviderId, provider.ProviderId))
.SelectMany(provider => provider.Surfaces.Select(surface => new { Provider = provider, Surface = surface }))
.Where(candidate => Matches(request.SurfaceId, candidate.Surface.SurfaceId))
.Where(candidate => Matches(request.SurfaceKind, candidate.Surface.SurfaceKind))
.Where(candidate => !request.RequireActive || string.Equals(candidate.Surface.Status, "active", StringComparison.OrdinalIgnoreCase))
.Where(candidate => string.IsNullOrWhiteSpace(request.LoweringTarget) ||
candidate.Surface.WorldInteraction?.LoweringTargets.Any(target =>
string.Equals(target, request.LoweringTarget, StringComparison.Ordinal)) == true)
.OrderBy(candidate => candidate.Provider.ProviderId, StringComparer.Ordinal)
.ThenBy(candidate => candidate.Surface.SurfaceId, StringComparer.Ordinal)
.FirstOrDefault();
if (match == null)
throw new InvalidOperationException(
$"No advertised Eve surface matched provider='{request.ProviderId}', surface='{request.SurfaceId}', " +
$"kind='{request.SurfaceKind}', loweringTarget='{request.LoweringTarget}'.");
if (!string.Equals(match.Surface.Transport, "cultmesh-record", StringComparison.Ordinal))
throw new InvalidOperationException(
$"Eve surface '{match.Surface.SurfaceId}' uses unsupported transport '{match.Surface.Transport}'.");
if (string.IsNullOrWhiteSpace(match.Surface.RecordRef))
throw new InvalidOperationException($"Eve surface '{match.Surface.SurfaceId}' does not advertise a record reference.");
return (match.Provider, match.Surface);
}
private static bool Matches(string expected, string actual) =>
string.IsNullOrWhiteSpace(expected) || string.Equals(expected, actual, StringComparison.Ordinal);
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b3c40df8a2a14d7db2f45f7018591fd3

View file

@ -0,0 +1,79 @@
using System;
using GameCult.Eve.Surface;
using NUnit.Framework;
namespace GameCult.Eve.UnityScene.Tests
{
public sealed class EveCultMeshSurfaceSelectionTests
{
[Test]
public void Select_MatchesDeveloperIntentWithoutPhysicalTopology()
{
var provider = Provider(
new EveAdvertisedSurface(
"aetheria.pilot",
EveSurfaceDocument.SchemaId,
"eve:surface:aetheria.pilot",
"cultmesh-record",
"active",
"interactive-world",
new EveWorldInteractionAdvertisement(
"world3d",
Array.Empty<string>(),
"provider-command",
"eve:commands",
EveCommandReceiptDocument.SchemaId,
"eve:receipts",
"eve:assets",
new[] { "unity-scene", "web" },
"provider")));
var selected = EveCultMeshSurfaceSelection.Select(
new[] { provider },
new EveSurfaceRequest
{
ProviderId = "aetheria.daemon",
SurfaceKind = "interactive-world",
LoweringTarget = "unity-scene"
});
Assert.That(selected.Provider, Is.SameAs(provider));
Assert.That(selected.Surface.SurfaceId, Is.EqualTo("aetheria.pilot"));
Assert.That(selected.Surface.RecordRef, Is.EqualTo("eve:surface:aetheria.pilot"));
}
[Test]
public void Select_RejectsUnsupportedLoweringTarget()
{
var provider = Provider(new EveAdvertisedSurface(
"aetheria.pilot",
EveSurfaceDocument.SchemaId,
"eve:surface:aetheria.pilot",
"cultmesh-record",
"active",
"interactive-world",
new EveWorldInteractionAdvertisement(
"world3d", Array.Empty<string>(), "", "", "", "", "",
new[] { "unity-scene" }, "provider")));
Assert.Throws<InvalidOperationException>(() => EveCultMeshSurfaceSelection.Select(
new[] { provider },
new EveSurfaceRequest { LoweringTarget = "flutter" }));
}
private static EveProviderAdvertisementDocument Provider(EveAdvertisedSurface surface) =>
new EveProviderAdvertisementDocument(
"aetheria.daemon",
"aetheria",
"aetheria",
"Aetheria",
"game",
"aetheria.daemon",
"2026-07-12T00:00:00Z",
new EveProviderFreshness("fresh", "2026-07-12T00:00:00Z", 5000),
Array.Empty<string>(),
Array.Empty<EveProviderWitness>(),
new[] { surface },
Array.Empty<EveAdvertisedCommand>());
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c578fb24db4b41fc88ea08bde4c42dd4