diff --git a/src/GameCult.Geometry/CultGeometryDocuments.cs b/src/GameCult.Geometry/CultGeometryDocuments.cs index 7a1677b3..306b5849 100644 --- a/src/GameCult.Geometry/CultGeometryDocuments.cs +++ b/src/GameCult.Geometry/CultGeometryDocuments.cs @@ -411,7 +411,13 @@ namespace GameCult.Geometry public static string StableVector(float[] values) { - return string.Join(",", values.Select(value => value.ToString("R", CultureInfo.InvariantCulture))); + return string.Join(",", values.Select(StableFloat)); + } + + public static string StableFloat(float value) + { + return BitConverter.ToUInt32(BitConverter.GetBytes(value), 0) + .ToString("x8", CultureInfo.InvariantCulture); } public static string StableArray(string[] values) diff --git a/tests/GameCult.Geometry.Tests/Fixtures/vg-csg-ragnarok/ragnarok-domain.msgpack b/tests/GameCult.Geometry.Tests/Fixtures/vg-csg-ragnarok/ragnarok-domain.msgpack new file mode 100644 index 00000000..79c5f5b6 Binary files /dev/null and b/tests/GameCult.Geometry.Tests/Fixtures/vg-csg-ragnarok/ragnarok-domain.msgpack differ diff --git a/tests/GameCult.Geometry.Tests/Fixtures/vg-csg-ragnarok/ragnarok-first-chunk.msgpack b/tests/GameCult.Geometry.Tests/Fixtures/vg-csg-ragnarok/ragnarok-first-chunk.msgpack new file mode 100644 index 00000000..4cfcf493 Binary files /dev/null and b/tests/GameCult.Geometry.Tests/Fixtures/vg-csg-ragnarok/ragnarok-first-chunk.msgpack differ diff --git a/tests/GameCult.Geometry.Tests/GameCult.Geometry.Tests.csproj b/tests/GameCult.Geometry.Tests/GameCult.Geometry.Tests.csproj index fe7fd8c1..407f5305 100644 --- a/tests/GameCult.Geometry.Tests/GameCult.Geometry.Tests.csproj +++ b/tests/GameCult.Geometry.Tests/GameCult.Geometry.Tests.csproj @@ -12,6 +12,10 @@ + + + + diff --git a/tests/GameCult.Geometry.Tests/GeometryDocumentTests.cs b/tests/GameCult.Geometry.Tests/GeometryDocumentTests.cs index e3938284..1723b852 100644 --- a/tests/GameCult.Geometry.Tests/GeometryDocumentTests.cs +++ b/tests/GameCult.Geometry.Tests/GeometryDocumentTests.cs @@ -82,15 +82,41 @@ namespace GameCult.Geometry.Tests claim.StableFingerprint().Should().Be(string.Join('\u001e', "column-support-shell", - "0,0,0", - "0,0,0,1", - "0,0,45", - "18,18,96", + "00000000,00000000,00000000", + "00000000,00000000,00000000,3f800000", + "00000000,00000000,42340000", + "41900000,41900000,42c00000", "SupportShell", "10", "RenderAndCollider")); } + [Test] + public void RustExportedDomainDocument_DecodesWithStableCultCacheKey() + { + var payload = File.ReadAllBytes(FixturePath("ragnarok-domain.msgpack")); + var domain = CultDocumentMessagePackSerialization.Deserialize(payload); + + domain.DomainId.Should().Be("ragnarok-column"); + domain.Root.Children.Should().NotBeEmpty(); + domain.Root.Children[0].Name.Should().Be("stellarator-column-00"); + CultGeometryDomainDocument.CreateRecordKey(domain).Value + .Should().Be("geometry:domain:02c9b5810977406b0c206f3a3494327a423abb9448192be1b9a1863cd0f2ed95"); + } + + [Test] + public void RustExportedChunkArtifact_DecodesWithStableCultCacheKey() + { + var payload = File.ReadAllBytes(FixturePath("ragnarok-first-chunk.msgpack")); + var chunk = CultDocumentMessagePackSerialization.Deserialize(payload); + + chunk.ChunkId.Should().StartWith("chunk/"); + chunk.RenderMesh.TriangleCount.Should().BeGreaterThan(0); + chunk.SourceDomainKeys.Should().NotBeEmpty(); + CultGeometryChunkArtifact.CreateRecordKey(chunk).Value + .Should().Be("geometry:chunk:b94dcd7d6776df15d0fa5fcf7f4b24f1384876b13b2d38cc57c4468136521af5"); + } + [Test] public async Task ChunkArtifact_Replicates_ThroughCultNetRawDocumentLane() { @@ -273,6 +299,15 @@ namespace GameCult.Geometry.Tests }; } + private static string FixturePath(string fileName) + { + return Path.Combine( + TestContext.CurrentContext.TestDirectory, + "Fixtures", + "vg-csg-ragnarok", + fileName); + } + private static CultGeometryChunkArtifact SampleChunk() { return new CultGeometryChunkArtifact