This repository has been archived on 2026-10-29. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
EveUnity/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs

3587 lines
180 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections;
using System.Threading.Tasks;
using GameCult.Caching;
using GameCult.Eve.PluginFields;
using GameCult.Eve.Surface;
using GameCult.Mesh;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.UIElements;
using UnityEngine.TestTools;
#nullable enable
namespace GameCult.Eve.UnityScene.Tests
{
public sealed class EveUnitySceneSurfaceLowererTests
{
[Test]
public void LiveTransportDefersDefaultBodyAuthorizationUntilAdvertisementResolution()
{
Assert.DoesNotThrow(() =>
{
using var transport = new EveUnityCultMeshLiveProviderTransport(
"test-cache",
"cultnet://127.0.0.1:3075",
"aetheria",
"aetheria",
"aetheria",
"aetheria.pilot");
});
}
[Test]
public async System.Threading.Tasks.Task LiveTransportResolvesViewGenerationWhenNextPublicationAlreadyExists()
{
var view = EntityLeaseDocument();
var generationN = BodyPublication(view, sequence: view.Sequence);
var generationNPlusOne = BodyPublication(view, sequence: view.Sequence + 1);
var cache = new CultCache();
await cache.UpsertAsync(generationN,
new CultRecordHandle<CultMeshBodyPublicationDocument>(generationN.RecordKey));
await cache.UpsertAsync(generationNPlusOne,
new CultRecordHandle<CultMeshBodyPublicationDocument>(generationNPlusOne.RecordKey));
var handle = (CultMeshBodyPublicationHandle)typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("BodyPublicationHandle", BindingFlags.NonPublic | BindingFlags.Static)!
.Invoke(null, new object[] { view })!;
var resolved = cache.Get<CultMeshBodyPublicationDocument>(handle.RecordKey);
Assert.That(resolved, Is.Not.Null);
handle.Validate(resolved!);
Assert.That(resolved!.Sequence, Is.EqualTo(view.Sequence));
Assert.That(handle.RecordKey, Is.Not.EqualTo(generationNPlusOne.RecordKey));
}
[Test]
public void LiveTransportReusesStableLayoutForNewBodyPublications()
{
using var transport = new EveUnityCultMeshLiveProviderTransport(
"test-cache",
"cultnet://127.0.0.1:3075",
"aetheria",
"aetheria",
"aetheria",
"aetheria.pilot");
var view = EntityLeaseDocument();
var publication = BodyPublication(view, view.Sequence + 1);
var queueView = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("QueueEntityView", BindingFlags.NonPublic | BindingFlags.Instance)!;
var queuePublication = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("QueueBodyPublication", BindingFlags.NonPublic | BindingFlags.Instance)!;
var queue = (System.Collections.Concurrent.ConcurrentQueue<object>)
typeof(EveUnityCultMeshLiveProviderTransport)
.GetField("_liveDocuments", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(transport)!;
queueView.Invoke(transport, new object[] { view });
Assert.That(queue, Is.Empty, "a layout without its body publication is not a complete generation");
queuePublication.Invoke(transport, new object[] { publication });
Assert.That(queue.TryDequeue(out var queued), Is.True);
var generation = queued!.GetType().GetProperty("View")!.GetValue(queued) as EveEntitySoaViewDocument;
Assert.That(generation, Is.Not.Null);
Assert.That(generation!.Sequence, Is.EqualTo(publication.Sequence));
Assert.That(generation.ProducerEpoch, Is.EqualTo(publication.ProducerEpoch));
Assert.That(generation.Buffers, Is.SameAs(view.Buffers));
Assert.That(generation.Columns, Is.SameAs(view.Columns));
Assert.That(generation.Identities, Is.SameAs(view.Identities));
Assert.That(generation.DirtyRanges.All(range => range.Sequence == publication.Sequence), Is.True);
}
[Test]
public void LiveTransportPairsBodyPublicationThatArrivesBeforeItsLayout()
{
using var transport = new EveUnityCultMeshLiveProviderTransport(
"test-cache",
"cultnet://127.0.0.1:3075",
"aetheria",
"aetheria",
"aetheria",
"aetheria.pilot");
var view = EntityLeaseDocument();
var publication = BodyPublication(view, view.Sequence);
var queueView = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("QueueEntityView", BindingFlags.NonPublic | BindingFlags.Instance)!;
var queuePublication = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("QueueBodyPublication", BindingFlags.NonPublic | BindingFlags.Instance)!;
var queue = (System.Collections.Concurrent.ConcurrentQueue<object>)
typeof(EveUnityCultMeshLiveProviderTransport)
.GetField("_liveDocuments", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(transport)!;
queuePublication.Invoke(transport, new object[] { publication });
Assert.That(queue, Is.Empty, "a publication without its layout is not a complete generation");
queueView.Invoke(transport, new object[] { view });
Assert.That(queue.TryDequeue(out var queued), Is.True);
Assert.That(queued!.GetType().GetProperty("View")!.GetValue(queued), Is.SameAs(view));
Assert.That(queued.GetType().GetProperty("Publication")!.GetValue(queued), Is.SameAs(publication));
}
[Test]
public void LiveTransportKeepsOnlyLatestPendingRealtimeGenerationPerBody()
{
using var transport = new EveUnityCultMeshLiveProviderTransport(
"test-cache",
"cultnet://127.0.0.1:3075",
"provider",
"provider",
"provider",
"provider.pilot");
var enqueue = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("EnqueueRealtimeEntityFrame", BindingFlags.NonPublic | BindingFlags.Instance)!;
var queue = (System.Collections.Concurrent.ConcurrentQueue<object>)
typeof(EveUnityCultMeshLiveProviderTransport)
.GetField("_liveDocuments", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(transport)!;
var latest = (Dictionary<string, CultMeshRealtimeFrame>)
typeof(EveUnityCultMeshLiveProviderTransport)
.GetField("_latestRealtimeFrames", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(transport)!;
for (var sequence = 0; sequence < 100; sequence++)
enqueue.Invoke(transport, new object[] { RealtimeFrame("entities", 7, sequence) });
enqueue.Invoke(transport, new object[] { RealtimeFrame("entities", 7, 50) });
Assert.That(queue.Count, Is.EqualTo(1), "superseded frames must not become render-thread debt");
Assert.That(latest, Has.Count.EqualTo(1));
Assert.That(latest["entities"].ProducerEpoch, Is.EqualTo(7));
Assert.That(latest["entities"].Sequence, Is.EqualTo(99));
}
[Test]
public void LiveTransportReadsLaterMappedFramesWithoutNewControlDocuments()
{
using var transport = new EveUnityCultMeshLiveProviderTransport(
"test-cache",
"cultnet://127.0.0.1:3075",
"provider",
"provider",
"provider",
"provider.pilot");
var view = EntityLeaseDocument();
view.Sequence = 0;
using var publisher = new CultMeshFrameBodyPublisher(
view.Buffers[0].BufferId,
view.BodySchemaId,
view.LayoutVersion,
view.Capacity,
view.ProducerEpoch,
slotByteLength: 4);
publisher.TryPublish(new byte[] { 1, 2, 3, 4 }, DateTimeOffset.UtcNow, out var bootstrap);
var publication = new CultMeshBodyPublicationDocument
{
BodyId = bootstrap.BodyId,
ProducerId = view.ProviderId,
SchemaId = bootstrap.SchemaId,
LayoutVersion = bootstrap.LayoutVersion,
ByteSize = bootstrap.ByteSize,
Capacity = bootstrap.Capacity,
ProducerEpoch = bootstrap.ProducerEpoch,
Sequence = bootstrap.Sequence,
Synchronization = bootstrap.Synchronization,
LivenessExpiresAtUnixMs = bootstrap.LeaseExpiresAtUnixMs,
Representations = new[] { bootstrap }
};
var queueView = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("QueueEntityView", BindingFlags.NonPublic | BindingFlags.Instance)!;
var ensureCursor = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("EnsureMappedEntityFrameCursor", BindingFlags.NonPublic | BindingFlags.Instance)!;
var pumpCursor = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("PumpMappedEntityFrame", BindingFlags.NonPublic | BindingFlags.Instance)!;
var queue = (System.Collections.Concurrent.ConcurrentQueue<object>)
typeof(EveUnityCultMeshLiveProviderTransport)
.GetField("_liveDocuments", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(transport)!;
queueView.Invoke(transport, new object[] { view });
queue.TryDequeue(out _);
ensureCursor.Invoke(transport, new object[] { publication });
publisher.TryPublish(new byte[] { 9, 8, 7, 6 }, DateTimeOffset.UtcNow, out _);
EveEntitySoaViewDocument? presented = null;
byte presentedByte = 0;
transport.EntityViewAvailable += (generation, lease) =>
{
presented = generation;
presentedByte = lease.ReadByte(0);
lease.Dispose();
};
pumpCursor.Invoke(transport, Array.Empty<object>());
Assert.That(presented, Is.Not.Null);
Assert.That(presented!.Sequence, Is.EqualTo(1));
Assert.That(presentedByte, Is.EqualTo(9));
Assert.That(queue, Is.Empty, "mapped frames must not be represented as queued CultNet documents");
}
[Test]
public void LiveTransportDropsExpiredHistoricalGenerationWithoutReinterpretingLatest()
{
var view = EntityLeaseDocument();
var publication = BodyPublication(view, view.Sequence);
publication.LivenessExpiresAtUnixMs = 999;
var method = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("IsPublicationLive", BindingFlags.NonPublic | BindingFlags.Static)!;
var live = (bool)method.Invoke(null, new object[]
{
publication,
DateTimeOffset.FromUnixTimeMilliseconds(1000)
})!;
Assert.That(live, Is.False);
Assert.That(publication.RecordKey, Is.EqualTo(
new CultMeshBodyPublicationHandle(view.Buffers[0].BufferId, view.ProducerEpoch, view.Sequence).RecordKey));
}
[Test]
public void LiveTransportHasNoSnapshotWireRegistryForBodyBytes()
{
var transport = typeof(EveUnityCultMeshLiveProviderTransport);
var wireRegistry = transport.GetField(
"WireDocumentTypes",
BindingFlags.NonPublic | BindingFlags.Static);
var fieldTypes = transport
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
.Select(field => field.FieldType)
.ToArray();
Assert.That(wireRegistry, Is.Null);
Assert.That(fieldTypes.Any(type => type == typeof(CultMeshCdnArtifactChunk)), Is.False);
Assert.That(fieldTypes.Any(type => type.Name == "CultMeshNetworkBodyDocument"), Is.False);
}
[Test]
public void LiveTransportBuildsImmutableNetworkFallbackForMappedAssetContent()
{
var manifest = CultMeshCdn.PackArtifact(
"provider/world/windows",
Enumerable.Range(0, 64).Select(value => (byte)value).ToArray()).Manifest;
var now = DateTimeOffset.UtcNow;
var method = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("NetworkArtifactDescriptor", BindingFlags.NonPublic | BindingFlags.Static)!;
var descriptor = (CultMeshBodyDescriptor)method.Invoke(
null,
new object[] { manifest, now, TimeSpan.FromMinutes(5) })!;
Assert.That(descriptor.BodyId, Is.EqualTo(manifest.ArtifactId));
Assert.That(descriptor.SchemaId, Is.EqualTo("gamecult.mesh.cdn-artifact.v1"));
Assert.That(descriptor.ByteSize, Is.EqualTo(manifest.SizeBytes));
Assert.That(descriptor.Capacity, Is.EqualTo(manifest.SizeBytes));
Assert.That(descriptor.AccessMode, Is.EqualTo(CultMeshBodyAccessMode.ReadOnly));
Assert.That(descriptor.Synchronization, Is.EqualTo(CultMeshBodySynchronization.ImmutableSequence));
Assert.That(descriptor.TransportKind, Is.EqualTo(CultMeshBodyTransportKind.Network));
Assert.That(descriptor.SemanticHash, Is.EqualTo(manifest.ContentHash));
Assert.That(descriptor.CapabilityToken, Is.EqualTo(
CultMeshCdnArtifactManifest.CreateRecordKey(manifest).Value));
}
[Test]
public void LiveTransportAuthorizesOnlyExplicitlyAdvertisedBodyProducers()
{
var advertisement = ProviderAdvertisement("aetheria", "aetheria-daemon-17", "aetheria.daemon");
var method = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("RequireAdvertisedBodyProducerIds", BindingFlags.NonPublic | BindingFlags.Static)!;
var producerIds = (IReadOnlyList<string>)method.Invoke(null, new object?[] { advertisement })!;
Assert.That(producerIds, Is.EqualTo(new[] { "aetheria.daemon" }));
Assert.That(producerIds, Does.Not.Contain(advertisement.ProviderId));
Assert.That(producerIds, Does.Not.Contain(advertisement.ServiceId));
var authorizes = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("IsAdvertisedBodyProducer", BindingFlags.NonPublic | BindingFlags.Static)!;
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "aetheria.daemon" }), Is.True);
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "aetheria" }), Is.False);
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "aetheria-daemon-17" }), Is.False);
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "impostor" }), Is.False);
}
[Test]
public void LiveTransportRejectsAdvertisementWithoutBodyProducerIdentity()
{
var advertisement = ProviderAdvertisement("aetheria", "aetheria-daemon-17");
var method = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("RequireAdvertisedBodyProducerIds", BindingFlags.NonPublic | BindingFlags.Static)!;
var error = Assert.Throws<TargetInvocationException>(() =>
method.Invoke(null, new object?[] { advertisement }));
Assert.That(error!.InnerException, Is.TypeOf<InvalidOperationException>());
StringAssert.Contains("does not advertise an authorized body producer", error.InnerException!.Message);
}
[Test]
public void ProviderAdvertisementRoundTripPreservesBodyProducerAuthority()
{
var advertisement = ProviderAdvertisement(
"aetheria",
"aetheria-daemon-17",
"aetheria.daemon",
"aetheria.asset-publisher");
var bytes = MessagePack.MessagePackSerializer.Serialize(advertisement);
var decoded = MessagePack.MessagePackSerializer.Deserialize<EveProviderAdvertisementDocument>(bytes);
Assert.That(decoded.ProviderId, Is.EqualTo("aetheria"));
Assert.That(decoded.ServiceId, Is.EqualTo("aetheria-daemon-17"));
Assert.That(decoded.AuthorizedBodyProducerIds,
Is.EqualTo(new[] { "aetheria.daemon", "aetheria.asset-publisher" }));
}
[Test]
public void EntitySoaViewReadsGenericSemanticColumnsFromInjectedLease()
{
var bytes = new byte[32];
Buffer.BlockCopy(new[] { 4f, 5f, 6f, 7f, 8f, 9f }, 0, bytes, 0, 24);
Buffer.BlockCopy(new[] { 41, 42 }, 0, bytes, 24, 8);
var document = new EveEntitySoaViewDocument
{
ProviderId = "provider",
ViewId = "pilot",
BodySchemaId = "test.entity.slab.v1",
LayoutVersion = 3,
ProducerEpoch = 5,
Sequence = 7,
Capacity = 2,
Buffers = new[] { new EveEntitySoaBuffer { BufferId = "hot", ByteLength = 32 } },
Columns = new[]
{
new EveEntitySoaColumn { ColumnId = "position", Semantic = "transform.position", BufferId = "hot", ScalarType = "float3", ElementStride = 12, ElementCount = 2 },
new EveEntitySoaColumn { ColumnId = "entity-index", Semantic = "entity.index", BufferId = "hot", ScalarType = "int32", ByteOffset = 24, ElementStride = 4, ElementCount = 2 }
}
};
var lease = new FakeBodyReadLease(document, bytes);
using var view = EveUnityEntitySoaView.Open(document, lease);
Assert.That(view.TryReadVector3("transform.position", 1, out var position), Is.True);
Assert.That(position, Is.EqualTo(new Vector3(7f, 8f, 9f)));
Assert.That(view.TryReadInt32("entity.index", 0, out var entityIndex), Is.True);
Assert.That(entityIndex, Is.EqualTo(41));
Assert.That(view.Generation, Is.EqualTo(7));
Assert.That(lease.Disposed, Is.False);
}
[TestCase("body")]
[TestCase("schema")]
[TestCase("layout")]
[TestCase("epoch")]
[TestCase("sequence")]
[TestCase("capacity")]
public void EntitySoaViewRejectsLeaseThatDisagreesWithLogicalLayout(string mismatch)
{
var document = EntityLeaseDocument();
var descriptor = FakeBodyReadLease.DescriptorFor(document, 4);
if (mismatch == "body") descriptor.BodyId = "wrong";
if (mismatch == "schema") descriptor.SchemaId = "wrong";
if (mismatch == "layout") descriptor.LayoutVersion++;
if (mismatch == "epoch") descriptor.ProducerEpoch++;
if (mismatch == "sequence") descriptor.Sequence++;
if (mismatch == "capacity") descriptor.Capacity++;
Assert.Throws<InvalidOperationException>(() => EveUnityEntitySoaView.Open(
document, new FakeBodyReadLease(descriptor, new byte[4])));
}
[Test]
public void EntitySoaPresenterPreservesProviderAuthoredIdentityFacts()
{
var bytes = new byte[16];
Buffer.BlockCopy(new[] { 12 }, 0, bytes, 0, 4);
Buffer.BlockCopy(new[] { 1f, 2f, 3f }, 0, bytes, 4, 12);
var document = EntityLeaseDocument();
document.Buffers[0].ByteLength = bytes.Length;
document.Columns = new[]
{
new EveEntitySoaColumn { ColumnId = "entity-index", Semantic = "entity.index", BufferId = "hot", ScalarType = "int32", ElementStride = 4, ElementCount = 1 },
new EveEntitySoaColumn { ColumnId = "position", Semantic = "transform.position", BufferId = "hot", ScalarType = "float3", ByteOffset = 4, ElementStride = 12, ElementCount = 1 }
};
var sink = new FakePlayableWorldSceneSink();
new EveUnityEntitySoaPresenter(sink).Apply(document, new FakeBodyReadLease(document, bytes));
var entity = sink.Upserts.Single().entity;
Assert.That(entity.EntityId, Is.EqualTo("entity:pilot"));
Assert.That(entity.Label, Is.EqualTo("Pilot"));
Assert.That(entity.EntityKind, Is.EqualTo("player"));
Assert.That(entity.Faction, Is.EqualTo("alliance"));
Assert.That(entity.Selectable, Is.True);
Assert.That(entity.Controllable, Is.True);
Assert.That(entity.AssetRef, Is.EqualTo("cultmesh://assets/pilot"));
}
[Test]
public void EntitySoaPresenterPublishesImmutableGenerationFactsAndRegistryLookups()
{
var root = new GameObject("presented-entities");
try
{
var bytes = new byte[48];
Buffer.BlockCopy(new[] { 12 }, 0, bytes, 0, 4);
Buffer.BlockCopy(new[] { 1f, 2f, 3f }, 0, bytes, 4, 12);
Buffer.BlockCopy(new[] { 0.5f }, 0, bytes, 16, 4);
Buffer.BlockCopy(new[] { 4f, 5f, 6f }, 0, bytes, 20, 12);
Buffer.BlockCopy(new[] { 7f }, 0, bytes, 32, 4);
Buffer.BlockCopy(new[] { 1.5f }, 0, bytes, 36, 4);
Buffer.BlockCopy(new[] { 9 }, 0, bytes, 40, 4);
Buffer.BlockCopy(new[] { 2 }, 0, bytes, 44, 4);
var document = EntityLeaseDocument();
document.Buffers[0].ByteLength = bytes.Length;
document.Columns = new[]
{
SoaColumn("entity.index", "int32", 0, 4),
SoaColumn("transform.position", "float3", 4, 12),
SoaColumn("transform.rotation.radians", "float32", 16, 4),
SoaColumn("transform.velocity", "float3", 20, 12),
SoaColumn("physics.body.radius", "float32", 32, 4),
SoaColumn("render.scale", "float32", 36, 4),
SoaColumn("render.group.id", "uint32", 40, 4),
SoaColumn("render.lod", "int32", 44, 4)
};
var sink = new EveUnityGameObjectPlayableWorldSceneSink(
root.transform, new FixedGameObjectAssetProvider(null));
new EveUnityEntitySoaPresenter(sink).Apply(document, new FakeBodyReadLease(document, bytes));
Assert.That(sink.CurrentGeneration, Is.Not.Null);
Assert.That(sink.CurrentGeneration!.ProducerEpoch, Is.EqualTo(document.ProducerEpoch));
Assert.That(sink.CurrentGeneration.Sequence, Is.EqualTo(document.Sequence));
Assert.That(sink.TryGetByEntityId("entity:pilot", out var byId), Is.True);
Assert.That(sink.TryGetBySourceIndex(12, out var byIndex), Is.True);
Assert.That(byIndex, Is.SameAs(byId));
Assert.That(byId.Entity.Position, Is.EqualTo(new Vector3(1f, 2f, 3f)));
Assert.That(byId.Entity.Velocity, Is.EqualTo(new Vector3(4f, 5f, 6f)));
Assert.That(byId.Entity.Radius, Is.EqualTo(7f));
Assert.That(byId.Entity.Scale, Is.EqualTo(1.5f));
Assert.That(byId.Entity.RenderGroupId, Is.EqualTo(9));
Assert.That(byId.Entity.Lod, Is.EqualTo(2));
Assert.That(byId.Entity.Selectable, Is.True);
Assert.That(byId.Entity.Controllable, Is.True);
Assert.That(byId.Entity.AssetRef, Is.EqualTo("cultmesh://assets/pilot"));
}
finally
{
UnityEngine.Object.DestroyImmediate(root);
}
}
[Test]
public void GameObjectGenerationRegistrySwapsOnlyAfterCompleteApplication()
{
var root = new GameObject("atomic-generation");
EveUnityGameObjectPlayableWorldSceneSink? sink = null;
long observedSequenceDuringApply = -1;
try
{
var provider = new ObservingGameObjectAssetProvider(() =>
observedSequenceDuringApply = sink?.CurrentGeneration?.Sequence ?? -1);
sink = new EveUnityGameObjectPlayableWorldSceneSink(root.transform, provider);
sink.ApplyGeneration(new EveUnityPresentedEntityGeneration(
"entities", 3, 10,
new[] { PresentedEntity(1, "first", "asset:first") }));
Assert.That(sink.TryGetByEntityId("first", out var firstGeneration), Is.True);
var firstTransform = firstGeneration.Transform;
observedSequenceDuringApply = -1;
sink.ApplyGeneration(new EveUnityPresentedEntityGeneration(
"entities", 3, 11,
new[]
{
PresentedEntity(1, "first", "asset:first"),
PresentedEntity(2, "second", "asset:second")
}));
Assert.That(observedSequenceDuringApply, Is.EqualTo(10));
Assert.That(sink.CurrentGeneration!.Sequence, Is.EqualTo(11));
Assert.That(sink.TryGetByEntityId("first", out var retained), Is.True);
Assert.That(retained.Transform, Is.SameAs(firstTransform));
Assert.That(sink.TryGetByEntityId("second", out var second), Is.True);
Assert.That(sink.TryGetBySourceIndex(2, out var byIndex), Is.True);
Assert.That(byIndex, Is.SameAs(second));
Assert.That(sink.ActiveEntityCount, Is.EqualTo(2));
}
finally
{
UnityEngine.Object.DestroyImmediate(root);
}
}
[Test]
public void LowerCarriesProviderAdvertisedWorldBoundary()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(Document("aetheria.daemon.game"), Advertisement("aetheria.daemon.game"));
Assert.That(projection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(projection.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(projection.ProjectionKind, Is.EqualTo("provider-authored-world-surface"));
Assert.That(projection.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(projection.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
Assert.That(projection.Ownership, Is.EqualTo("provider-owns-world-state-assets-command-acceptance-and-receipts"));
Assert.That(projection.Root.Id, Is.EqualTo("aetheria.daemon.game.root"));
Assert.That(projection.Root.SceneObjectKind, Is.EqualTo("scene-node"));
}
[Test]
public void LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(Document("aetheria.daemon.game"), Advertisement("aetheria.daemon.game"));
Assert.That(projection.Root.Children.Count, Is.EqualTo(3));
Assert.That(projection.Root.Children[0].SceneObjectKind, Is.EqualTo("world-projection-node"));
Assert.That(projection.Root.Children[0].Props["binding"], Is.EqualTo("cultmesh://aetheria/world/entities"));
Assert.That(projection.Root.Children[0].StateBindingCount, Is.EqualTo(1));
Assert.That(projection.Root.Children[1].SceneObjectKind, Is.EqualTo("command-control"));
Assert.That(projection.Root.Children[1].Props["command"], Is.EqualTo("aetheria.daemon.focus"));
var nornNode = projection.Root.Children[2];
Assert.That(nornNode.SceneObjectKind, Is.EqualTo("norn-graph-scene-projection"));
Assert.That(nornNode.EmbeddedDocumentCount, Is.EqualTo(1));
Assert.That(nornNode.EmbeddedDocuments.Count, Is.EqualTo(1));
Assert.That(nornNode.EmbeddedDocuments[0].SlotId, Is.EqualTo("norn.map"));
Assert.That(nornNode.EmbeddedDocuments[0].DocumentId, Is.EqualTo("cultmesh://aetheria/norn/map"));
Assert.That(nornNode.EmbeddedDocuments[0].SchemaId, Is.EqualTo("gamecult.eve.surface.v1"));
Assert.That(nornNode.EmbeddedDocuments[0].PresentationKind, Is.EqualTo("scene-overlay"));
Assert.That(nornNode.PluginProjection, Is.Not.Null);
Assert.That(nornNode.PluginProjection!.PluginId, Is.EqualTo("norn.graph"));
Assert.That(nornNode.PluginProjection.ProjectionKind, Is.EqualTo("norn-scene-embedded-graph-shell"));
Assert.That(nornNode.PluginProjection.AbiSchema, Is.EqualTo("gamecult.eve.plugin_abi.v1"));
Assert.That(nornNode.PluginProjection.CommandBoundary, Is.EqualTo("sidecar-advertised-plugin-abi"));
Assert.That(nornNode.PluginProjection.Capabilities, Does.Contain("embed.norn"));
Assert.That(nornNode.PluginProjection.Command, Is.EqualTo("graph.focus"));
Assert.That(nornNode.PluginProjection.DocumentId, Is.EqualTo("cultmesh://aetheria/norn/map"));
Assert.That(nornNode.PluginProjection.SemanticOwner, Is.EqualTo("Norn"));
}
[Test]
public void LowerExtractsPlayableArpgWorldFromGenericScene3dSurface()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
Assert.That(projection.Root.Children[0].SceneObjectKind, Is.EqualTo("playable-world-root"));
Assert.That(projection.Root.Children[0].Children[0].SceneObjectKind, Is.EqualTo("playable-world-entity"));
Assert.That(projection.Root.Children[0].Children.Any(child => child.SceneObjectKind == "world-field-3d"), Is.True);
Assert.That(projection.PlayableWorld, Is.Not.Null);
Assert.That(projection.PlayableWorld!.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
Assert.That(projection.PlayableWorld.StatePointerId, Is.EqualTo("cultmesh://aetheria/run/current"));
Assert.That(projection.PlayableWorld.EntityViewPointerId, Is.EqualTo("cultmesh://aetheria/world/entities.soa"));
Assert.That(projection.PlayableWorld.EntityViewSchema, Is.EqualTo(EveEntitySoaViewDocument.SchemaId));
Assert.That(projection.PlayableWorld.EntityBodyId, Is.EqualTo("eve:entity-soa:aetheria.daemon:pilot"));
Assert.That(projection.PlayableWorld.ZoneRenderPointerId, Is.EqualTo("cultmesh://aetheria/world/zone-render"));
Assert.That(projection.PlayableWorld.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(projection.PlayableWorld.AssetProviderId, Is.EqualTo("aetheria.assets"));
Assert.That(projection.PlayableWorld.AssetVerseId, Is.EqualTo("aetheria.mods"));
Assert.That(projection.PlayableWorld.AssetAuthorityRuntimeId, Is.EqualTo("mod-authority"));
Assert.That(projection.PlayableWorld.AssetRendezvousEndpoints, Is.EqualTo(new[] { "wss://odin-a/cultmesh", "wss://odin-b/cultmesh" }));
Assert.That(projection.PlayableWorld.InputProfile, Is.EqualTo("arpg-third-person"));
Assert.That(projection.PlayableWorld.CameraRig, Is.EqualTo("planar.top-down-follow.v1"));
Assert.That(projection.PlayableWorld.CameraLookAt, Is.Empty);
Assert.That(projection.PlayableWorld.CameraTargetEntityId, Is.EqualTo("player-vanguard"));
Assert.That(projection.PlayableWorld.CameraDistance, Is.EqualTo(150f));
Assert.That(projection.PlayableWorld.CameraVerticalFieldOfViewDegrees, Is.EqualTo(60f));
Assert.That(projection.PlayableWorld.CameraTargetScreenX, Is.EqualTo(0.9f));
Assert.That(projection.PlayableWorld.CameraTargetScreenY, Is.EqualTo(0.55f));
Assert.That(projection.PlayableWorld.CameraPositionDamping, Is.EqualTo(5f));
Assert.That(projection.PlayableWorld.CameraNearClipPlane, Is.EqualTo(0.3f));
Assert.That(projection.PlayableWorld.CameraFarClipPlane, Is.EqualTo(4096f));
Assert.That(projection.PlayableWorld.AmbientLightR, Is.EqualTo(0.2f));
Assert.That(projection.PlayableWorld.AmbientLightG, Is.EqualTo(0.2f));
Assert.That(projection.PlayableWorld.AmbientLightB, Is.EqualTo(0.2f));
Assert.That(projection.PlayableWorld.AmbientLightIntensity, Is.EqualTo(1.46f));
Assert.That(projection.PlayableWorld.SkyboxAssetRef, Is.Empty);
Assert.That(projection.PlayableWorld.ReflectionAssetRef, Is.Empty);
Assert.That(projection.PlayableWorld.PostProcessProfileAssetRef, Is.Empty);
Assert.That(projection.PlayableWorld.ReflectionIntensity, Is.EqualTo(1f));
Assert.That(projection.PlayableWorld.ExcludedRenderChannels, Is.EqualTo(new[] { "map" }));
Assert.That(projection.PlayableWorld.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(projection.PlayableWorld.MovementCommand, Is.EqualTo("aetheria.daemon.move_intent"));
Assert.That(projection.PlayableWorld.FocusCommand, Is.EqualTo("aetheria.daemon.focus"));
Assert.That(projection.PlayableWorld.TargetCommand, Is.EqualTo("aetheria.daemon.target"));
Assert.That(projection.PlayableWorld.EntityCount, Is.EqualTo(3));
Assert.That(projection.PlayableWorld.FieldVolumes.Count, Is.EqualTo(1));
Assert.That(projection.PlayableWorld.FieldVolumes[0].DocumentRef, Is.EqualTo("cultmesh://aetheria/world/fog-splats"));
Assert.That(projection.PlayableWorld.FieldVolumes[0].MaterialAssetRef, Is.EqualTo("shader.environment.gravity-fog"));
Assert.That(projection.PlayableWorld.FieldVolumes[0].RenderChannel, Is.EqualTo("world.transparent"));
Assert.That(projection.PlayableWorld.FieldParticles.Count, Is.EqualTo(1));
Assert.That(projection.PlayableWorld.FieldParticles[0].DocumentRef,
Is.EqualTo("cultmesh://aetheria/world/fog-splats"));
Assert.That(projection.PlayableWorld.FieldParticles[0].ComputeProgramAssetRef,
Is.EqualTo("compute.environment.stardust"));
Assert.That(projection.PlayableWorld.FieldParticles[0].MaterialAssetRef,
Is.EqualTo("material.environment.stardust"));
var player = FindEntity(projection.PlayableWorld, "player-vanguard");
Assert.That(player.EntityKind, Is.EqualTo("player"));
Assert.That(player.AssetRef, Is.EqualTo("cultmesh://aetheria/assets/map/entity/player"));
Assert.That(player.PositionX, Is.EqualTo(0f));
Assert.That(player.PositionY, Is.EqualTo(0f));
Assert.That(player.PositionZ, Is.EqualTo(0f));
Assert.That(player.Controllable, Is.True);
Assert.That(player.MoveCommand, Is.EqualTo("aetheria.daemon.move_intent"));
Assert.That(player.Props["presentationState"], Is.EqualTo("active"));
var raider = FindEntity(projection.PlayableWorld, "raider-scout");
Assert.That(raider.EntityKind, Is.EqualTo("enemy"));
Assert.That(raider.PositionX, Is.EqualTo(9f));
Assert.That(raider.PositionZ, Is.EqualTo(14f));
Assert.That(raider.TargetCommand, Is.EqualTo("aetheria.daemon.target"));
}
[Test]
public void SemanticEntityPresentationConsumesProviderPulseState()
{
var instance = GameObject.CreatePrimitive(PrimitiveType.Cube);
try
{
var presentation = instance.AddComponent<EveUnitySemanticEntityPresentation>();
presentation.Apply(new Dictionary<string, string>(StringComparer.Ordinal)
{
["presentationState"] = "triggered",
["activePulseSeconds"] = "1",
["triggeredPulseSeconds"] = "0.25",
["activeEmission"] = "100",
["triggeredEmission"] = "1000"
});
presentation.ApplyAt(0.125f);
Assert.That(presentation.PresentationState, Is.EqualTo("triggered"));
Assert.That(instance.GetComponent<Renderer>().HasPropertyBlock(), Is.True);
}
finally
{
UnityEngine.Object.DestroyImmediate(instance);
}
}
[Test]
public void GenericClientSessionConsumesAetheriaPlayableWorldSnapshotWithoutAetheriaTypes()
{
var session = new EveUnitySceneClientSession();
var projection = session.Connect(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
42));
Assert.That(session.ActiveSourcePointer, Is.EqualTo("cultmesh://aetheria/eve/surfaces/aetheria.daemon.game"));
Assert.That(session.ActiveProjection, Is.SameAs(projection));
Assert.That(projection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(projection.PlayableWorld, Is.Not.Null);
Assert.That(projection.PlayableWorld!.InputProfile, Is.EqualTo("arpg-third-person"));
Assert.That(projection.PlayableWorld.EntityCount, Is.EqualTo(3));
var moveIntent = session.CreateMoveIntent(
"player-vanguard",
12f,
0f,
8f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(moveIntent.Schema, Is.EqualTo(EveSurfaceCommandRequest.SchemaId));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveIntent.ClientId, Is.EqualTo("unity-scene"));
Assert.That(moveIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
var focusIntent = session.CreateFocusIntent("anchor-station");
Assert.That(focusIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(focusIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
var moveVectorIntent = session.CreateMoveVectorIntent(
"player-vanguard",
0.25f,
0.75f,
0.8f,
DateTimeOffset.Parse("2026-07-09T00:00:01Z"));
Assert.That(moveVectorIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveVectorIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveVectorIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveVectorIntent.Payload.GetString("commandId"), Is.EqualTo("aetheria.daemon.move_intent"));
Assert.That(moveVectorIntent.Payload.GetString("entityId"), Is.EqualTo("player-vanguard"));
Assert.That(moveVectorIntent.Payload.GetString("directionX"), Is.EqualTo("0.25"));
Assert.That(moveVectorIntent.Payload.GetString("directionY"), Is.EqualTo("0.75"));
Assert.That(moveVectorIntent.Payload.GetString("scalarValue"), Is.EqualTo("0.8"));
var lookIntent = session.CreateLookDirectionIntent(
"player-vanguard",
0.6f,
0f,
0.8f,
DateTimeOffset.Parse("2026-07-09T00:00:02Z"));
Assert.That(lookIntent.Payload.GetString("commandId"), Is.EqualTo("aetheria.daemon.look_intent"));
Assert.That(lookIntent.Payload.GetString("entityId"), Is.EqualTo("player-vanguard"));
Assert.That(lookIntent.Payload.GetString("directionX"), Is.EqualTo("0.6"));
Assert.That(lookIntent.Payload.GetString("directionY"), Is.EqualTo("0"));
Assert.That(lookIntent.Payload.GetString("directionZ"), Is.EqualTo("0.8"));
}
[Test]
public void GenericProviderConnectionAppliesLiveSnapshotsAndSubmitsCommandsThroughSink()
{
var source = new FakeProviderSurfaceSource(
"aetheria",
"aetheria.daemon.game",
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
var sink = new FakeCommandSink("cultmesh-command-sink");
using var connection = new EveUnitySceneProviderConnection(source, sink);
var projectionUpdates = 0;
connection.ProjectionUpdated += _ => projectionUpdates++;
var initialProjection = connection.Connect();
Assert.That(initialProjection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(connection.ProviderId, Is.EqualTo("aetheria"));
Assert.That(connection.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(connection.SourcePointer, Is.EqualTo("cultmesh://aetheria/eve/surfaces/aetheria.daemon.game"));
Assert.That(connection.ActiveVersion, Is.EqualTo(1));
Assert.That(projectionUpdates, Is.EqualTo(1));
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(connection.ActiveVersion, Is.EqualTo(2));
Assert.That(connection.ActiveProjection, Is.Not.Null);
Assert.That(connection.ActiveProjection!.PlayableWorld, Is.Not.Null);
Assert.That(connection.ActiveProjection.PlayableWorld!.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(projectionUpdates, Is.EqualTo(2));
var moveIntent = connection.SubmitMoveIntent(
"player-vanguard",
20f,
0f,
15f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(sink.SinkKind, Is.EqualTo("cultmesh-command-sink"));
Assert.That(sink.Submitted.Count, Is.EqualTo(1));
Assert.That(sink.Submitted[0], Is.SameAs(moveIntent));
Assert.That(moveIntent.Schema, Is.EqualTo(EveSurfaceCommandRequest.SchemaId));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
connection.Disconnect();
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
3));
Assert.That(connection.ActiveVersion, Is.EqualTo(2));
Assert.That(projectionUpdates, Is.EqualTo(2));
}
[Test]
public void ProviderSurfaceDocumentSourceFeedsPlayableClientWithoutAetheriaTypes()
{
var documentSource = new FakeProviderSurfaceDocumentSource(new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
using var surfaceSource = new EveUnitySceneProviderSurfaceDocumentSource(documentSource);
var commandSink = new FakeCommandSink("cultmesh-command-sink");
var sceneSink = new FakePlayableWorldSceneSink();
using var connection = new EveUnitySceneProviderConnection(surfaceSource, commandSink);
using var client = new EveUnityPlayableWorldLiveClient(
connection,
new EveUnityPlayableWorldPresenter(sceneSink, new EveUnityAssetRefResolver()));
var initialPresentation = client.Connect();
Assert.That(surfaceSource.ProviderId, Is.EqualTo("aetheria"));
Assert.That(surfaceSource.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(surfaceSource.SourcePointer, Is.EqualTo("cultmesh://aetheria/eve/surfaces/aetheria.daemon.game"));
Assert.That(initialPresentation.ActiveEntities, Is.EqualTo(3));
Assert.That(client.ActiveWorld, Is.Not.Null);
Assert.That(client.ActiveWorld!.StatePointerId, Is.EqualTo("cultmesh://aetheria/run/current"));
Assert.That(client.ActiveWorld.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
documentSource.Publish(new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(includeRaider: false, playerPosition: "11,0,5"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(client.LastPresentation, Is.Not.Null);
Assert.That(client.LastPresentation!.ActiveEntities, Is.EqualTo(2));
Assert.That(sceneSink.RemovedEntityIds, Does.Contain("raider-scout"));
Assert.That(sceneSink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sceneSink.Upserts[3].entity.PositionX, Is.EqualTo(11f));
Assert.That(sceneSink.Upserts[3].entity.PositionZ, Is.EqualTo(5f));
var moveIntent = client.SubmitMoveIntent("player-vanguard", 12f, 0f, 8f);
Assert.That(commandSink.Submitted.Count, Is.EqualTo(1));
Assert.That(commandSink.Submitted[0], Is.SameAs(moveIntent));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
}
[Test]
public void PlayableWorldRuntimeComposesProviderDocumentsAssetsReceiptsAndSceneSink()
{
var surfaceDocuments = new FakeProviderSurfaceDocumentSource(new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
var assetDocuments = new FakeAssetManifestDocumentSource(new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestDocumentEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"Resources/Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard"),
new EveUnityPlayableWorldAssetManifestDocumentEntry(
"cultmesh://aetheria/assets/map/entity/ship",
"enemy",
"Resources/Aetheria/Entities/Raider.prefab",
"aetheria.raider")
},
"aetheria"));
var commandSink = new FakeCommandSink("cultmesh-command-sink");
var receiptSource = new FakeCommandReceiptSource();
var sceneSink = new FakePlayableWorldSceneSink();
using var runtime = new EveUnityPlayableWorldRuntime(
surfaceDocuments,
commandSink,
sceneSink,
assetDocuments,
receiptSource);
var initialPresentation = runtime.Connect();
Assert.That(initialPresentation.ActiveEntities, Is.EqualTo(3));
Assert.That(runtime.ActiveWorld, Is.Not.Null);
Assert.That(runtime.ActiveWorld!.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(runtime.AssetManifests.GetForWorld(runtime.ActiveWorld), Is.Not.Null);
Assert.That(sceneSink.Upserts.Count, Is.EqualTo(3));
var moveIntent = runtime.SubmitMoveIntent(
"player-vanguard",
14f,
0f,
9f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(commandSink.Submitted.Count, Is.EqualTo(1));
Assert.That(commandSink.Submitted[0], Is.SameAs(moveIntent));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
surfaceDocuments.Publish(new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(includeRaider: false, playerPosition: "14,0,9"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(runtime.ActiveVersion, Is.EqualTo(2));
Assert.That(runtime.LastPresentation, Is.Not.Null);
Assert.That(runtime.LastPresentation!.ActiveEntities, Is.EqualTo(2));
Assert.That(sceneSink.RemovedEntityIds, Does.Contain("raider-scout"));
Assert.That(sceneSink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sceneSink.Upserts[3].entity.PositionX, Is.EqualTo(14f));
Assert.That(sceneSink.Upserts[3].entity.PositionZ, Is.EqualTo(9f));
receiptSource.Publish(new EveUnitySceneCommandReceipt(
"aetheria.daemon.move_intent.accepted",
"aetheria.daemon.move_intent",
"aetheria.daemon.move_intent",
"accepted",
"Aetheria",
"provider-owned-daemon",
navigation: new EveUnitySceneNavigationTarget(
"gamecult.aetheria",
"aetheria",
"aetheria.pilot",
"interactive-world")));
Assert.That(runtime.LastReceipt, Is.Not.Null);
Assert.That(runtime.LastReceipt!.IsProviderOwned, Is.True);
Assert.That(runtime.LastReceipt.ShouldRefreshProviderSurface, Is.True);
Assert.That(runtime.LastReceipt.Navigation, Is.Not.Null);
Assert.That(runtime.LastReceipt.Navigation!.VerseId, Is.EqualTo("gamecult.aetheria"));
Assert.That(runtime.LastReceipt.Navigation.SurfaceId, Is.EqualTo("aetheria.pilot"));
}
[Test]
public void LiveProviderBridgeFeedsPlayableWorldRuntimeThroughTransportPorts()
{
var transport = new FakeLiveProviderTransport(
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",
"Resources/Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard")
},
"aetheria"));
using var bridge = new EveUnitySceneLiveProviderBridge(transport);
var sceneSink = new FakePlayableWorldSceneSink();
using var runtime = new EveUnityPlayableWorldRuntime(
bridge,
bridge,
sceneSink,
bridge,
bridge);
var initialPresentation = runtime.Connect();
Assert.That(transport.ConnectCount, Is.EqualTo(1));
Assert.That(bridge.TransportKind, Is.EqualTo("cultmesh-cultnet-provider-transport"));
Assert.That(bridge.SurfacePointer, Is.EqualTo("cultmesh://aetheria/eve/surfaces/aetheria.daemon.game"));
Assert.That(bridge.AssetManifestPointer, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(initialPresentation.ActiveEntities, Is.EqualTo(3));
Assert.That(runtime.ActiveWorld, Is.Not.Null);
Assert.That(runtime.AssetManifests.GetForWorld(runtime.ActiveWorld!), Is.Not.Null);
var moveIntent = runtime.SubmitMoveVectorIntent(
"player-vanguard",
0.5f,
1f,
0.75f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(transport.Submitted.Count, Is.EqualTo(1));
Assert.That(transport.Submitted[0], Is.SameAs(moveIntent));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
transport.PublishSurface(new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(includeRaider: false, playerPosition: "6,0,2"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(runtime.ActiveVersion, Is.EqualTo(2));
Assert.That(runtime.LastPresentation, Is.Not.Null);
Assert.That(runtime.LastPresentation!.ActiveEntities, Is.EqualTo(2));
Assert.That(sceneSink.RemovedEntityIds, Does.Contain("raider-scout"));
transport.PublishReceipt(new EveUnitySceneCommandReceipt(
"aetheria.daemon.move_intent.accepted",
"aetheria.daemon.commands",
"aetheria.daemon.move_intent",
"accepted",
"Aetheria",
"provider-owned-daemon"));
Assert.That(runtime.LastReceipt, Is.Not.Null);
Assert.That(runtime.LastReceipt!.IsProviderOwned, Is.True);
Assert.That(transport.RefreshCount, Is.EqualTo(0));
}
[Test]
public void LiveProviderBridgeRollsBackSubscriptionsWhenTransportConnectFails()
{
var transport = new FakeLiveProviderTransport(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"))
{
FailNextConnect = true
};
using var bridge = new EveUnitySceneLiveProviderBridge(transport);
Assert.Throws<IOException>(() => bridge.Connect());
Assert.That(transport.SurfaceSubscriberCount, Is.Zero);
Assert.That(transport.AssetManifestSubscriberCount, Is.Zero);
Assert.That(transport.ReceiptSubscriberCount, Is.Zero);
bridge.Connect();
Assert.That(transport.ConnectCount, Is.EqualTo(2));
Assert.That(transport.SurfaceSubscriberCount, Is.EqualTo(1));
Assert.That(transport.AssetManifestSubscriberCount, Is.EqualTo(1));
Assert.That(transport.ReceiptSubscriberCount, Is.EqualTo(1));
}
[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<FakePlayableWorldProviderComponent>();
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<EveUnityPlayableWorldClientHost>();
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 PlayableWorldClientBootstrapWiresGenericUnityClientFromProviderInterfaces()
{
var hostObject = new GameObject("generic-eve-client");
hostObject.SetActive(false);
try
{
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"));
var bootstrap = hostObject.AddComponent<EveUnityPlayableWorldClientBootstrap>();
bootstrap.ConfigureProvider(provider);
bootstrap.Mount();
Assert.That(bootstrap.Host, Is.Not.Null);
Assert.That(bootstrap.Host!.ActiveWorld, Is.Not.Null);
Assert.That(bootstrap.Host.ActiveWorld!.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(bootstrap.LastPresentation, Is.Not.Null);
Assert.That(bootstrap.LastPresentation!.ActiveEntities, Is.EqualTo(3));
Assert.That(bootstrap.SceneRoot, Is.Not.Null);
Assert.That(bootstrap.SceneRoot!.childCount, Is.EqualTo(3));
Assert.That(hostObject.GetComponent<EveUnityPlayableWorldInputDriver>(), Is.Not.Null);
Assert.That(hostObject.GetComponent<EveUnityPlayableWorldCameraRig>(), Is.Not.Null);
Assert.That(hostObject.GetComponent<EveUnityUiToolkitOverlay>(), Is.Not.Null);
Assert.That(hostObject.GetComponent<EveUnityUiToolkitOverlay>().Root, Is.Not.Null);
Assert.That(hostObject.GetComponent<UIDocument>().panelSettings.themeStyleSheet, Is.Not.Null);
Assert.That(bootstrap.CameraTransform, Is.Not.Null);
Assert.That(provider.RefreshCount, Is.EqualTo(1));
var input = hostObject.GetComponent<EveUnityPlayableWorldInputDriver>();
var moveVector = input.SubmitMoveVectorInput(0f, 1f);
Assert.That(moveVector, Is.Not.Null);
Assert.That(provider.Submitted.Count, Is.EqualTo(1));
Assert.That(provider.Submitted[0], Is.SameAs(moveVector));
Assert.That(moveVector!.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveVector.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveVector.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveVector.Payload.GetString("commandId"), Is.EqualTo("aetheria.daemon.move_intent"));
Assert.That(moveVector.Payload.GetString("entityId"), Is.EqualTo("player-vanguard"));
}
finally
{
UnityEngine.Object.DestroyImmediate(hostObject);
}
}
[UnityTest]
public IEnumerator PlayableWorldClientBootstrapFollowsProviderOwnedReceiptNavigation()
{
var hostObject = new GameObject("generic-eve-navigation-client");
try
{
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"));
var bootstrap = hostObject.AddComponent<EveUnityPlayableWorldClientBootstrap>();
bootstrap.ConfigureProvider(provider);
bootstrap.Mount();
var originalHost = bootstrap.Host;
var originalRoot = bootstrap.SceneRoot!;
provider.PreviousPresentationActive = () => originalRoot.gameObject.activeSelf;
provider.PublishReceipt(new EveUnitySceneCommandReceipt(
"receipt:launch",
"aetheria.hangar.launch",
"launch",
"accepted",
"Aetheria",
"commander-daemon",
navigation: new EveUnitySceneNavigationTarget(
"gamecult.aetheria",
"aetheria.daemon",
"aetheria.pilot",
"interactive-world",
new[] { "cultnet+tcp://odin.example:3076" })));
yield return null;
yield return null;
Assert.That(provider.LastNavigation, Is.Not.Null);
Assert.That(provider.LastNavigation!.VerseId, Is.EqualTo("gamecult.aetheria"));
Assert.That(provider.LastNavigation.RendezvousEndpoints, Is.EqualTo(new[] { "cultnet+tcp://odin.example:3076" }));
Assert.That(bootstrap.Host, Is.Not.SameAs(originalHost));
Assert.That(bootstrap.Host!.ConnectionEpoch, Is.EqualTo(1));
Assert.That(provider.CommitNavigationCount, Is.EqualTo(1));
Assert.That(provider.FinalizeNavigationCount, Is.EqualTo(1));
Assert.That(provider.RollbackNavigationCount, Is.Zero);
Assert.That(provider.PreviousPresentationActiveDuringCommit, Is.False);
provider.PublishReceipt(new EveUnitySceneCommandReceipt(
"receipt:launch-again",
"aetheria.hangar.launch",
"launch",
"accepted",
"Aetheria",
"commander-daemon",
navigation: new EveUnitySceneNavigationTarget(
"gamecult.aetheria",
"aetheria.daemon",
"aetheria.pilot",
"interactive-world",
new[] { "cultnet+tcp://odin.example:3076" })));
yield return null;
yield return null;
Assert.That(provider.CommitNavigationCount, Is.EqualTo(2));
Assert.That(provider.FinalizeNavigationCount, Is.EqualTo(2));
Assert.That(provider.ActiveConnectionLeases, Is.EqualTo(1),
"disposing superseded presentations must leave the committed Verse connected");
Assert.That(hostObject.transform.Cast<Transform>()
.Count(child => child.name == "Eve Unity Candidate Presentation"), Is.EqualTo(1));
}
finally
{
UnityEngine.Object.DestroyImmediate(hostObject);
}
}
[UnityTest]
public IEnumerator PlayableWorldClientBootstrapKeepsMountedPresentationAfterNavigationMountFailure()
{
var hostObject = new GameObject("generic-eve-navigation-rollback-client");
try
{
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"));
var bootstrap = hostObject.AddComponent<EveUnityPlayableWorldClientBootstrap>();
bootstrap.ConfigureProvider(provider);
bootstrap.Mount();
var originalHost = bootstrap.Host;
var originalRuntime = originalHost!.Runtime;
var originalRoot = bootstrap.SceneRoot;
var originalPresentation = bootstrap.LastPresentation;
provider.FailNextNavigationMount = true;
LogAssert.Expect(LogType.Error, new Regex("Eve provider navigation failed; the current surface remains mounted"));
provider.PublishReceipt(new EveUnitySceneCommandReceipt(
"receipt:launch-failure",
"aetheria.hangar.launch",
"launch",
"accepted",
"Aetheria",
"commander-daemon",
navigation: new EveUnitySceneNavigationTarget(
"gamecult.aetheria",
"broken-provider",
"aetheria.pilot",
"interactive-world",
new[] { "cultnet+tcp://odin.example:3999" })));
yield return null;
yield return null;
Assert.That(provider.CommitNavigationCount, Is.Zero);
Assert.That(provider.RollbackNavigationCount, Is.EqualTo(1));
Assert.That(bootstrap.LastNavigationFailure, Is.Not.Null);
Assert.That(bootstrap.Host, Is.SameAs(originalHost));
Assert.That(bootstrap.Host!.Runtime, Is.SameAs(originalRuntime));
Assert.That(bootstrap.SceneRoot, Is.SameAs(originalRoot));
Assert.That(bootstrap.LastPresentation, Is.SameAs(originalPresentation));
Assert.That(bootstrap.Host!.Runtime, Is.Not.Null);
Assert.That(bootstrap.Host.ActiveWorld, Is.Not.Null);
Assert.That(bootstrap.Host.ActiveWorld!.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(bootstrap.Host.ConnectionEpoch, Is.EqualTo(1));
}
finally
{
UnityEngine.Object.DestroyImmediate(hostObject);
}
}
[UnityTest]
public IEnumerator PlayableWorldClientBootstrapRestoresProviderRouteBeforeReactivatingPreviousView()
{
var hostObject = new GameObject("generic-eve-navigation-order-client");
try
{
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"));
var bootstrap = hostObject.AddComponent<EveUnityPlayableWorldClientBootstrap>();
bootstrap.ConfigureProvider(provider);
bootstrap.Mount();
var originalRoot = bootstrap.SceneRoot!;
provider.PreviousPresentationActive = () => originalRoot.gameObject.activeSelf;
provider.FailNextNavigationFinalize = true;
LogAssert.Expect(LogType.Error, new Regex("Eve provider navigation failed; the current surface remains mounted"));
provider.PublishReceipt(new EveUnitySceneCommandReceipt(
"receipt:launch-finalize-failure",
"aetheria.hangar.launch",
"launch",
"accepted",
"Aetheria",
"commander-daemon",
navigation: new EveUnitySceneNavigationTarget(
"gamecult.aetheria",
"candidate-provider",
"aetheria.pilot",
"interactive-world",
new[] { "cultnet+tcp://odin.example:3999" })));
yield return null;
yield return null;
Assert.That(provider.CommitNavigationCount, Is.EqualTo(1));
Assert.That(provider.RollbackNavigationCount, Is.EqualTo(1));
Assert.That(provider.ActiveRoute, Is.EqualTo("original"));
Assert.That(provider.PreviousPresentationActiveDuringRollback, Is.False);
Assert.That(originalRoot.gameObject.activeSelf, Is.True);
Assert.That(bootstrap.Host!.Runtime, Is.Not.Null);
}
finally
{
UnityEngine.Object.DestroyImmediate(hostObject);
}
}
[Test]
public void PlayableWorldClientHostSubmitsProviderOwnedMoveVector()
{
var rootObject = new GameObject("generic-eve-world-root");
var hostObject = new GameObject("generic-eve-client");
hostObject.SetActive(false);
try
{
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"));
var host = hostObject.AddComponent<EveUnityPlayableWorldClientHost>();
host.Configure(rootObject.transform, provider, provider, provider, provider);
host.Connect();
var moveVector = host.SubmitMoveVectorIntent("player-vanguard", 0.6f, -0.8f, 1f);
Assert.That(provider.Submitted.Count, Is.EqualTo(1));
Assert.That(provider.Submitted[0], Is.SameAs(moveVector));
Assert.That(moveVector.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveVector.Payload.GetString("commandId"), Is.EqualTo("aetheria.daemon.move_intent"));
Assert.That(moveVector.Payload.GetString("entityId"), Is.EqualTo("player-vanguard"));
Assert.That(moveVector.Payload.GetString("directionX"), Is.EqualTo("0.6"));
Assert.That(moveVector.Payload.GetString("directionY"), Is.EqualTo("-0.8"));
Assert.That(moveVector.Payload.GetString("scalarValue"), Is.EqualTo("1"));
}
finally
{
UnityEngine.Object.DestroyImmediate(hostObject);
UnityEngine.Object.DestroyImmediate(rootObject);
}
}
[Test]
public void PlayableWorldInputDriverBuildsCameraRelativeMoveVectorWithoutProviderTypes()
{
var rootObject = new GameObject("generic-eve-world-root");
var hostObject = new GameObject("generic-eve-client");
var cameraObject = new GameObject("generic-eve-camera");
hostObject.SetActive(false);
try
{
cameraObject.transform.rotation = Quaternion.Euler(0f, 90f, 0f);
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"));
var host = hostObject.AddComponent<EveUnityPlayableWorldClientHost>();
host.Configure(rootObject.transform, provider, provider, provider, provider);
host.Connect();
var driver = hostObject.AddComponent<EveUnityPlayableWorldInputDriver>();
driver.Host = host;
driver.CameraTransform = cameraObject.transform;
var request = driver.SubmitMoveVectorInput(0f, 1f);
Assert.That(request, Is.Not.Null);
Assert.That(provider.Submitted.Count, Is.EqualTo(1));
Assert.That(request!.Payload.GetString("commandId"), Is.EqualTo("aetheria.daemon.move_intent"));
Assert.That(request.Payload.GetDouble("directionX", 0), Is.EqualTo(1f).Within(0.0001f));
Assert.That(request.Payload.GetDouble("directionY", 0), Is.EqualTo(0f).Within(0.0001f));
Assert.That(request.Payload.GetDouble("scalarValue", 0), Is.EqualTo(1f).Within(0.0001f));
var controlledYaw = rootObject.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>(true)
.Single(marker => marker.EntityId == "player-vanguard")
.transform.eulerAngles.y * Mathf.Deg2Rad;
driver.QueueLookInput(250f);
driver.QueueLookInput(250f);
driver.QueueLookInput(500f);
var look = driver.SubmitPendingLookInput();
Assert.That(look, Is.Not.Null);
Assert.That(provider.Submitted.Count, Is.EqualTo(2));
Assert.That(look!.Payload.GetString("commandId"), Is.EqualTo("aetheria.daemon.look_intent"));
Assert.That(look.Payload.GetDouble("directionX", 0), Is.EqualTo(Mathf.Sin(controlledYaw - 1f)).Within(0.0001f));
Assert.That(look.Payload.GetDouble("directionZ", 0), Is.EqualTo(Mathf.Cos(controlledYaw - 1f)).Within(0.0001f));
host.Connect();
var rebasedYaw = rootObject.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>(true)
.Single(marker => marker.EntityId == "player-vanguard")
.transform.eulerAngles.y * Mathf.Deg2Rad;
var rebasedLook = driver.SubmitLookInput(1000f);
Assert.That(rebasedLook, Is.Not.Null);
Assert.That(provider.Submitted.Count, Is.EqualTo(3));
Assert.That(rebasedLook!.Payload.GetDouble("directionX", 0),
Is.EqualTo(Mathf.Sin(rebasedYaw - 1f)).Within(0.0001f));
Assert.That(rebasedLook.Payload.GetDouble("directionZ", 0),
Is.EqualTo(Mathf.Cos(rebasedYaw - 1f)).Within(0.0001f));
var movement = EveUnityPlayableWorldMoveVector.FromCameraRelativeInput(1f, 1f, null);
Assert.That(movement.HasInput, Is.True);
Assert.That(movement.ScalarValue, Is.EqualTo(1f).Within(0.0001f));
}
finally
{
UnityEngine.Object.DestroyImmediate(cameraObject);
UnityEngine.Object.DestroyImmediate(hostObject);
UnityEngine.Object.DestroyImmediate(rootObject);
}
}
[Test]
public void PlayableWorldInputDriverUsesAdvertisedPrimaryBinding()
{
var capability = new EveInputCapabilityDocument
{
Actions = new[]
{
new EveInputActionDocument
{
ActionId = "weapon-group.0.fire",
Availability = "available"
}
},
DefaultProfiles = new[]
{
new EveInputProfileDocument
{
ProfileId = "keyboard-mouse",
DeviceClass = "keyboard-mouse",
Bindings = new[]
{
new EveInputBindingDocument
{
ActionId = "weapon-group.0.fire",
Gesture = new EveInputGestureDocument
{
Controls = new[] { "mouse.primary" }
},
ActionBar = true
}
}
}
}
};
Assert.That(
EveUnityPlayableWorldInputDriver.ResolvePrimaryActionId(capability),
Is.EqualTo("weapon-group.0.fire"));
capability.Actions[0].Availability = "unavailable";
Assert.That(EveUnityPlayableWorldInputDriver.ResolvePrimaryActionId(capability), Is.Empty);
}
[Test]
public void PlayableWorldInputDriverPerformsAdvertisedViewDirectionBindingOnPressEdge()
{
var rootObject = new GameObject("generic-eve-world-root");
var hostObject = new GameObject("generic-eve-client");
var cameraObject = new GameObject("generic-eve-camera");
hostObject.SetActive(false);
try
{
cameraObject.transform.rotation = Quaternion.Euler(30f, 90f, 0f);
var capability = new EveInputCapabilityDocument
{
ProviderId = "generic-provider",
CapabilityId = "pilot.input",
Actions = new[]
{
new EveInputActionDocument
{
ActionId = "pilot.target-reticle",
Operation = "generic.commands.TargetReticle",
Availability = "available",
InputValue = new EveInputValueDocument
{
Model = EveUnityAdvertisedInputAction.ViewDirectionValueModel,
PayloadKeys = new[] { "directionX", "directionY", "directionZ" }
}
}
},
DefaultProfiles = new[]
{
new EveInputProfileDocument
{
ProfileId = "keyboard-mouse",
DeviceClass = "keyboard-mouse",
Bindings = new[]
{
new EveInputBindingDocument
{
BindingId = "target.reticle.r",
ActionId = "pilot.target-reticle",
Gesture = new EveInputGestureDocument
{
Kind = "direct",
Controls = new[] { "keyboard.r" }
}
}
}
}
}
};
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://generic/eve/surfaces/game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://generic/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"generic-provider"),
capability);
var host = hostObject.AddComponent<EveUnityPlayableWorldClientHost>();
host.Configure(rootObject.transform, provider, provider, provider, provider);
host.Connect();
var pressed = false;
var driver = hostObject.AddComponent<EveUnityPlayableWorldInputDriver>();
driver.Host = host;
driver.CameraTransform = cameraObject.transform;
driver.DigitalControlReader = control => control == "keyboard.r" ? pressed : (bool?)null;
driver.SubmitChangedAdvertisedActions();
pressed = true;
driver.SubmitChangedAdvertisedActions();
driver.SubmitChangedAdvertisedActions();
Assert.That(provider.Submitted.Count, Is.EqualTo(1));
var request = provider.Submitted.Single();
Assert.That(request.Payload.GetString("commandId"), Is.EqualTo("generic.commands.TargetReticle"));
Assert.That(request.Payload.GetString("entityId"), Is.EqualTo("player-vanguard"));
Assert.That(request.Payload.GetString("actionId"), Is.EqualTo("pilot.target-reticle"));
var expected = cameraObject.transform.forward.normalized;
Assert.That(request.Payload.GetDouble("directionX", 0), Is.EqualTo(expected.x).Within(0.000001));
Assert.That(request.Payload.GetDouble("directionY", 0), Is.EqualTo(expected.y).Within(0.000001));
Assert.That(request.Payload.GetDouble("directionZ", 0), Is.EqualTo(expected.z).Within(0.000001));
pressed = false;
driver.SubmitChangedAdvertisedActions();
pressed = true;
driver.SubmitChangedAdvertisedActions();
Assert.That(provider.Submitted.Count, Is.EqualTo(2),
"a released and re-pressed generic binding must perform exactly once per press edge");
}
finally
{
UnityEngine.Object.DestroyImmediate(cameraObject);
UnityEngine.Object.DestroyImmediate(hostObject);
UnityEngine.Object.DestroyImmediate(rootObject);
}
}
[Test]
public void GenericActionBarSelectsSuggestionsAndSubmitsScalarThroughAdvertisedOperation()
{
var rootObject = new GameObject("generic-eve-world-root");
var hostObject = new GameObject("generic-eve-client");
hostObject.SetActive(false);
try
{
var capability = new EveInputCapabilityDocument
{
ProviderId = "generic-provider",
CapabilityId = "pilot.input",
Actions = new[]
{
new EveInputActionDocument
{
ActionId = "equipment.0.temperature",
Label = "Target Temperature",
Operation = "generic.commands.SetTemperature",
Availability = "available",
ActionBar = true,
IconRef = "item.thermostat.icon",
Payload = new Dictionary<string, string>(StringComparer.Ordinal)
{
["equipmentIndex"] = "0",
["behaviorIndex"] = "3"
},
InputValue = new EveInputValueDocument
{
Model = EveUnityAdvertisedInputAction.ScalarValueModel,
PayloadKey = "scalarValue",
CurrentValue = 300,
Unit = "kelvin"
}
},
new EveInputActionDocument
{
ActionId = "pilot.interact",
Label = "Interact",
Operation = "generic.commands.Interact",
Availability = "available"
},
new EveInputActionDocument
{
ActionId = "pilot.hidden",
Label = "Hidden",
Operation = "generic.commands.Hidden",
Availability = "available"
}
},
DefaultProfiles = new[]
{
new EveInputProfileDocument
{
ProfileId = "keyboard-mouse",
DeviceClass = "keyboard-mouse",
Bindings = new[]
{
new EveInputBindingDocument
{
BindingId = "interact.f",
ActionId = "pilot.interact",
ActionBar = true,
Gesture = new EveInputGestureDocument
{
Kind = "direct",
Controls = new[] { "keyboard.f" }
}
}
}
}
}
};
Assert.That(EveUnityInputActionBar.SelectActions(capability)
.Select(action => action.ActionId),
Is.EqualTo(new[] { "pilot.interact", "equipment.0.temperature" }));
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://generic/eve/surfaces/game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://generic/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"generic-provider"),
capability);
var host = hostObject.AddComponent<EveUnityPlayableWorldClientHost>();
host.Configure(rootObject.transform, provider, provider, provider, provider);
host.Connect();
var bar = hostObject.GetComponent<EveUnityInputActionBar>();
Assert.That(bar, Is.Not.Null);
bar!.RefreshNow();
Assert.That(bar.PresentedActionIds,
Is.EquivalentTo(new[] { "pilot.interact", "equipment.0.temperature" }));
bar.SubmitScalar("equipment.0.temperature", "425.5");
var request = provider.Submitted.Single();
Assert.That(request.Payload.GetString("commandId"), Is.EqualTo("generic.commands.SetTemperature"));
Assert.That(request.Payload.GetString("entityId"), Is.EqualTo("player-vanguard"));
Assert.That(request.Payload.GetString("actionId"), Is.EqualTo("equipment.0.temperature"));
Assert.That(request.Payload.GetString("equipmentIndex"), Is.EqualTo("0"));
Assert.That(request.Payload.GetDouble("scalarValue", 0), Is.EqualTo(425.5).Within(0.000001));
}
finally
{
UnityEngine.Object.DestroyImmediate(hostObject);
UnityEngine.Object.DestroyImmediate(rootObject);
}
}
[Test]
public void PlayableWorldCameraRigFollowsAdvertisedPlayerEntityWithoutProviderTypes()
{
var rootObject = new GameObject("generic-eve-world-root");
var hostObject = new GameObject("generic-eve-client");
var cameraObject = new GameObject("generic-eve-camera");
var competingCameraObject = new GameObject("competing-eve-camera");
var secondaryRootObject = new GameObject("secondary-eve-world-root");
var secondaryHostObject = new GameObject("secondary-eve-client");
var secondaryCameraObject = new GameObject("secondary-eve-camera");
hostObject.SetActive(false);
secondaryHostObject.SetActive(false);
var ambientMode = RenderSettings.ambientMode;
var ambientLight = RenderSettings.ambientLight;
var ambientIntensity = RenderSettings.ambientIntensity;
var previousSkybox = RenderSettings.skybox;
var previousReflectionMode = RenderSettings.defaultReflectionMode;
var previousCustomReflection = RenderSettings.customReflectionTexture;
var previousReflectionIntensity = RenderSettings.reflectionIntensity;
var previousDefaultPipeline = GraphicsSettings.defaultRenderPipeline;
var previousQualityPipeline = QualitySettings.renderPipeline;
var gradingRendererData = ScriptableObject.CreateInstance<UniversalRendererData>();
var gradingPipelineAsset = UniversalRenderPipelineAsset.Create(gradingRendererData);
gradingPipelineAsset.colorGradingMode = ColorGradingMode.LowDynamicRange;
GraphicsSettings.defaultRenderPipeline = gradingPipelineAsset;
QualitySettings.renderPipeline = gradingPipelineAsset;
var previousGradingMode = gradingPipelineAsset.colorGradingMode;
Material? skyboxMaterial = null;
Cubemap? reflectionCubemap = null;
VolumeProfile? postProcessProfile = null;
try
{
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
var nativeAssets = hostObject.AddComponent<FixedNativeAssetProviderComponent>();
var skyboxShader = Shader.Find("Skybox/Procedural");
Assert.That(skyboxShader, Is.Not.Null);
skyboxMaterial = new Material(skyboxShader!);
reflectionCubemap = new Cubemap(4, TextureFormat.RGBA32, false);
postProcessProfile = ScriptableObject.CreateInstance<VolumeProfile>();
nativeAssets.Set("material.environment.skybox", skyboxMaterial);
nativeAssets.Set("texture.environment.reflection", reflectionCubemap);
nativeAssets.Set("profile.environment.flight", postProcessProfile);
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(
skyboxAssetRef: "material.environment.skybox",
reflectionAssetRef: "texture.environment.reflection",
postProcessProfileAssetRef: "profile.environment.flight",
cameraReconstruction: "temporal-reprojection.v1",
temporalQuality: "high",
temporalHistoryBlend: "0.99",
temporalJitterScale: "0.1",
temporalSharpening: "0",
exposureMode: "histogram.v1",
exposureLowPercent: "47.37294",
exposureHighPercent: "99",
exposureMinimumEv: "-3",
exposureMaximumEv: "0.3",
exposureKeyValue: "0.5",
exposureAdaptation: "progressive",
exposureSpeedUp: "2",
exposureSpeedDown: "1",
colorGradingSpace: "hdr-before-tonemap.v1"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria"));
var host = hostObject.AddComponent<EveUnityPlayableWorldClientHost>();
host.Configure(rootObject.transform, provider, provider, provider, provider, nativeAssets);
host.Connect();
hostObject.SetActive(true);
var exposedWorld = host.ActiveProjection?.PlayableWorld;
Assert.That(exposedWorld, Is.Not.Null);
Assert.That(exposedWorld!.ExposureMode, Is.EqualTo("histogram.v1"));
Assert.That(exposedWorld.ColorGradingSpace, Is.EqualTo("hdr-before-tonemap.v1"));
Assert.That(exposedWorld.ExposureLowPercent, Is.EqualTo(47.37294f).Within(0.00001f));
Assert.That(exposedWorld.ExposureHighPercent, Is.EqualTo(99f));
Assert.That(exposedWorld.ExposureMinimumEv, Is.EqualTo(-3f));
Assert.That(exposedWorld.ExposureMaximumEv, Is.EqualTo(0.3f).Within(0.00001f));
Assert.That(exposedWorld.ExposureKeyValue, Is.EqualTo(0.5f));
Assert.That(exposedWorld.ExposureAdaptation, Is.EqualTo("progressive"));
Assert.That(exposedWorld.ExposureSpeedUp, Is.EqualTo(2f));
Assert.That(exposedWorld.ExposureSpeedDown, Is.EqualTo(1f));
var rig = hostObject.AddComponent<EveUnityPlayableWorldCameraRig>();
rig.Host = host;
rig.CameraTransform = cameraObject.transform;
rig.RenderPolicySource = new FixedRenderChannelPolicy("map", 14);
var camera = cameraObject.AddComponent<Camera>();
camera.clearFlags = CameraClearFlags.SolidColor;
camera.aspect = 16f / 9f;
camera.orthographic = true;
camera.lensShift = new Vector2(0.25f, -0.2f);
var player = rootObject.GetComponentInChildren<EveUnityPlayableWorldEntityMarker>();
Assert.That(player, Is.Not.Null);
var largeVisual = GameObject.CreatePrimitive(PrimitiveType.Cube);
largeVisual.transform.SetParent(player.transform, false);
largeVisual.transform.localScale = Vector3.one * 100f;
largeVisual.layer = 14;
Assert.That(rig.ApplyRig(0f), Is.True);
Assert.That(host.ActiveCameraTransform, Is.SameAs(cameraObject.transform));
Assert.That(camera.cullingMask & (1 << 14), Is.Zero);
Assert.That(camera.fieldOfView, Is.EqualTo(60f).Within(0.001f));
Assert.That(camera.orthographic, Is.False);
Assert.That(camera.lensShift, Is.EqualTo(Vector2.zero));
Assert.That(camera.nearClipPlane, Is.EqualTo(0.3f).Within(0.001f));
Assert.That(camera.farClipPlane, Is.EqualTo(4096f).Within(0.001f));
Assert.That(cameraObject.transform.position.y - player.transform.position.y, Is.EqualTo(150f).Within(0.001f));
var viewport = camera.WorldToViewportPoint(player.transform.position);
Assert.That(viewport.x, Is.EqualTo(0.9f).Within(0.001f));
Assert.That(viewport.y, Is.EqualTo(0.55f).Within(0.001f));
Assert.That(RenderSettings.ambientMode, Is.EqualTo(UnityEngine.Rendering.AmbientMode.Skybox));
Assert.That(RenderSettings.ambientIntensity, Is.EqualTo(1.46f).Within(0.001f));
Assert.That(RenderSettings.skybox, Is.SameAs(skyboxMaterial));
Assert.That(RenderSettings.defaultReflectionMode, Is.EqualTo(DefaultReflectionMode.Custom));
Assert.That(RenderSettings.customReflectionTexture, Is.SameAs(reflectionCubemap));
Assert.That(RenderSettings.reflectionIntensity, Is.EqualTo(1f).Within(0.001f));
Assert.That(camera.clearFlags, Is.EqualTo(CameraClearFlags.Skybox));
Assert.That(camera.GetComponent<UniversalAdditionalCameraData>(), Is.Not.Null);
var cameraData = camera.GetComponent<UniversalAdditionalCameraData>();
Assert.That(cameraData.renderPostProcessing, Is.True);
Assert.That(cameraData.antialiasing, Is.EqualTo(AntialiasingMode.TemporalAntiAliasing));
Assert.That(cameraData.taaSettings.quality, Is.EqualTo(TemporalAAQuality.High));
Assert.That(cameraData.taaSettings.baseBlendFactor, Is.EqualTo(0.99f).Within(0.0001f));
Assert.That(cameraData.taaSettings.jitterScale, Is.EqualTo(0.1f).Within(0.0001f));
Assert.That(cameraData.taaSettings.contrastAdaptiveSharpening, Is.Zero);
var adaptiveExposure = camera.GetComponent<EveUnityAdaptiveExposureRenderer>();
Assert.That(adaptiveExposure, Is.Not.Null);
Assert.That(adaptiveExposure.IsConfigured, Is.True);
Assert.That(gradingPipelineAsset.colorGradingMode, Is.EqualTo(ColorGradingMode.HighDynamicRange));
var postProcessTransform = hostObject.transform.Find("Eve World Post Process");
Assert.That(postProcessTransform, Is.Not.Null);
Assert.That(postProcessTransform.GetComponent<Volume>().sharedProfile, Is.SameAs(postProcessProfile));
var keyLightTransform = hostObject.transform.Find("Eve World Key Light");
Assert.That(keyLightTransform, Is.Not.Null);
var keyLight = keyLightTransform.GetComponent<Light>();
Assert.That(keyLight.type, Is.EqualTo(LightType.Directional));
Assert.That(keyLight.intensity, Is.EqualTo(0.75f).Within(0.001f));
Assert.That(keyLight.color, Is.EqualTo(new Color(1f, 0.95f, 0.9f, 1f)));
Assert.That(Vector3.Dot(keyLightTransform.forward, new Vector3(0.4f, -1f, 0.25f).normalized),
Is.GreaterThan(0.999f));
Assert.That(nativeAssets.LastBinding?.AssetRef, Is.EqualTo("profile.environment.flight"));
Assert.That(nativeAssets.LastBinding?.EntityKind, Is.Empty);
var secondaryHost = secondaryHostObject.AddComponent<EveUnityPlayableWorldClientHost>();
secondaryHost.Configure(secondaryRootObject.transform, provider, provider, provider, provider, nativeAssets);
secondaryHost.Connect();
secondaryHostObject.SetActive(true);
secondaryCameraObject.AddComponent<Camera>();
var secondaryRig = secondaryCameraObject.AddComponent<EveUnityPlayableWorldCameraRig>();
secondaryRig.Host = secondaryHost;
secondaryRig.CameraTransform = secondaryCameraObject.transform;
Assert.That(secondaryRig.ApplyRig(0f), Is.False);
Assert.That(secondaryHost.ActiveCameraTransform, Is.Null);
Assert.That(RenderSettings.skybox, Is.SameAs(skyboxMaterial));
var competingRig = competingCameraObject.AddComponent<EveUnityPlayableWorldCameraRig>();
competingRig.Host = host;
competingRig.CameraTransform = competingCameraObject.transform;
Assert.That(competingRig.ApplyRig(0f), Is.False);
Assert.That(host.ActiveCameraTransform, Is.SameAs(cameraObject.transform));
var aim = hostObject.GetComponent<EveUnityAimPresentationRenderer>();
Assert.That(aim, Is.Not.Null);
var playerPosition = player.transform.position;
var playerRotation = player.transform.rotation;
aim.RefreshNow();
Assert.That(aim.ViewDotVisible, Is.True);
Assert.That(Vector3.Distance(playerPosition, aim.ViewDotPosition), Is.EqualTo(50f).Within(0.001f));
Assert.That(player.transform.position, Is.EqualTo(playerPosition));
Assert.That(player.transform.rotation, Is.EqualTo(playerRotation));
var selectedTarget = rootObject.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>()
.Single(marker => marker.EntityId == "raider-scout");
Assert.That(EveUnityCombatPresentation.Find(host.ActiveProjection)?.SelectedTargetEntityId,
Is.EqualTo("raider-scout"));
var poseWithoutSelectedTarget = cameraObject.transform.position;
selectedTarget.transform.position = new Vector3(10000f, 0f, -10000f);
Assert.That(rig.ApplyRig(0f), Is.True);
Assert.That(cameraObject.transform.position, Is.EqualTo(poseWithoutSelectedTarget));
viewport = camera.WorldToViewportPoint(player.transform.position);
Assert.That(viewport.x, Is.EqualTo(0.9f).Within(0.001f));
Assert.That(viewport.y, Is.EqualTo(0.55f).Within(0.001f));
var beforeFollow = cameraObject.transform.position;
player.transform.position += Vector3.right * 10f;
Assert.That(rig.ApplyRig(0.1f), Is.True);
Assert.That(
cameraObject.transform.position.x - beforeFollow.x,
Is.EqualTo(10f * (1f - Mathf.Exp(-0.5f))).Within(0.001f));
rig.CameraTransform = null;
Assert.That(host.ActiveCameraTransform, Is.Null);
Assert.That(hostObject.transform.Find("Eve World Key Light"), Is.Null);
Assert.That(hostObject.transform.Find("Eve World Post Process"), Is.Null);
Assert.That(camera.GetComponent<UniversalAdditionalCameraData>(), Is.Null);
Assert.That(gradingPipelineAsset.colorGradingMode, Is.EqualTo(previousGradingMode));
aim.RefreshNow();
Assert.That(aim.ViewDotVisible, Is.False);
Assert.That(RenderSettings.ambientMode, Is.EqualTo(ambientMode));
Assert.That(RenderSettings.ambientLight, Is.EqualTo(ambientLight));
Assert.That(RenderSettings.ambientIntensity, Is.EqualTo(ambientIntensity));
Assert.That(RenderSettings.skybox, Is.SameAs(previousSkybox));
Assert.That(RenderSettings.defaultReflectionMode, Is.EqualTo(previousReflectionMode));
Assert.That(RenderSettings.customReflectionTexture, Is.SameAs(previousCustomReflection));
Assert.That(RenderSettings.reflectionIntensity, Is.EqualTo(previousReflectionIntensity));
Assert.That(camera.clearFlags, Is.EqualTo(CameraClearFlags.SolidColor));
rig.CameraTransform = cameraObject.transform;
nativeAssets.Clear();
nativeAssets.Set("some.other.asset", skyboxMaterial);
Assert.That(rig.ApplyRig(0f), Is.False);
Assert.That(host.ActiveCameraTransform, Is.Null);
nativeAssets.Set("material.environment.skybox", reflectionCubemap);
nativeAssets.Set("texture.environment.reflection", skyboxMaterial);
nativeAssets.Set("profile.environment.flight", skyboxMaterial);
Assert.That(rig.ApplyRig(0f), Is.False);
Assert.That(host.ActiveCameraTransform, Is.Null);
}
finally
{
RenderSettings.ambientMode = ambientMode;
RenderSettings.ambientLight = ambientLight;
RenderSettings.ambientIntensity = ambientIntensity;
RenderSettings.skybox = previousSkybox;
RenderSettings.defaultReflectionMode = previousReflectionMode;
RenderSettings.customReflectionTexture = previousCustomReflection;
RenderSettings.reflectionIntensity = previousReflectionIntensity;
QualitySettings.renderPipeline = previousQualityPipeline;
GraphicsSettings.defaultRenderPipeline = previousDefaultPipeline;
UnityEngine.Object.DestroyImmediate(gradingPipelineAsset);
UnityEngine.Object.DestroyImmediate(gradingRendererData);
if (skyboxMaterial != null) UnityEngine.Object.DestroyImmediate(skyboxMaterial);
if (reflectionCubemap != null) UnityEngine.Object.DestroyImmediate(reflectionCubemap);
if (postProcessProfile != null) UnityEngine.Object.DestroyImmediate(postProcessProfile);
UnityEngine.Object.DestroyImmediate(secondaryCameraObject);
UnityEngine.Object.DestroyImmediate(secondaryHostObject);
UnityEngine.Object.DestroyImmediate(secondaryRootObject);
UnityEngine.Object.DestroyImmediate(competingCameraObject);
UnityEngine.Object.DestroyImmediate(cameraObject);
UnityEngine.Object.DestroyImmediate(hostObject);
UnityEngine.Object.DestroyImmediate(rootObject);
}
}
[Test]
public void PlayableWorldCameraRigFramesEntityAndLooksAtAdvertisedConvergencePointWithoutProviderTypes()
{
var rootObject = new GameObject("generic-forward-world-root");
var hostObject = new GameObject("generic-forward-client");
var cameraObject = new GameObject("generic-forward-camera");
hostObject.SetActive(false);
try
{
var provider = hostObject.AddComponent<FakePlayableWorldProviderComponent>();
var nativeAssets = hostObject.AddComponent<FixedNativeAssetProviderComponent>();
provider.Set(
new EveUnitySceneProviderSurfaceDocument(
PlayableArpgDocument(
cameraRig: "perspective.entity-forward-follow.v1",
cameraDistance: "30",
cameraTargetScreenX: "0.64",
cameraTargetScreenY: "0.81",
cameraPositionDamping: "0",
cameraNearClipPlane: "1",
cameraFarClipPlane: "4096",
cameraLookAt: "aim.convergence-point.v1"),
Advertisement("aetheria.daemon.game"),
"cultmesh://generic/eve/surfaces/forward",
1),
new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://generic/assets/manifest",
Array.Empty<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"generic"));
var host = hostObject.AddComponent<EveUnityPlayableWorldClientHost>();
host.Configure(rootObject.transform, provider, provider, provider, provider, nativeAssets);
host.Connect();
hostObject.SetActive(true);
var camera = cameraObject.AddComponent<Camera>();
camera.aspect = 16f / 9f;
var rig = hostObject.AddComponent<EveUnityPlayableWorldCameraRig>();
rig.Host = host;
rig.CameraTransform = cameraObject.transform;
var player = rootObject.GetComponentInChildren<EveUnityPlayableWorldEntityMarker>();
Assert.That(player, Is.Not.Null);
Assert.That(rig.ApplyRig(0f), Is.True);
var aimPoint = player!.transform.position + player.transform.forward * 50f;
var cameraToAim = (aimPoint - cameraObject.transform.position).normalized;
Assert.That(Vector3.Dot(cameraObject.transform.forward, cameraToAim), Is.GreaterThan(0.999f));
Assert.That(cameraObject.transform.forward.y, Is.GreaterThan(0f));
Assert.That(camera.fieldOfView, Is.EqualTo(60f).Within(0.001f));
Assert.That(camera.nearClipPlane, Is.EqualTo(1f).Within(0.001f));
Assert.That(camera.farClipPlane, Is.EqualTo(4096f).Within(0.001f));
var viewport = camera.WorldToViewportPoint(player.transform.position);
Assert.That(viewport.x, Is.EqualTo(0.64f).Within(0.001f));
Assert.That(viewport.y, Is.EqualTo(0.81f).Within(0.001f));
Assert.That(cameraObject.transform.position.y, Is.LessThan(player.transform.position.y));
}
finally
{
UnityEngine.Object.DestroyImmediate(cameraObject);
UnityEngine.Object.DestroyImmediate(hostObject);
UnityEngine.Object.DestroyImmediate(rootObject);
}
}
[Test]
public void SemanticScaleWrapsProviderAuthoredScaleAndFallbackGeometry()
{
var root = new GameObject("world");
var prefab = new GameObject("provider-prefab");
prefab.transform.localScale = new Vector3(2f, 3f, 4f);
try
{
var providerSink = new EveUnityGameObjectPlayableWorldSceneSink(
root.transform,
new FixedGameObjectAssetProvider(prefab));
var entity = PlayableEntityModel("provider", "prefab.ship", 12f);
providerSink.UpsertEntity(entity, new EveUnityPlayableWorldAssetBinding(
"prefab.ship", "ship", "provider-asset-ref"));
var instance = root.transform.GetChild(0);
Assert.That(instance.localScale, Is.EqualTo(Vector3.one * 12f));
Assert.That(instance.GetChild(0).localScale, Is.EqualTo(new Vector3(2f, 3f, 4f)));
var fallbackSink = new EveUnityGameObjectPlayableWorldSceneSink(
root.transform,
new FixedGameObjectAssetProvider(null));
fallbackSink.UpsertEntity(
PlayableEntityModel("fallback", "", 7f),
new EveUnityPlayableWorldAssetBinding("", "ship", "unity-generated-placeholder"));
Assert.That(root.transform.GetChild(1).localScale, Is.EqualTo(Vector3.one * 7f));
}
finally
{
UnityEngine.Object.DestroyImmediate(prefab);
UnityEngine.Object.DestroyImmediate(root);
}
}
[Test]
public void PlayableWorldPresenterInstantiatesUpdatesAndDespawnsProviderEntities()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var sink = new FakePlayableWorldSceneSink();
var presenter = new EveUnityPlayableWorldPresenter(sink, new EveUnityAssetRefResolver());
var firstProjection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
var firstPresentation = presenter.Apply(firstProjection);
Assert.That(firstPresentation.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
Assert.That(firstPresentation.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(firstPresentation.InputProfile, Is.EqualTo("arpg-third-person"));
Assert.That(firstPresentation.CameraRig, Is.EqualTo("planar.top-down-follow.v1"));
Assert.That(firstPresentation.UpsertedEntities, Is.EqualTo(3));
Assert.That(firstPresentation.RemovedEntities, Is.EqualTo(0));
Assert.That(firstPresentation.ActiveEntities, Is.EqualTo(3));
Assert.That(sink.ConfiguredWorlds.Count, Is.EqualTo(1));
Assert.That(sink.Upserts.Count, Is.EqualTo(3));
Assert.That(sink.Upserts[0].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sink.Upserts[0].entity.PositionX, Is.EqualTo(0f));
Assert.That(sink.Upserts[0].asset.AssetRef, Is.EqualTo("cultmesh://aetheria/assets/map/entity/player"));
Assert.That(sink.Upserts[0].asset.PresentationKind, Is.EqualTo("provider-asset-ref"));
var secondProjection = lowerer.Lower(
PlayableArpgDocument(includeRaider: false, playerPosition: "5,0,2"),
Advertisement("aetheria.daemon.game"));
var secondPresentation = presenter.Apply(secondProjection);
Assert.That(secondPresentation.UpsertedEntities, Is.EqualTo(2));
Assert.That(secondPresentation.RemovedEntities, Is.EqualTo(1));
Assert.That(secondPresentation.ActiveEntities, Is.EqualTo(2));
Assert.That(sink.ConfiguredWorlds.Count, Is.EqualTo(2));
Assert.That(sink.RemovedEntityIds.Count, Is.EqualTo(1));
Assert.That(sink.RemovedEntityIds[0], Is.EqualTo("raider-scout"));
Assert.That(sink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sink.Upserts[3].entity.PositionX, Is.EqualTo(5f));
Assert.That(sink.Upserts[3].entity.PositionZ, Is.EqualTo(2f));
}
[Test]
public void LivePlayableWorldClientPresentsProviderSnapshotsAndKeepsCommandsProviderOwned()
{
var source = new FakeProviderSurfaceSource(
"aetheria",
"aetheria.daemon.game",
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
var commandSink = new FakeCommandSink("cultmesh-command-sink");
var sceneSink = new FakePlayableWorldSceneSink();
using var connection = new EveUnitySceneProviderConnection(source, commandSink);
using var client = new EveUnityPlayableWorldLiveClient(
connection,
new EveUnityPlayableWorldPresenter(sceneSink, new EveUnityAssetRefResolver()));
var initialPresentation = client.Connect();
Assert.That(initialPresentation.ActiveEntities, Is.EqualTo(3));
Assert.That(initialPresentation.PlayerEntityId, Is.EqualTo("player-vanguard"));
Assert.That(client.ActiveWorld, Is.Not.Null);
Assert.That(client.ActiveWorld!.AssetManifest, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(1));
Assert.That(sceneSink.Upserts.Count, Is.EqualTo(3));
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(includeRaider: false, playerPosition: "7,0,4"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(client.LastPresentation, Is.Not.Null);
Assert.That(client.LastPresentation!.ActiveEntities, Is.EqualTo(2));
Assert.That(client.LastPresentation.RemovedEntities, Is.EqualTo(1));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(2));
Assert.That(sceneSink.RemovedEntityIds[0], Is.EqualTo("raider-scout"));
Assert.That(sceneSink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sceneSink.Upserts[3].entity.PositionX, Is.EqualTo(7f));
Assert.That(sceneSink.Upserts[3].entity.PositionZ, Is.EqualTo(4f));
var moveIntent = client.SubmitMoveIntent(
"player-vanguard",
12f,
0f,
8f,
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(commandSink.Submitted.Count, Is.EqualTo(1));
Assert.That(commandSink.Submitted[0], Is.SameAs(moveIntent));
Assert.That(moveIntent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(moveIntent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(moveIntent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(moveIntent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
client.Disconnect();
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(playerPosition: "99,0,99"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
3));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(2));
}
[Test]
public void LivePlayableWorldClientReconcilesReceiptAfterReactiveStateVersionAdvances()
{
var source = new FakeProviderSurfaceSource(
"aetheria",
"aetheria.daemon.game",
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
1));
var commandSink = new FakeCommandSink("cultmesh-command-sink");
var receiptSource = new FakeCommandReceiptSource();
var sceneSink = new FakePlayableWorldSceneSink();
using var connection = new EveUnitySceneProviderConnection(source, commandSink);
using var client = new EveUnityPlayableWorldLiveClient(
connection,
new EveUnityPlayableWorldPresenter(sceneSink, new EveUnityAssetRefResolver()),
receiptSource);
client.Connect();
client.SubmitMoveIntent("player-vanguard", 30f, 0f, 30f);
Assert.That(sceneSink.Upserts[0].entity.PositionX, Is.EqualTo(0f));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(1));
receiptSource.Publish(new EveUnitySceneCommandReceipt(
"aetheria.daemon.move_intent.pending",
"aetheria.daemon.commands",
"aetheria.daemon.move_intent",
"pending",
"Aetheria",
"AetheriaRuntimeDaemonCommandBoundaryDocument",
"aetheria.eve_command_acceptance_status.v1",
"aetheria",
"aetheria.daemon.game"));
Assert.That(client.LastReceipt, Is.Not.Null);
Assert.That(client.LastReceipt!.State, Is.EqualTo("pending"));
Assert.That(client.LastReceipt.IsProviderOwned, Is.True);
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(1));
receiptSource.Publish(new EveUnitySceneCommandReceipt(
"aetheria.daemon.move_intent.reconciled",
"aetheria.daemon.commands",
"aetheria.daemon.move_intent",
"reconciled",
"Aetheria",
"AetheriaRuntimeDaemonCommandBoundaryDocument",
"gamecult.eve.command_receipt.v1",
"aetheria",
"aetheria.daemon.game",
sourceVersion: 2));
Assert.That(client.LastReceipt.State, Is.EqualTo("pending"));
Assert.That(client.ActiveVersion, Is.EqualTo(1));
client.AdvanceStateVersion(2);
Assert.That(client.LastReceipt.State, Is.EqualTo("reconciled"));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(1),
"reactive state visibility must not require republishing heavy surface topology");
source.Publish(new EveUnitySceneProviderSurfaceSnapshot(
PlayableArpgDocument(includeRaider: false, playerPosition: "30,0,30"),
Advertisement("aetheria.daemon.game"),
"cultmesh://aetheria/eve/surfaces/aetheria.daemon.game",
2));
Assert.That(client.ActiveVersion, Is.EqualTo(2));
Assert.That(client.LastPresentation, Is.Not.Null);
Assert.That(client.LastPresentation!.ActiveEntities, Is.EqualTo(2));
Assert.That(sceneSink.ConfiguredWorlds.Count, Is.EqualTo(2));
Assert.That(sceneSink.Upserts[3].entity.EntityId, Is.EqualTo("player-vanguard"));
Assert.That(sceneSink.Upserts[3].entity.PositionX, Is.EqualTo(30f));
Assert.That(sceneSink.Upserts[3].entity.PositionZ, Is.EqualTo(30f));
}
[Test]
public void AssetManifestMapsProviderAssetRefsToUnityLoadKeysWithoutAetheriaTypes()
{
var manifest = new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard"),
new EveUnityPlayableWorldAssetManifestEntry(
"",
"enemy",
"Resources/Aetheria/Entities/Raider.prefab",
"aetheria.raider")
});
var player = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(player, Is.Not.Null);
Assert.That(player!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Vanguard"));
Assert.That(player.PrefabKey, Is.EqualTo("aetheria.vanguard"));
var enemy = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/missing-raider",
"enemy",
"provider-asset-ref"));
Assert.That(enemy, Is.Not.Null);
Assert.That(enemy!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Raider"));
Assert.That(enemy.PrefabKey, Is.EqualTo("aetheria.raider"));
}
[Test]
public void AssetManifestCacheTracksPlayableWorldManifestPointerAndLiveUpdates()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
Assert.That(projection.PlayableWorld, Is.Not.Null);
var source = new FakeAssetManifestSource(new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard")
}));
var cache = new EveUnityPlayableWorldAssetManifestCache();
cache.Connect(source);
var manifest = cache.GetForWorld(projection.PlayableWorld!);
Assert.That(manifest, Is.Not.Null);
Assert.That(manifest!.ManifestRef, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
Assert.That(cache.Count, Is.EqualTo(1));
var player = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(player, Is.Not.Null);
Assert.That(player!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Vanguard"));
source.Publish(new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/VanguardMk2.prefab",
"aetheria.vanguard.mk2")
}));
var updated = cache.GetForWorld(projection.PlayableWorld);
Assert.That(updated, Is.Not.Null);
var updatedPlayer = updated!.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(updatedPlayer, Is.Not.Null);
Assert.That(updatedPlayer!.ResourcesPath, Is.EqualTo("Aetheria/Entities/VanguardMk2"));
cache.Disconnect(source);
source.Publish(new EveUnityPlayableWorldAssetManifest(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/VanguardIgnored.prefab",
"aetheria.vanguard.ignored")
}));
var afterDisconnect = cache.GetForWorld(projection.PlayableWorld);
var afterDisconnectPlayer = afterDisconnect!.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(afterDisconnectPlayer!.ResourcesPath, Is.EqualTo("Aetheria/Entities/VanguardMk2"));
}
[Test]
public void AssetManifestDocumentSourceFeedsCacheWithoutChangingSceneLowering()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
Assert.That(projection.PlayableWorld, Is.Not.Null);
var documentSource = new FakeAssetManifestDocumentSource(new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestDocumentEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"resources://Aetheria/Entities/Vanguard.prefab",
"aetheria.vanguard")
},
"aetheria"));
using var manifestSource = new EveUnityPlayableWorldAssetManifestDocumentSource(documentSource);
var cache = new EveUnityPlayableWorldAssetManifestCache();
manifestSource.Connect();
cache.Connect(manifestSource);
var manifest = cache.GetForWorld(projection.PlayableWorld!);
Assert.That(manifest, Is.Not.Null);
Assert.That(manifest!.ManifestRef, Is.EqualTo("cultmesh://aetheria/assets/manifest"));
var player = manifest.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(player, Is.Not.Null);
Assert.That(player!.ResourcesPath, Is.EqualTo("Aetheria/Entities/Vanguard"));
Assert.That(player.PrefabKey, Is.EqualTo("aetheria.vanguard"));
documentSource.Publish(new EveUnityPlayableWorldAssetManifestDocument(
"cultmesh://aetheria/assets/manifest",
new[]
{
new EveUnityPlayableWorldAssetManifestDocumentEntry(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"Resources/Aetheria/Entities/VanguardAuthority.prefab",
"aetheria.vanguard.authority")
},
"aetheria"));
var updated = cache.GetForWorld(projection.PlayableWorld);
var updatedPlayer = updated!.Find(new EveUnityPlayableWorldAssetBinding(
"cultmesh://aetheria/assets/map/entity/player",
"player",
"provider-asset-ref"));
Assert.That(updatedPlayer, Is.Not.Null);
Assert.That(updatedPlayer!.ResourcesPath, Is.EqualTo("Aetheria/Entities/VanguardAuthority"));
Assert.That(updatedPlayer.PrefabKey, Is.EqualTo("aetheria.vanguard.authority"));
}
[Test]
public void SaiVisualNovelLowersThroughRuntimeProjectionAdapterWithoutOwningStoryState()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var projection = lowerer.Lower(SaiDocument(), Advertisement("sai.visual_novel.surface"));
Assert.That(projection.ProviderId, Is.EqualTo("gamecult.home.vn"));
Assert.That(projection.SurfaceId, Is.EqualTo("sai.visual_novel.surface"));
Assert.That(projection.Root.SceneObjectKind, Is.EqualTo("sai-vn-scene-stage"));
Assert.That(projection.Root.PluginProjection, Is.Not.Null);
Assert.That(projection.Root.PluginProjection!.PluginId, Is.EqualTo("sai.vn"));
Assert.That(projection.Root.PluginProjection.ProjectionKind, Is.EqualTo("sai-vn-scene-stage-shell"));
Assert.That(projection.Root.PluginProjection.AbiSchema, Is.EqualTo("gamecult.eve.plugin_abi.v1"));
Assert.That(projection.Root.PluginProjection.CommandBoundary, Is.EqualTo("sidecar-advertised-plugin-abi"));
Assert.That(projection.Root.PluginProjection.Capabilities, Does.Contain("vn.stage"));
Assert.That(projection.Root.PluginProjection.Capabilities, Does.Contain("story.choose"));
Assert.That(projection.Root.PluginProjection.DocumentId, Is.EqualTo("gamecult-compound"));
Assert.That(projection.Root.PluginProjection.SemanticOwner, Is.EqualTo("Sai"));
var dialogue = projection.Root.Children[0];
Assert.That(dialogue.SceneObjectKind, Is.EqualTo("sai-vn-scene-dialogue"));
Assert.That(dialogue.PluginProjection!.ProjectionKind, Is.EqualTo("sai-vn-scene-dialogue-shell"));
Assert.That(dialogue.PluginProjection.SemanticOwner, Is.EqualTo("Sai"));
var choice = projection.Root.Children[1].Children[0];
Assert.That(choice.SceneObjectKind, Is.EqualTo("command-control"));
Assert.That(choice.PluginProjection, Is.Not.Null);
Assert.That(choice.PluginProjection!.PluginId, Is.EqualTo("sai.vn"));
Assert.That(choice.PluginProjection.ProjectionKind, Is.EqualTo("sai-vn-scene-story-command-shell"));
Assert.That(choice.PluginProjection.Command, Is.EqualTo("story.choose"));
Assert.That(choice.PluginProjection.SemanticOwner, Is.EqualTo("Sai"));
var tex = projection.Root.Children[2];
Assert.That(tex.SceneObjectKind, Is.EqualTo("tex-math-scene-projection"));
Assert.That(tex.PluginProjection, Is.Not.Null);
Assert.That(tex.PluginProjection!.PluginId, Is.EqualTo("tex.math"));
Assert.That(tex.PluginProjection.ProjectionKind, Is.EqualTo("tex-math-scene-block-fallback-shell"));
Assert.That(tex.PluginProjection.Capabilities, Does.Contain("embed.tex"));
Assert.That(tex.PluginProjection.Capabilities, Does.Contain("tex.scene-placement"));
Assert.That(tex.PluginProjection.DocumentId, Is.EqualTo("\\\\mathrm{votes}(p)=1+\\\\lfloor\\\\log_b(1+p)\\\\rfloor"));
Assert.That(tex.PluginProjection.SemanticOwner, Is.EqualTo("EvePlugins"));
}
[Test]
public void CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts()
{
var lowerer = new EveUnitySceneSurfaceLowerer();
var intent = lowerer.CreateCommandIntent(
Document("aetheria.daemon.game"),
Advertisement("aetheria.daemon.game"),
"aetheria.daemon.commands",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["commandId"] = "aetheria.daemon.focus"
},
DateTimeOffset.Parse("2026-07-09T00:00:00Z"));
Assert.That(intent.Schema, Is.EqualTo(EveSurfaceCommandRequest.SchemaId));
Assert.That(intent.ProviderId, Is.EqualTo("aetheria"));
Assert.That(intent.SurfaceId, Is.EqualTo("aetheria.daemon.game"));
Assert.That(intent.Command, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(intent.ClientId, Is.EqualTo("unity-scene"));
Assert.That(intent.CommandBoundary, Is.EqualTo("aetheria.daemon.commands"));
Assert.That(intent.ReceiptSchema, Is.EqualTo("aetheria.eve_command_acceptance_status.v1"));
}
private static EveSurfaceDocument Document(string surfaceId)
{
return new EveSurfaceDocument(
"aetheria",
"game.provider",
"Aetheria world",
1,
"2026-07-09T00:00:00Z",
new EveSurfaceTree(
surfaceId,
new EveSurfaceComponent(
$"{surfaceId}.root",
"surface",
new Dictionary<string, string>(StringComparer.Ordinal),
new[]
{
new EveSurfaceComponent(
$"{surfaceId}.entities",
"world.entities",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["binding"] = "cultmesh://aetheria/world/entities"
},
Array.Empty<EveSurfaceComponent>(),
new[]
{
new CultMeshStateBindingDescriptor("entities", "cultmesh://aetheria/world/entities")
}),
new EveSurfaceComponent(
$"{surfaceId}.focus",
"control.button",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["command"] = "aetheria.daemon.focus"
},
Array.Empty<EveSurfaceComponent>()),
new EveSurfaceComponent(
$"{surfaceId}.norn",
"embed.norn",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["graph.document"] = "cultmesh://aetheria/norn/map",
["interaction.nodeAction"] = "graph.focus"
},
Array.Empty<EveSurfaceComponent>(),
Array.Empty<CultMeshStateBindingDescriptor>(),
new[]
{
new EveEmbeddedDocumentSlot(
"norn.map",
"cultmesh://aetheria/norn/map",
"gamecult.eve.surface.v1",
"scene-overlay")
})
}),
Array.Empty<EveStyleToken>()),
Array.Empty<EveCommandTemplate>());
}
private static EveSurfaceDocument PlayableArpgDocument(
bool includeRaider = true,
string playerPosition = "0,0,0",
string skyboxAssetRef = "",
string reflectionAssetRef = "",
string postProcessProfileAssetRef = "",
string cameraRig = "planar.top-down-follow.v1",
string cameraDistance = "150",
string cameraTargetScreenX = "0.9",
string cameraTargetScreenY = "0.55",
string cameraPositionDamping = "5",
string cameraNearClipPlane = "0.3",
string cameraFarClipPlane = "4096",
string cameraLookAt = "",
string cameraReconstruction = "",
string temporalQuality = "high",
string temporalHistoryBlend = "0",
string temporalJitterScale = "0",
string temporalSharpening = "0",
string exposureMode = "",
string exposureLowPercent = "50",
string exposureHighPercent = "95",
string exposureMinimumEv = "0",
string exposureMaximumEv = "0",
string exposureKeyValue = "1",
string exposureAdaptation = "progressive",
string exposureSpeedUp = "2",
string exposureSpeedDown = "1",
string colorGradingSpace = "")
{
var playableChildren = new List<EveSurfaceComponent>
{
PlayableEntity(
"aetheria.daemon.game.entity.player",
"player-vanguard",
"player",
"Vanguard",
"player",
"cultmesh://aetheria/assets/map/entity/player",
playerPosition,
"35",
"1.15",
true,
true,
"aetheria.daemon.focus",
"aetheria.daemon.move_intent",
"",
"")
};
if (includeRaider)
{
playableChildren.Add(PlayableEntity(
"aetheria.daemon.game.entity.raider",
"raider-scout",
"enemy",
"Raider Scout",
"raider",
"cultmesh://aetheria/assets/map/entity/ship",
"9,0,14",
"220",
"0.9",
true,
false,
"",
"",
"aetheria.daemon.target",
""));
}
playableChildren.Add(PlayableEntity(
"aetheria.daemon.game.entity.station",
"anchor-station",
"station",
"Anchor Station",
"neutral",
"cultmesh://aetheria/assets/map/entity/station",
"-18,0,6",
"0",
"3.4",
true,
false,
"aetheria.daemon.focus",
"",
"",
""));
playableChildren.Add(new EveSurfaceComponent(
"aetheria.daemon.game.aim",
"aim.presentation",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["controlledEntityId"] = "player-vanguard",
["convergenceTargetEntityId"] = "",
["viewDotRole"] = "pilot.view-direction",
["minimumConvergenceDistance"] = "50",
["viewDotRadius"] = "0.8"
},
Array.Empty<EveSurfaceComponent>()));
playableChildren.Add(new EveSurfaceComponent(
"aetheria.daemon.game.combat",
"combat.presentation",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["controlledEntityId"] = "player-vanguard",
["selectedTargetEntityId"] = "raider-scout",
["targetVisible"] = "true",
["targetHostile"] = "true"
},
Array.Empty<EveSurfaceComponent>()));
playableChildren.Add(new EveSurfaceComponent(
"aetheria.daemon.game.flow",
"field.vector3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["fieldId"] = "aetheria.zone.flow",
["bind"] = "aetheria.daemon.soaView.flowField3d",
["visualizer"] = "particles",
["bounds"] = "-32,0,-32,32,24,32"
},
Array.Empty<EveSurfaceComponent>()));
playableChildren.Add(new EveSurfaceComponent(
"aetheria.daemon.game.gravity-fog",
"field.volume3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["documentRef"] = "cultmesh://aetheria/world/fog-splats",
["documentSchema"] = EveFieldsSchemas.Splats,
["materialAssetRef"] = "shader.environment.gravity-fog",
["renderChannel"] = "world.transparent",
["compositeMode"] = "premultiplied-alpha",
["quality"] = "normal",
["layerBindings"] = "fog.surface_height=surfaceHeight;fog.tint=tint"
},
Array.Empty<EveSurfaceComponent>()));
playableChildren.Add(new EveSurfaceComponent(
"aetheria.daemon.game.stardust",
"field.particles3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["documentRef"] = "cultmesh://aetheria/world/fog-splats",
["documentSchema"] = EveFieldsSchemas.Splats,
["computeProgramAssetRef"] = "compute.environment.stardust",
["materialAssetRef"] = "material.environment.stardust",
["renderChannel"] = "world.transparent",
["span"] = "256",
["spacing"] = "6"
},
Array.Empty<EveSurfaceComponent>()));
return new EveSurfaceDocument(
"aetheria",
"game.runtime",
"Aetheria daemon game",
1,
"2026-07-09T00:00:00Z",
new EveSurfaceTree(
"aetheria.daemon.game",
new EveSurfaceComponent(
"aetheria.daemon.game.root",
"surface",
new Dictionary<string, string>(StringComparer.Ordinal),
new[]
{
new EveSurfaceComponent(
"aetheria.daemon.game.playable",
"world.scene3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["statePointerId"] = "cultmesh://aetheria/run/current",
["entityViewPointerId"] = "cultmesh://aetheria/world/entities.soa",
["entityViewSchema"] = EveEntitySoaViewDocument.SchemaId,
["entityBodyId"] = "eve:entity-soa:aetheria.daemon:pilot",
["zoneRenderPointerId"] = "cultmesh://aetheria/world/zone-render",
["zoneRenderSchema"] = "gamecult.aetheria.zone_render.v1",
["assetManifest"] = "cultmesh://aetheria/assets/manifest",
["assetProviderId"] = "aetheria.assets",
["assetVerseId"] = "aetheria.mods",
["assetAuthorityRuntimeId"] = "mod-authority",
["assetRendezvousEndpoints"] = "wss://odin-a/cultmesh;wss://odin-b/cultmesh",
["inputProfile"] = "arpg-third-person",
["cameraRig"] = cameraRig,
["cameraLookAt"] = cameraLookAt,
["cameraTargetEntityId"] = "player-vanguard",
["cameraDistance"] = cameraDistance,
["cameraVerticalFieldOfViewDegrees"] = "60",
["cameraTargetScreenX"] = cameraTargetScreenX,
["cameraTargetScreenY"] = cameraTargetScreenY,
["cameraPositionDamping"] = cameraPositionDamping,
["cameraNearClipPlane"] = cameraNearClipPlane,
["cameraFarClipPlane"] = cameraFarClipPlane,
["ambientLightColor"] = "0.2,0.2,0.2",
["ambientLightIntensity"] = "1.46",
["skyboxAssetRef"] = skyboxAssetRef,
["reflectionAssetRef"] = reflectionAssetRef,
["reflectionIntensity"] = "1",
["postProcessProfileAssetRef"] = postProcessProfileAssetRef,
["cameraReconstruction"] = cameraReconstruction,
["temporalQuality"] = temporalQuality,
["temporalHistoryBlend"] = temporalHistoryBlend,
["temporalJitterScale"] = temporalJitterScale,
["temporalSharpening"] = temporalSharpening,
["exposureMode"] = exposureMode,
["exposureLowPercent"] = exposureLowPercent,
["exposureHighPercent"] = exposureHighPercent,
["exposureMinimumEv"] = exposureMinimumEv,
["exposureMaximumEv"] = exposureMaximumEv,
["exposureKeyValue"] = exposureKeyValue,
["exposureAdaptation"] = exposureAdaptation,
["exposureSpeedUp"] = exposureSpeedUp,
["exposureSpeedDown"] = exposureSpeedDown,
["colorGradingSpace"] = colorGradingSpace,
["keyLightDirection"] = "0.4,-1,0.25",
["keyLightColor"] = "1,0.95,0.9",
["keyLightIntensity"] = "0.75",
["excludedRenderChannels"] = "map",
["playerEntityId"] = "player-vanguard",
["movementCommand"] = "aetheria.daemon.move_intent",
["lookCommand"] = "aetheria.daemon.look_intent",
["lookModel"] = "planar-yaw.v1",
["lookSensitivityRadians"] = "-0.001",
["focusCommand"] = "aetheria.daemon.focus",
["targetCommand"] = "aetheria.daemon.target",
["actionCommand"] = "aetheria.daemon.use_equipment"
},
playableChildren,
new[]
{
new CultMeshStateBindingDescriptor("run", "cultmesh://aetheria/run/current"),
new CultMeshStateBindingDescriptor("assets", "cultmesh://aetheria/assets/manifest")
})
}),
Array.Empty<EveStyleToken>()),
new[]
{
new EveCommandTemplate(CultMesh.OperationBinding("aetheria.daemon.commands", "Aetheria daemon commands"))
});
}
private static EveSurfaceComponent PlayableEntity(
string nodeId,
string entityId,
string entityKind,
string label,
string faction,
string assetRef,
string position,
string rotationY,
string radius,
bool selectable,
bool controllable,
string focusCommand,
string moveCommand,
string targetCommand,
string actionCommand)
{
return new EveSurfaceComponent(
nodeId,
"world.entity3d",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["entityId"] = entityId,
["entityKind"] = entityKind,
["label"] = label,
["faction"] = faction,
["assetRef"] = assetRef,
["position"] = position,
["rotationY"] = rotationY,
["radius"] = radius,
["presentationState"] = "active",
["selectable"] = selectable ? "true" : "false",
["controllable"] = controllable ? "true" : "false",
["focusCommand"] = focusCommand,
["moveCommand"] = moveCommand,
["targetCommand"] = targetCommand,
["actionCommand"] = actionCommand
},
Array.Empty<EveSurfaceComponent>());
}
private static EveUnityPlayableWorldEntity FindEntity(
EveUnityPlayableWorldProjection playableWorld,
string entityId)
{
foreach (var entity in playableWorld.Entities)
{
if (string.Equals(entity.EntityId, entityId, StringComparison.Ordinal))
return entity;
}
throw new AssertionException($"Playable entity not found: {entityId}");
}
private static EveSurfaceDocument SaiDocument()
{
return new EveSurfaceDocument(
"gamecult.home.vn",
"sai.visual_novel",
"GameCult Compound VN",
1,
"2026-07-09T00:00:00Z",
new EveSurfaceTree(
"sai.visual_novel.surface",
new EveSurfaceComponent(
"sai.root",
"vn.stage",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["storyId"] = "gamecult-compound",
["currentPath"] = "hub"
},
new[]
{
new EveSurfaceComponent(
"sai.dialogue",
"panel.dialogue",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["speaker"] = "Void",
["text"] = "Pick a door."
},
Array.Empty<EveSurfaceComponent>()),
new EveSurfaceComponent(
"sai.choices",
"rail.actions",
new Dictionary<string, string>(StringComparer.Ordinal),
new[]
{
new EveSurfaceComponent(
"sai.choice.eve",
"control.button",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["label"] = "What is Eve?",
["action.command"] = "story.choose",
["targetPath"] = "eve"
},
Array.Empty<EveSurfaceComponent>())
}),
new EveSurfaceComponent(
"sai.tex.log-power",
"embed.tex",
new Dictionary<string, string>(StringComparer.Ordinal)
{
["label"] = "Bifrost voting weight",
["source"] = "\\\\mathrm{votes}(p)=1+\\\\lfloor\\\\log_b(1+p)\\\\rfloor",
["format"] = "latex",
["display"] = "block"
},
Array.Empty<EveSurfaceComponent>())
}),
Array.Empty<EveStyleToken>()),
new[]
{
new EveCommandTemplate(CultMesh.OperationBinding("story.choose", "Choose"))
});
}
private static EveUnitySceneProviderSurfaceAdvertisement Advertisement(string surfaceId)
{
return new EveUnitySceneProviderSurfaceAdvertisement(
surfaceId,
"interactive-world",
new EveUnitySceneWorldInteraction(
"provider-authored-world-surface",
"aetheria.daemon.commands",
"aetheria.eve_command_acceptance_status.v1",
"provider-owns-world-state-assets-command-acceptance-and-receipts"));
}
private sealed class FakeProviderSurfaceSource : IEveUnitySceneProviderSurfaceSource
{
public FakeProviderSurfaceSource(
string providerId,
string surfaceId,
string sourcePointer,
EveUnitySceneProviderSurfaceSnapshot currentSnapshot)
{
ProviderId = providerId;
SurfaceId = surfaceId;
SourcePointer = sourcePointer;
CurrentSnapshot = currentSnapshot;
}
public string ProviderId { get; }
public string SurfaceId { get; }
public string SourcePointer { get; }
public EveUnitySceneProviderSurfaceSnapshot CurrentSnapshot { get; private set; }
public event Action<EveUnitySceneProviderSurfaceSnapshot>? SnapshotAvailable;
public void Connect()
{
}
public void Refresh()
{
}
public void Disconnect()
{
}
public void Stage(EveUnitySceneProviderSurfaceSnapshot snapshot)
{
CurrentSnapshot = snapshot;
}
public void Publish(EveUnitySceneProviderSurfaceSnapshot snapshot)
{
CurrentSnapshot = snapshot;
SnapshotAvailable?.Invoke(snapshot);
}
}
private sealed class FakeProviderSurfaceDocumentSource : IEveUnitySceneProviderSurfaceDocumentSource
{
public FakeProviderSurfaceDocumentSource(EveUnitySceneProviderSurfaceDocument currentDocument)
{
CurrentDocument = currentDocument;
}
public EveUnitySceneProviderSurfaceDocument CurrentDocument { get; private set; }
public event Action<EveUnitySceneProviderSurfaceDocument>? DocumentAvailable;
public void Publish(EveUnitySceneProviderSurfaceDocument document)
{
CurrentDocument = document;
DocumentAvailable?.Invoke(document);
}
}
private sealed class FakeCommandReceiptSource : IEveUnitySceneCommandReceiptSource
{
public event Action<EveUnitySceneCommandReceipt>? ReceiptAvailable;
public void Publish(EveUnitySceneCommandReceipt receipt)
{
ReceiptAvailable?.Invoke(receipt);
}
}
private sealed class FakeCommandSink : IEveUnitySceneCommandSink
{
public FakeCommandSink(string sinkKind)
{
SinkKind = sinkKind;
}
public string SinkKind { get; }
public List<EveSurfaceCommandRequest> Submitted { get; } = new List<EveSurfaceCommandRequest>();
public void Submit(EveSurfaceCommandRequest request)
{
Submitted.Add(request);
}
}
private sealed class FakePlayableWorldSceneSink : IEveUnityPlayableWorldSceneSink
{
public List<EveUnityPlayableWorldProjection> ConfiguredWorlds { get; } = new List<EveUnityPlayableWorldProjection>();
public List<(EveUnityPlayableWorldEntity entity, EveUnityPlayableWorldAssetBinding asset)> Upserts { get; } =
new List<(EveUnityPlayableWorldEntity entity, EveUnityPlayableWorldAssetBinding asset)>();
public List<string> RemovedEntityIds { get; } = new List<string>();
public void ConfigureWorld(EveUnityPlayableWorldProjection world)
{
ConfiguredWorlds.Add(world);
}
public void UpsertEntity(EveUnityPlayableWorldEntity entity, EveUnityPlayableWorldAssetBinding asset)
{
Upserts.Add((entity, asset));
}
public void RemoveEntity(string entityId)
{
RemovedEntityIds.Add(entityId);
}
}
private sealed class FakeAssetManifestSource : IEveUnityPlayableWorldAssetManifestSource
{
public FakeAssetManifestSource(EveUnityPlayableWorldAssetManifest currentManifest)
{
CurrentManifest = currentManifest;
}
public string ManifestRef => CurrentManifest.ManifestRef;
public EveUnityPlayableWorldAssetManifest CurrentManifest { get; private set; }
public event Action<EveUnityPlayableWorldAssetManifest>? ManifestAvailable;
public void Publish(EveUnityPlayableWorldAssetManifest manifest)
{
CurrentManifest = manifest;
ManifestAvailable?.Invoke(manifest);
}
}
private sealed class FakeAssetManifestDocumentSource : IEveUnityPlayableWorldAssetManifestDocumentSource
{
public FakeAssetManifestDocumentSource(EveUnityPlayableWorldAssetManifestDocument currentDocument)
{
CurrentDocument = currentDocument;
}
public string ManifestRef => CurrentDocument.ManifestRef;
public EveUnityPlayableWorldAssetManifestDocument CurrentDocument { get; private set; }
public event Action<EveUnityPlayableWorldAssetManifestDocument>? DocumentAvailable;
public void Publish(EveUnityPlayableWorldAssetManifestDocument document)
{
CurrentDocument = document;
DocumentAvailable?.Invoke(document);
}
}
private sealed class FakeLiveProviderTransport : IEveUnitySceneLiveProviderTransport
{
private Action<EveUnitySceneProviderSurfaceDocument>? _surfaceDocumentAvailable;
private Action<EveUnityPlayableWorldAssetManifestDocument>? _assetManifestDocumentAvailable;
private Action<EveUnitySceneCommandReceipt>? _commandReceiptAvailable;
public FakeLiveProviderTransport(
EveUnitySceneProviderSurfaceDocument surfaceDocument,
EveUnityPlayableWorldAssetManifestDocument assetManifestDocument)
{
CurrentSurfaceDocument = surfaceDocument;
CurrentAssetManifestDocument = assetManifestDocument;
}
public string TransportKind => "cultmesh-cultnet-provider-transport";
public string SurfacePointer => CurrentSurfaceDocument.SourcePointer;
public string AssetManifestPointer => CurrentAssetManifestDocument.ManifestRef;
public EveUnitySceneProviderSurfaceDocument CurrentSurfaceDocument { get; private set; }
public EveUnityPlayableWorldAssetManifestDocument CurrentAssetManifestDocument { get; private set; }
public List<EveSurfaceCommandRequest> Submitted { get; } = new List<EveSurfaceCommandRequest>();
public int ConnectCount { get; private set; }
public int DisconnectCount { get; private set; }
public int RefreshCount { get; private set; }
public bool FailNextConnect { get; set; }
public int SurfaceSubscriberCount => _surfaceDocumentAvailable?.GetInvocationList().Length ?? 0;
public int AssetManifestSubscriberCount => _assetManifestDocumentAvailable?.GetInvocationList().Length ?? 0;
public int ReceiptSubscriberCount => _commandReceiptAvailable?.GetInvocationList().Length ?? 0;
public event Action<EveUnitySceneProviderSurfaceDocument>? SurfaceDocumentAvailable
{
add => _surfaceDocumentAvailable += value;
remove => _surfaceDocumentAvailable -= value;
}
public event Action<EveUnityPlayableWorldAssetManifestDocument>? AssetManifestDocumentAvailable
{
add => _assetManifestDocumentAvailable += value;
remove => _assetManifestDocumentAvailable -= value;
}
public event Action<EveUnitySceneCommandReceipt>? CommandReceiptAvailable
{
add => _commandReceiptAvailable += value;
remove => _commandReceiptAvailable -= value;
}
public void Connect()
{
ConnectCount++;
if (!FailNextConnect) return;
FailNextConnect = false;
throw new IOException("transient transport failure");
}
public void Disconnect()
{
DisconnectCount++;
}
public void Refresh()
{
RefreshCount++;
}
public void SubmitCommand(EveSurfaceCommandRequest request)
{
Submitted.Add(request);
}
public void PublishSurface(EveUnitySceneProviderSurfaceDocument document)
{
CurrentSurfaceDocument = document;
_surfaceDocumentAvailable?.Invoke(document);
}
public void PublishAssetManifest(EveUnityPlayableWorldAssetManifestDocument document)
{
CurrentAssetManifestDocument = document;
_assetManifestDocumentAvailable?.Invoke(document);
}
public void PublishReceipt(EveUnitySceneCommandReceipt receipt)
{
_commandReceiptAvailable?.Invoke(receipt);
}
}
private static EveUnityPlayableWorldEntity PlayableEntityModel(
string entityId,
string assetRef,
float radius)
{
return new EveUnityPlayableWorldEntity(
entityId,
entityId,
"ship",
entityId,
"player",
assetRef,
0f, 0f, 0f,
0f,
radius,
true,
true,
"", "", "", "");
}
private static EveEntitySoaViewDocument EntityLeaseDocument() => new EveEntitySoaViewDocument
{
ProviderId = "provider",
ViewId = "entities",
BodySchemaId = "test.entity.slab.v1",
LayoutVersion = 3,
ProducerEpoch = 5,
Sequence = 7,
Capacity = 1,
Buffers = new[] { new EveEntitySoaBuffer { BufferId = "hot", ByteLength = 4 } },
Identities = new[]
{
new EveEntityIdentity
{
Index = 12,
EntityId = "entity:pilot",
EntityKind = "player",
Label = "Pilot",
Faction = "alliance",
Selectable = true,
Controllable = true,
AssetRef = "cultmesh://assets/pilot"
}
},
Columns = new[]
{
new EveEntitySoaColumn
{
ColumnId = "entity-index",
Semantic = "entity.index",
BufferId = "hot",
ScalarType = "int32",
ElementStride = 4,
ElementCount = 1
}
}
};
private static CultMeshRealtimeFrame RealtimeFrame(string bodyId, long epoch, long sequence) =>
new CultMeshRealtimeFrame
{
ChannelId = "state",
BodyId = bodyId,
SchemaId = "test.entity.slab.v1",
ProducerEpoch = epoch,
Sequence = sequence,
Delivery = CultMeshRealtimeDelivery.LatestOnly,
Payload = new byte[4]
};
private static EveProviderAdvertisementDocument ProviderAdvertisement(
string providerId,
string serviceId,
params string[] authorizedBodyProducerIds) =>
new EveProviderAdvertisementDocument(
providerId,
serviceId,
"test-verse",
"Test Provider",
"test",
"cultmesh://test/provider",
DateTimeOffset.UtcNow.ToString("O"),
new EveProviderFreshness("fresh", DateTimeOffset.UtcNow.ToString("O"), 5000),
Array.Empty<string>(),
Array.Empty<EveProviderWitness>(),
Array.Empty<EveAdvertisedSurface>(),
Array.Empty<EveAdvertisedCommand>(),
authorizedBodyProducerIds);
private static CultMeshBodyPublicationDocument BodyPublication(
EveEntitySoaViewDocument view,
long sequence)
{
var descriptor = FakeBodyReadLease.DescriptorFor(view, view.Buffers[0].ByteLength);
descriptor.Sequence = sequence;
descriptor.Synchronization = CultMeshBodySynchronization.ImmutableSequence;
descriptor.LeaseExpiresAtUnixMs = DateTimeOffset.UtcNow.AddMinutes(1).ToUnixTimeMilliseconds();
descriptor.TransportKind = CultMeshBodyTransportKind.SharedFileMapping;
var network = FakeBodyReadLease.DescriptorFor(view, view.Buffers[0].ByteLength);
network.Sequence = sequence;
network.Synchronization = descriptor.Synchronization;
network.LeaseExpiresAtUnixMs = descriptor.LeaseExpiresAtUnixMs;
network.TransportKind = CultMeshBodyTransportKind.Network;
return new CultMeshBodyPublicationDocument
{
BodyId = descriptor.BodyId,
ProducerId = view.ProviderId,
SchemaId = descriptor.SchemaId,
LayoutVersion = descriptor.LayoutVersion,
ByteSize = descriptor.ByteSize,
Capacity = descriptor.Capacity,
ProducerEpoch = descriptor.ProducerEpoch,
Sequence = sequence,
Synchronization = descriptor.Synchronization,
LivenessExpiresAtUnixMs = descriptor.LeaseExpiresAtUnixMs,
Representations = new[] { descriptor, network }
};
}
private static EveEntitySoaColumn SoaColumn(
string semantic,
string scalarType,
long byteOffset,
int stride) => new EveEntitySoaColumn
{
ColumnId = semantic,
Semantic = semantic,
BufferId = "hot",
ScalarType = scalarType,
ByteOffset = byteOffset,
ElementStride = stride,
ElementCount = 1
};
private static EveUnityPresentedEntity PresentedEntity(int index, string entityId, string assetRef) =>
new EveUnityPresentedEntity(
index, entityId, "entity", entityId, "", Vector3.zero, 0f, Vector3.zero,
1f, 1f, 0, 0, true, false, assetRef);
private sealed class FakeBodyReadLease : ICultMeshBodyReadLease
{
private readonly byte[] _bytes;
public FakeBodyReadLease(EveEntitySoaViewDocument document, byte[] bytes)
: this(DescriptorFor(document, bytes.Length), bytes) { }
public FakeBodyReadLease(CultMeshBodyDescriptor descriptor, byte[] bytes)
{
Descriptor = descriptor;
_bytes = bytes;
}
public CultMeshBodyDescriptor Descriptor { get; }
public CultMeshBodyTransportKind TransportKind => CultMeshBodyTransportKind.Network;
public bool Disposed { get; private set; }
public byte ReadByte(long offset) => _bytes[checked((int)offset)];
public int ReadInt32(long offset) => BitConverter.ToInt32(_bytes, checked((int)offset));
public long ReadInt64(long offset) => BitConverter.ToInt64(_bytes, checked((int)offset));
public float ReadSingle(long offset) => BitConverter.ToSingle(_bytes, checked((int)offset));
public double ReadDouble(long offset) => BitConverter.ToDouble(_bytes, checked((int)offset));
public int CopyTo(long offset, byte[] destination, int destinationOffset, int count)
{
Buffer.BlockCopy(_bytes, checked((int)offset), destination, destinationOffset, count);
return count;
}
public void Dispose() => Disposed = true;
public static CultMeshBodyDescriptor DescriptorFor(EveEntitySoaViewDocument document, long byteSize) =>
new CultMeshBodyDescriptor
{
BodyId = document.Buffers[0].BufferId,
SchemaId = document.BodySchemaId,
LayoutVersion = document.LayoutVersion,
ByteSize = byteSize,
Capacity = document.Capacity,
ProducerEpoch = document.ProducerEpoch,
Sequence = document.Sequence
};
}
private sealed class FixedGameObjectAssetProvider : IEveUnityGameObjectAssetProvider
{
private readonly GameObject? _prefab;
public FixedGameObjectAssetProvider(GameObject? prefab)
{
_prefab = prefab;
}
public GameObject? ResolvePrefab(EveUnityPlayableWorldAssetBinding asset) => _prefab;
}
private sealed class FixedNativeAssetProviderComponent : MonoBehaviour, IEveUnityNativeAssetProvider
{
private readonly Dictionary<string, UnityEngine.Object> _assets =
new Dictionary<string, UnityEngine.Object>(StringComparer.Ordinal);
public EveUnityPlayableWorldAssetBinding? LastBinding { get; private set; }
public void Set(string assetRef, UnityEngine.Object asset)
{
_assets[assetRef ?? ""] = asset;
}
public void Clear() => _assets.Clear();
public GameObject? ResolvePrefab(EveUnityPlayableWorldAssetBinding asset) => null;
public UnityEngine.Object? ResolveAsset(EveUnityPlayableWorldAssetBinding asset, Type assetType)
{
LastBinding = asset;
return _assets.TryGetValue(asset?.AssetRef ?? "", out var value) && assetType.IsInstanceOfType(value)
? value
: null;
}
}
private sealed class ObservingGameObjectAssetProvider : IEveUnityGameObjectAssetProvider
{
private readonly Action _onResolve;
public ObservingGameObjectAssetProvider(Action onResolve)
{
_onResolve = onResolve;
}
public GameObject? ResolvePrefab(EveUnityPlayableWorldAssetBinding asset)
{
_onResolve();
return null;
}
}
private sealed class FixedRenderChannelPolicy : IEveUnityCameraRenderPolicySource
{
private readonly string _channel;
private readonly int _layer;
public FixedRenderChannelPolicy(string channel, int layer)
{
_channel = channel;
_layer = layer;
}
public bool TryGetRenderChannelLayer(string channel, out int layer)
{
layer = _layer;
return string.Equals(channel, _channel, StringComparison.Ordinal);
}
}
private sealed class FakePlayableWorldProviderComponent :
MonoBehaviour,
IEveUnitySceneProviderSurfaceDocumentSource,
IEveUnityPlayableWorldAssetManifestDocumentSource,
IEveUnitySceneCommandSink,
IEveUnitySceneCommandReceiptSource,
IEveUnityProviderRefreshSource,
IEveUnityInputCapabilitySource,
IEveUnitySceneProviderSurfaceDocumentConnection,
IEveUnityNavigableProvider
{
public List<EveSurfaceCommandRequest> Submitted { get; } = new List<EveSurfaceCommandRequest>();
public int RefreshCount { get; private set; }
public EveUnitySceneNavigationTarget? LastNavigation { get; private set; }
public int CommitNavigationCount { get; private set; }
public int FinalizeNavigationCount { get; private set; }
public int RollbackNavigationCount { get; private set; }
public bool FailNextNavigationMount { get; set; }
public bool FailNextNavigationFinalize { get; set; }
public int ActiveConnectionLeases { get; private set; }
public string ActiveRoute { get; private set; } = "original";
public Func<bool>? PreviousPresentationActive { get; set; }
public bool? PreviousPresentationActiveDuringCommit { get; private set; }
public bool? PreviousPresentationActiveDuringRollback { get; private set; }
private bool _failSurfaceRead;
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<EveUnityPlayableWorldAssetManifestDocumentEntry>(),
"aetheria");
public EveInputCapabilityDocument CurrentInputCapability { get; private set; } =
new EveInputCapabilityDocument();
EveUnitySceneProviderSurfaceDocument IEveUnitySceneProviderSurfaceDocumentSource.CurrentDocument =>
_failSurfaceRead
? throw new InvalidOperationException("Deliberate candidate surface read failure.")
: CurrentSurfaceDocument;
public event Action<EveUnitySceneProviderSurfaceDocument>? DocumentAvailable;
public event Action<EveUnityPlayableWorldAssetManifestDocument>? AssetManifestDocumentAvailable;
event Action<EveUnityPlayableWorldAssetManifestDocument> IEveUnityPlayableWorldAssetManifestDocumentSource.DocumentAvailable
{
add => AssetManifestDocumentAvailable += value;
remove => AssetManifestDocumentAvailable -= value;
}
public event Action<EveUnitySceneCommandReceipt>? ReceiptAvailable;
public void Set(
EveUnitySceneProviderSurfaceDocument surfaceDocument,
EveUnityPlayableWorldAssetManifestDocument assetManifest,
EveInputCapabilityDocument? inputCapability = null)
{
CurrentSurfaceDocument = surfaceDocument;
CurrentDocument = assetManifest;
CurrentInputCapability = inputCapability ?? new EveInputCapabilityDocument();
}
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);
}
public Task NavigateAsync(EveUnitySceneNavigationTarget target)
{
LastNavigation = target;
_failSurfaceRead = FailNextNavigationMount;
FailNextNavigationMount = false;
return Task.CompletedTask;
}
public void Connect()
{
ActiveConnectionLeases++;
}
public void Disconnect()
{
if (ActiveConnectionLeases > 0)
ActiveConnectionLeases--;
}
public void CommitNavigation()
{
PreviousPresentationActiveDuringCommit = PreviousPresentationActive?.Invoke();
CommitNavigationCount++;
ActiveRoute = "candidate";
_failSurfaceRead = false;
}
public void FinalizeNavigation()
{
FinalizeNavigationCount++;
if (FailNextNavigationFinalize)
{
FailNextNavigationFinalize = false;
throw new InvalidOperationException("Deliberate provider finalization failure.");
}
}
public void RollbackNavigation()
{
RollbackNavigationCount++;
ActiveRoute = "original";
_failSurfaceRead = false;
PreviousPresentationActiveDuringRollback = PreviousPresentationActive?.Invoke();
}
}
}
}