Merge Geometry references as CultRecordRef<T>
DomainKey, RequestKey and CutKey were plain strings marked [CultReference];
since e420410f (D11) the cache refuses a reference it cannot walk, and
every cache that registered the geometry catalog threw (7 tests red since
2026-09-22). The wire is unchanged. Geometry gains a reference to
GameCult.Caching.MessagePack for the formatter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
a6720b620f
3 changed files with 33 additions and 11 deletions
|
|
@ -153,8 +153,8 @@ namespace GameCult.Geometry
|
|||
public string RequestId { get; set; } = string.Empty;
|
||||
|
||||
[Key(1)]
|
||||
[CultReference(typeof(CultGeometryDomainDocument))]
|
||||
public string DomainKey { get; set; } = string.Empty;
|
||||
[MessagePackFormatter(typeof(CultRecordRefFormatter<CultGeometryDomainDocument>))]
|
||||
public CultRecordRef<CultGeometryDomainDocument> DomainKey { get; set; }
|
||||
|
||||
[Key(2)]
|
||||
[CultIndex]
|
||||
|
|
@ -200,7 +200,7 @@ namespace GameCult.Geometry
|
|||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
return new CultRecordKey("geometry:request:" + StableHash(
|
||||
request.DomainKey,
|
||||
request.DomainKey.Key.Value,
|
||||
request.WorkerGroup,
|
||||
StableVector(request.CameraPosition),
|
||||
StableVector(request.FrustumMin),
|
||||
|
|
@ -228,8 +228,8 @@ namespace GameCult.Geometry
|
|||
public string CutId { get; set; } = string.Empty;
|
||||
|
||||
[Key(1)]
|
||||
[CultReference(typeof(CultGeometryBuildRequest))]
|
||||
public string RequestKey { get; set; } = string.Empty;
|
||||
[MessagePackFormatter(typeof(CultRecordRefFormatter<CultGeometryBuildRequest>))]
|
||||
public CultRecordRef<CultGeometryBuildRequest> RequestKey { get; set; }
|
||||
|
||||
[Key(2)]
|
||||
public string[] SelectedNodes { get; set; } = Array.Empty<string>();
|
||||
|
|
@ -247,7 +247,7 @@ namespace GameCult.Geometry
|
|||
{
|
||||
if (cut == null) throw new ArgumentNullException(nameof(cut));
|
||||
return new CultRecordKey("geometry:cut:" + StableHash(
|
||||
cut.RequestKey,
|
||||
cut.RequestKey.Key.Value,
|
||||
cut.CutId,
|
||||
StableArray(cut.SelectedNodes),
|
||||
StableArray(cut.DeferredChildRequests),
|
||||
|
|
@ -313,8 +313,8 @@ namespace GameCult.Geometry
|
|||
public string ChunkId { get; set; } = string.Empty;
|
||||
|
||||
[Key(1)]
|
||||
[CultReference(typeof(CultGeometrySelectedCutManifest))]
|
||||
public string CutKey { get; set; } = string.Empty;
|
||||
[MessagePackFormatter(typeof(CultRecordRefFormatter<CultGeometrySelectedCutManifest>))]
|
||||
public CultRecordRef<CultGeometrySelectedCutManifest> CutKey { get; set; }
|
||||
|
||||
[Key(2)]
|
||||
[CultIndex]
|
||||
|
|
@ -357,7 +357,7 @@ namespace GameCult.Geometry
|
|||
{
|
||||
if (chunk == null) throw new ArgumentNullException(nameof(chunk));
|
||||
return new CultRecordKey("geometry:chunk:" + StableHash(
|
||||
chunk.CutKey,
|
||||
chunk.CutKey.Key.Value,
|
||||
chunk.ChunkId,
|
||||
chunk.SelectedCutId,
|
||||
StableArray(chunk.SourceDomainKeys),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\packages\cultmath\src\CultMath\CultMath.csproj" />
|
||||
<ProjectReference Include="..\GameCult.Caching\GameCult.Caching.csproj" />
|
||||
<ProjectReference Include="..\GameCult.Caching.MessagePack\GameCult.Caching.MessagePack.csproj" />
|
||||
<PackageReference Include="MessagePack" Version="3.1.7" />
|
||||
<PackageReference Include="System.Text.Json" Version="9.0.10" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using R3;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -104,7 +105,7 @@ namespace GameCult.Geometry.Tests
|
|||
var domainKey = CultGeometryDomainDocument.CreateRecordKey(domain);
|
||||
var request = new CultGeometryBuildRequest
|
||||
{
|
||||
DomainKey = domainKey.Value,
|
||||
DomainKey = new CultRecordRef<CultGeometryDomainDocument>(domainKey),
|
||||
WorkerGroup = "ragnarok-column-workers",
|
||||
CameraPosition = [36f, -42f, 30f],
|
||||
FrustumMin = [-75f, -75f, -30f],
|
||||
|
|
@ -129,6 +130,26 @@ namespace GameCult.Geometry.Tests
|
|||
first.Value.Should().StartWith("geometry:request:");
|
||||
}
|
||||
|
||||
// The geometry documents reference each other by record key. The reference members are
|
||||
// CultRecordRef<T> so the cache can walk them, and they stay a bare msgpack string on the wire.
|
||||
[Test]
|
||||
public void GeometryReferences_AreWalkableByTheCache_AndStayStringsOnTheWire()
|
||||
{
|
||||
var cache = new CultCache();
|
||||
var descriptor = cache.Registry.GetRequired<CultGeometryBuildRequest>();
|
||||
descriptor.DeclaredMembers.Should().Contain(m => m.MemberName == "DomainKey" && m.IsReference && !m.IsMany);
|
||||
|
||||
var chunk = SampleChunk();
|
||||
var references = cache.Registry.GetRequired<CultGeometryChunkArtifact>().ReferencesOf(chunk, "CutKey").ToArray();
|
||||
references.Should().ContainSingle().Which.Target.Value.Should().Be("geometry:cut:test");
|
||||
|
||||
var payload = CultDocumentMessagePackSerialization.SerializeUntyped(chunk, typeof(CultGeometryChunkArtifact));
|
||||
var reader = new MessagePackReader(payload);
|
||||
reader.ReadArrayHeader();
|
||||
reader.ReadString().Should().Be(chunk.ChunkId);
|
||||
reader.ReadString().Should().Be("geometry:cut:test");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChunkArtifact_RoundTrips_ThroughCultCacheMessagePack()
|
||||
{
|
||||
|
|
@ -382,7 +403,7 @@ namespace GameCult.Geometry.Tests
|
|||
return new CultGeometryChunkArtifact
|
||||
{
|
||||
ChunkId = "chunk/ragnarok-column/column-00",
|
||||
CutKey = "geometry:cut:test",
|
||||
CutKey = new CultRecordRef<CultGeometrySelectedCutManifest>(new CultRecordKey("geometry:cut:test")),
|
||||
SelectedCutId = "cut-test",
|
||||
BoundsMin = [-1f, -1f, 0f],
|
||||
BoundsMax = [1f, 1f, 1f],
|
||||
|
|
|
|||
Loading…
Reference in a new issue