Select live Eve surfaces by runtime intent
This commit is contained in:
parent
0dee524662
commit
10ba7e5b07
4 changed files with 175 additions and 0 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b3c40df8a2a14d7db2f45f7018591fd3
|
||||
|
|
@ -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>());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c578fb24db4b41fc88ea08bde4c42dd4
|
||||
Reference in a new issue