From a6f00c6ac0b4d47316b17957918bc88fe0a3c8bb Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 13 Jul 2026 18:06:15 +0100 Subject: [PATCH] Restore entity identity semantics in SoA v2 --- .../Runtime/EveEntitySoaViewDocument.cs | 6 +++ .../Runtime/EveUnityEntitySoaPresenter.cs | 12 +++--- .../EveUnitySceneSurfaceLowererTests.cs | 42 +++++++++++++++++++ ...eSurfaceSerializationCompatibilityTests.cs | 25 ++++++++++- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/packages/org.gamecult.eve.surface/Runtime/EveEntitySoaViewDocument.cs b/packages/org.gamecult.eve.surface/Runtime/EveEntitySoaViewDocument.cs index 961ea2f..d5148f1 100644 --- a/packages/org.gamecult.eve.surface/Runtime/EveEntitySoaViewDocument.cs +++ b/packages/org.gamecult.eve.surface/Runtime/EveEntitySoaViewDocument.cs @@ -26,6 +26,7 @@ namespace GameCult.Eve.Surface [Key(11)] public EveEntitySoaDirtyRange[] DirtyRanges { get; set; } = Array.Empty(); [Key(12)] public EveEntityRenderGroup[] RenderGroups { get; set; } = Array.Empty(); [Key(13)] public EveEntityIdentity[] Identities { get; set; } = Array.Empty(); + [Key(14)] public long FrameId { get; set; } } [MessagePackObject] @@ -65,6 +66,11 @@ namespace GameCult.Eve.Surface [Key(0)] public int Index { get; set; } [Key(1)] public string EntityId { get; set; } = ""; [Key(2)] public string EntityKind { get; set; } = ""; + [Key(3)] public string Label { get; set; } = ""; + [Key(4)] public string Faction { get; set; } = ""; + [Key(5)] public bool Selectable { get; set; } + [Key(6)] public bool Controllable { get; set; } + [Key(7)] public string AssetRef { get; set; } = ""; } [MessagePackObject] diff --git a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityEntitySoaPresenter.cs b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityEntitySoaPresenter.cs index 17a89a0..eea519b 100644 --- a/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityEntitySoaPresenter.cs +++ b/packages/org.gamecult.eve.unity-scene/Runtime/EveUnityEntitySoaPresenter.cs @@ -40,22 +40,24 @@ namespace GameCult.Eve.UnityScene var identity = document.Identities.FirstOrDefault(item => item.Index == entityIndex); var entityId = identity?.EntityId ?? $"entity:{entityIndex}"; next.Add(entityId); - var assetRef = renderGroup?.MeshAssetRef ?? ""; + var assetRef = string.IsNullOrWhiteSpace(identity?.AssetRef) + ? renderGroup?.MeshAssetRef ?? "" + : identity!.AssetRef; _sink.UpsertEntity( new EveUnityPlayableWorldEntity( entityId, entityId, identity?.EntityKind ?? "entity", - entityId, - "", + string.IsNullOrWhiteSpace(identity?.Label) ? entityId : identity!.Label, + identity?.Faction ?? "", assetRef, position.x, position.y, position.z, rotationRadians * 57.2957795f, scale > 0f ? scale : renderGroup?.DefaultScale ?? 1f, - selectable: true, - controllable: false, + selectable: identity?.Selectable ?? true, + controllable: identity?.Controllable ?? false, focusCommand: "", moveCommand: "", targetCommand: "", diff --git a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs index a153dee..37203c4 100644 --- a/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs +++ b/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using GameCult.Eve.Surface; using GameCult.Mesh; using NUnit.Framework; @@ -66,6 +67,33 @@ namespace GameCult.Eve.UnityScene.Tests 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 LowerCarriesProviderAdvertisedWorldBoundary() { @@ -1771,6 +1799,20 @@ namespace GameCult.Eve.UnityScene.Tests 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 diff --git a/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs b/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs index 6b348ab..a756e74 100644 --- a/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs +++ b/tests/GameCult.Eve.Surface.Serialization.Tests/EveSurfaceSerializationCompatibilityTests.cs @@ -21,16 +21,37 @@ public sealed class EveSurfaceSerializationCompatibilityTests ProducerEpoch = 3, Sequence = 4, Capacity = 8, - Buffers = new[] { new EveEntitySoaBuffer { BufferId = "entity-hot", ByteLength = 64 } } + FrameId = 91, + Buffers = new[] { new EveEntitySoaBuffer { BufferId = "entity-hot", ByteLength = 64 } }, + Identities = new[] + { + new EveEntityIdentity + { + Index = 0, + EntityId = "player", + EntityKind = "ship", + Label = "Vanguard", + Faction = "alliance", + Selectable = true, + Controllable = true, + AssetRef = "cultmesh://assets/player" + } + } }; var bytes = MessagePackSerializer.Serialize(document, Options); var reader = new MessagePackReader(bytes); var restored = MessagePackSerializer.Deserialize(bytes, Options); - Assert.That(reader.ReadArrayHeader(), Is.EqualTo(14)); + Assert.That(reader.ReadArrayHeader(), Is.EqualTo(15)); Assert.That(restored.Schema, Is.EqualTo(EveEntitySoaViewDocument.SchemaId)); Assert.That(restored.Buffers[0].BufferId, Is.EqualTo("entity-hot")); + Assert.That(restored.FrameId, Is.EqualTo(91)); + Assert.That(restored.Identities[0].Label, Is.EqualTo("Vanguard")); + Assert.That(restored.Identities[0].Faction, Is.EqualTo("alliance")); + Assert.That(restored.Identities[0].Selectable, Is.True); + Assert.That(restored.Identities[0].Controllable, Is.True); + Assert.That(restored.Identities[0].AssetRef, Is.EqualTo("cultmesh://assets/player")); } [Test]