Geometry: reference members are CultRecordRef<T>, not [CultReference] strings
e420410f (D11) refuses a reference member whose CLR shape the cache cannot
walk; many:false needs CultRecordRef<T>. CultGeometryBuildRequest.DomainKey,
CultGeometrySelectedCutManifest.RequestKey and CultGeometryChunkArtifact.CutKey
were strings marked [CultReference], so registering the geometry catalog threw
and 7 geometry tests failed on main. All three are record keys of another
document, so the members become CultRecordRef<T> (the attribute is dropped, the
type declares the reference). The msgpack formatter writes a CultRecordRef as
its key string, so the wire bytes and the fingerprint inputs are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
15c6a7e2d4
commit
0479304d0b
2 changed files with 28 additions and 11 deletions
|
|
@ -153,8 +153,7 @@ namespace GameCult.Geometry
|
|||
public string RequestId { get; set; } = string.Empty;
|
||||
|
||||
[Key(1)]
|
||||
[CultReference(typeof(CultGeometryDomainDocument))]
|
||||
public string DomainKey { get; set; } = string.Empty;
|
||||
public CultRecordRef<CultGeometryDomainDocument> DomainKey { get; set; }
|
||||
|
||||
[Key(2)]
|
||||
[CultIndex]
|
||||
|
|
@ -200,7 +199,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 +227,7 @@ namespace GameCult.Geometry
|
|||
public string CutId { get; set; } = string.Empty;
|
||||
|
||||
[Key(1)]
|
||||
[CultReference(typeof(CultGeometryBuildRequest))]
|
||||
public string RequestKey { get; set; } = string.Empty;
|
||||
public CultRecordRef<CultGeometryBuildRequest> RequestKey { get; set; }
|
||||
|
||||
[Key(2)]
|
||||
public string[] SelectedNodes { get; set; } = Array.Empty<string>();
|
||||
|
|
@ -247,7 +245,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 +311,7 @@ namespace GameCult.Geometry
|
|||
public string ChunkId { get; set; } = string.Empty;
|
||||
|
||||
[Key(1)]
|
||||
[CultReference(typeof(CultGeometrySelectedCutManifest))]
|
||||
public string CutKey { get; set; } = string.Empty;
|
||||
public CultRecordRef<CultGeometrySelectedCutManifest> CutKey { get; set; }
|
||||
|
||||
[Key(2)]
|
||||
[CultIndex]
|
||||
|
|
@ -357,7 +354,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),
|
||||
|
|
|
|||
|
|
@ -104,7 +104,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 +129,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 +402,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