Resolve live bodies through negotiated sessions

This commit is contained in:
Meta Vens 2026-07-19 13:57:56 +01:00
commit 6aeb416cf1
3 changed files with 12 additions and 69 deletions

View file

@ -35,7 +35,6 @@ namespace GameCult.Eve.UnityScene
typeof(EveEntitySoaViewDocument),
typeof(EveFieldsSplatsDocument),
typeof(CultMeshBodyPublicationDocument),
typeof(CultMeshNetworkBodyDocument),
typeof(CultMeshCdnArtifactManifest),
typeof(CultMeshContentTransferStateDocument)
};
@ -501,13 +500,20 @@ namespace GameCult.Eve.UnityScene
private CultMeshBodyPublicationResolver CreateBodyResolver()
{
var producerIds = RequireAdvertisedBodyProducerIds(_advertisement);
if (string.IsNullOrWhiteSpace(_advertisement!.ServiceId))
throw new InvalidOperationException(
$"Eve provider '{_advertisement.ProviderId}' has no service-instance identity for body transport.");
var mappedRoot = Path.GetDirectoryName(_replicaPath) ?? ".";
var networkBodies = _meshClient!.BodyProvider(
_advertisement.ProviderId,
_advertisement.ServiceId,
new CultMeshSessionBodyProviderOptions { ResponseTimeout = TimeSpan.FromSeconds(2) });
return new CultMeshBodyPublicationResolver(new CultMeshBodyTransportService(
new ICultMeshBodyTransportAdapter[]
{
new CultMeshSharedMemoryBodyAdapter(),
new CultMeshMappedBodyAdapter(mappedRoot),
new CultMeshNetworkBodyAdapter(ReadNetworkBody)
new CultMeshNetworkBodyAdapter(networkBodies)
},
(candidateProducerId, _) => IsAdvertisedBodyProducer(producerIds, candidateProducerId)));
}
@ -532,47 +538,6 @@ namespace GameCult.Eve.UnityScene
return producerIds;
}
private byte[] ReadNetworkBody(CultMeshBodyDescriptor descriptor)
{
var binding = _snapshot!
.FetchDocumentsAsync<CultMeshNetworkBodyDocument>(
recordKeys: new[] { CultMeshNetworkBodyDocument.CreateRecordKey(descriptor.CapabilityToken).Value },
schemaIds: new[] { CultMeshBodyPublicationSchemaVersions.NetworkBody })
.GetAwaiter().GetResult().SingleOrDefault()
?? throw new FileNotFoundException("Provider network body capability is missing.", descriptor.CapabilityToken);
var manifest = _snapshot
.FetchDocumentsAsync<CultMeshCdnArtifactManifest>(
recordKeys: new[] { binding.ManifestRecordKey },
schemaIds: new[] { CultMeshCdnSchemaVersions.ArtifactManifest })
.GetAwaiter().GetResult().SingleOrDefault()
?? throw new FileNotFoundException("Provider network body manifest is missing.", binding.ManifestRecordKey);
ValidateNetworkBody(descriptor, binding, manifest);
var path = ContentTransfer().FetchAsync(manifest).GetAwaiter().GetResult();
return File.ReadAllBytes(path);
}
private static void ValidateNetworkBody(
CultMeshBodyDescriptor descriptor,
CultMeshNetworkBodyDocument binding,
CultMeshCdnArtifactManifest manifest)
{
if (!string.Equals(binding.CapabilityToken, descriptor.CapabilityToken, StringComparison.Ordinal) ||
!string.Equals(binding.BodyId, descriptor.BodyId, StringComparison.Ordinal) ||
!string.Equals(binding.SchemaId, descriptor.SchemaId, StringComparison.Ordinal) ||
binding.LayoutVersion != descriptor.LayoutVersion || binding.ByteSize != descriptor.ByteSize ||
binding.Capacity != descriptor.Capacity || binding.ProducerEpoch != descriptor.ProducerEpoch ||
binding.Sequence != descriptor.Sequence || binding.Synchronization != descriptor.Synchronization ||
binding.LeaseExpiresAtUnixMs != descriptor.LeaseExpiresAtUnixMs ||
!string.Equals(binding.SemanticHash, descriptor.SemanticHash, StringComparison.Ordinal))
throw new InvalidDataException("CultMesh network body descriptor disagrees with its capability binding.");
if (!string.Equals(CultMeshCdnArtifactManifest.CreateRecordKey(manifest).Value,
binding.ManifestRecordKey, StringComparison.Ordinal) ||
manifest.SizeBytes != binding.ByteSize ||
!string.Equals(NormalizeContentHash(manifest.ContentHash),
binding.SemanticHash, StringComparison.Ordinal))
throw new InvalidDataException("CultMesh network body manifest disagrees with its capability binding.");
}
private static string NormalizeContentHash(string value)
{
if (string.IsNullOrWhiteSpace(value))

View file

@ -200,36 +200,14 @@ namespace GameCult.Eve.UnityScene.Tests
}
[Test]
public async System.Threading.Tasks.Task LiveTransportValidatesNetworkBodyControlDocumentsWithoutSnapshotChunks()
public void LiveTransportDoesNotRegisterBodyBytesAsSnapshotDocuments()
{
var view = EntityLeaseDocument();
var bytes = Enumerable.Range(0, 32).Select(value => (byte)value).ToArray();
var source = new CultCache();
var generation = new CultMeshBodyGeneration
{
BodyId = view.Buffers[0].BufferId,
ProducerId = view.ProviderId,
SchemaId = view.BodySchemaId,
LayoutVersion = view.LayoutVersion,
Capacity = view.Capacity,
ProducerEpoch = view.ProducerEpoch,
Sequence = view.Sequence,
Synchronization = CultMeshBodySynchronization.TripleBuffer,
LeaseExpiresAtUnixMs = DateTimeOffset.UtcNow.AddMinutes(1).ToUnixTimeMilliseconds()
};
var descriptor = await new CultMeshNetworkBodyPublisher(source, _ => true).PublishAsync(generation, bytes);
var binding = source.Get<CultMeshNetworkBodyDocument>(
CultMeshNetworkBodyDocument.CreateRecordKey(descriptor.CapabilityToken))!;
var manifest = source.Get<CultMeshCdnArtifactManifest>(new CultRecordKey(binding.ManifestRecordKey))!;
var method = typeof(EveUnityCultMeshLiveProviderTransport)
.GetMethod("ValidateNetworkBody", BindingFlags.NonPublic | BindingFlags.Static)!;
method.Invoke(null, new object[] { descriptor, binding, manifest });
var wireTypes = (Type[])typeof(EveUnityCultMeshLiveProviderTransport)
.GetField("WireDocumentTypes", BindingFlags.NonPublic | BindingFlags.Static)!
.GetValue(null)!;
Assert.That(wireTypes.Any(type => type == typeof(CultMeshCdnArtifactChunk)), Is.False);
Assert.That(wireTypes.Any(type => type.Name == "CultMeshNetworkBodyDocument"), Is.False);
}
[Test]

View file

@ -1,11 +1,11 @@
{
"name": "org.gamecult.eve.unity-scene",
"displayName": "Eve Unity Scene Lowering",
"version": "0.3.88",
"version": "0.3.89",
"unity": "6000.0",
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
"dependencies": {
"org.gamecult.cultlib": "1.0.30",
"org.gamecult.cultlib": "1.0.31",
"org.gamecult.eve.plugin-fields": "0.2.3",
"org.gamecult.eve.surface": "0.2.4",
"com.unity.mathematics": "1.3.2",