From 4d2ddb5beba1a10925d2d38faa322aafec932394 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 19 Jul 2026 09:20:40 +0100 Subject: [PATCH] Default hot body subscriptions to live delivery --- src/GameCult.Mesh/CultMesh.cs | 4 ++-- .../docs/reliability-migration-plan.md | 9 ++++++++- .../CultMeshBodyTransportTests.cs | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/GameCult.Mesh/CultMesh.cs b/src/GameCult.Mesh/CultMesh.cs index 34b31474..567de868 100644 --- a/src/GameCult.Mesh/CultMesh.cs +++ b/src/GameCult.Mesh/CultMesh.cs @@ -2860,7 +2860,7 @@ namespace GameCult.Mesh CultNetDatabaseSubscriptionClient subscriptions, IEnumerable supportedBodyTransports, CultMeshHotBodySubscription subscription, - CultNetDatabaseSubscriptionDeliveryMode deliveryMode = CultNetDatabaseSubscriptionDeliveryMode.ReplicateToCache) + CultNetDatabaseSubscriptionDeliveryMode deliveryMode = CultNetDatabaseSubscriptionDeliveryMode.Live) { if (subscriptions == null) throw new ArgumentNullException(nameof(subscriptions)); if (supportedBodyTransports == null) throw new ArgumentNullException(nameof(supportedBodyTransports)); @@ -2883,7 +2883,7 @@ namespace GameCult.Mesh CultNetDatabaseSubscriptionClient subscriptions, CultMeshBodyPublicationResolver bodyResolver, CultMeshHotBodySubscription subscription, - CultNetDatabaseSubscriptionDeliveryMode deliveryMode = CultNetDatabaseSubscriptionDeliveryMode.ReplicateToCache) + CultNetDatabaseSubscriptionDeliveryMode deliveryMode = CultNetDatabaseSubscriptionDeliveryMode.Live) { if (bodyResolver == null) throw new ArgumentNullException(nameof(bodyResolver)); return SubscribeHotBodyAsync(subscriptions, bodyResolver.SupportedTransports, subscription, deliveryMode); diff --git a/src/GameCult.Mesh/docs/reliability-migration-plan.md b/src/GameCult.Mesh/docs/reliability-migration-plan.md index d0594d44..3d31483b 100644 --- a/src/GameCult.Mesh/docs/reliability-migration-plan.md +++ b/src/GameCult.Mesh/docs/reliability-migration-plan.md @@ -924,7 +924,11 @@ latest-publication record, consumer identity, and logical body demand in one contract. The local `CultMeshBodyTransportService` contributes only adapters it can actually open, ordered shared-memory first; the server combines those capabilities with observed peer locality. Applications do not hand-assemble -transport-name arrays or request one snapshot per frame. +transport-name arrays or request one snapshot per frame. Hot-body subscriptions +default to `Live`: their exact initial metadata is decoded for bootstrap, then +subsequent layout/publication changes remain ephemeral notifications. Durable +cache replication is an explicit opt-in for consumers that actually own a +replica requirement. ### Reactive document delivery body map @@ -936,6 +940,9 @@ the value; the transport and renderer own neither its persistence nor meaning. `CultNetDatabaseSubscriptionDeliveryMode`. `ReplicateToCache` materializes bootstrap and durable state before notification. `Live` validates and decodes the same typed payload, then notifies the subscriber without a replica write. +High-rate body subscriptions select `Live` by default; general database +subscriptions retain the durable default because CultMesh cannot infer their +ownership contract. **Outputs:** both modes produce the same typed initial values and change event. Only durable replication mutates the consumer's CultCache. diff --git a/tests/GameCult.Mesh.Tests/CultMeshBodyTransportTests.cs b/tests/GameCult.Mesh.Tests/CultMeshBodyTransportTests.cs index 154ee587..11782a0a 100644 --- a/tests/GameCult.Mesh.Tests/CultMeshBodyTransportTests.cs +++ b/tests/GameCult.Mesh.Tests/CultMeshBodyTransportTests.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using FluentAssertions; using NUnit.Framework; using System.IO.MemoryMappedFiles; @@ -381,6 +382,22 @@ public sealed class CultMeshBodyTransportTests subscription.ConsumerRuntimeId.Should().Be("eve-unity"); } + [Test] + public void HotBodySubscriptionsDefaultToEphemeralLiveDelivery() + { + var overloads = typeof(CultMesh).GetMethods() + .Where(method => method.Name == nameof(CultMesh.SubscribeHotBodyAsync)) + .ToArray(); + + overloads.Should().HaveCount(2); + foreach (var overload in overloads) + { + var deliveryMode = overload.GetParameters().Single(parameter => + parameter.ParameterType == typeof(CultNetDatabaseSubscriptionDeliveryMode)); + deliveryMode.DefaultValue.Should().Be(CultNetDatabaseSubscriptionDeliveryMode.Live); + } + } + private static CultMeshBodyValidationRequest Request(CultMeshBodyDescriptor descriptor, DateTimeOffset now) => new() { BodyId = descriptor.BodyId,