Restore entity identity semantics in SoA v2

This commit is contained in:
Meta Vens 2026-07-13 18:06:15 +01:00
commit a6f00c6ac0
4 changed files with 78 additions and 7 deletions

View file

@ -26,6 +26,7 @@ namespace GameCult.Eve.Surface
[Key(11)] public EveEntitySoaDirtyRange[] DirtyRanges { get; set; } = Array.Empty<EveEntitySoaDirtyRange>();
[Key(12)] public EveEntityRenderGroup[] RenderGroups { get; set; } = Array.Empty<EveEntityRenderGroup>();
[Key(13)] public EveEntityIdentity[] Identities { get; set; } = Array.Empty<EveEntityIdentity>();
[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]

View file

@ -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: "",

View file

@ -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

View file

@ -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<EveEntitySoaViewDocument>(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]