using AcDream.Runtime.Entities; namespace AcDream.Runtime.Physics; /// /// Presentation-only sink for canonical Runtime SetPosition receipts. /// Implementations must apply an exact receipt idempotently: acknowledgement /// can fail after a successful projection when a re-entrant Runtime mutation /// revises the FIFO head, and the same immutable receipt may then be retried. /// public interface IRuntimePlacementProjectionSink { bool TryApply(in RuntimePlacementProjectionSnapshot projection); } /// /// Shared graphical/no-window subscription which projects only the oldest /// canonical receipt and acknowledges it only after the host sink succeeds. /// Runtime remains the sole position, collision, residence, and lifetime /// authority; this class owns only its observer subscription and one /// idempotency token for a projection that succeeded before acknowledgement. /// public sealed class RuntimePlacementProjectionSubscription : IRuntimePlacementObserver, IDisposable { private readonly RuntimePlacementProjectionChannel _channel; private readonly Func _generation; private readonly IRuntimePlacementProjectionSink _sink; private IDisposable? _subscription; private RuntimePlacementProjectionToken _appliedAwaitingAcknowledgement; private bool _disposed; public RuntimePlacementProjectionSubscription( GameRuntime runtime, IRuntimePlacementProjectionSink sink) : this(runtime, sink, retryPendingOnSubscribe: true) { } /// /// Subscribes before optionally draining the pending FIFO. A session route /// which must publish its own disposal/retry ownership first passes /// false, stores those owners, then calls . /// public RuntimePlacementProjectionSubscription( GameRuntime runtime, IRuntimePlacementProjectionSink sink, bool retryPendingOnSubscribe) : this( runtime?.Placements ?? throw new ArgumentNullException(nameof(runtime)), () => runtime.Generation, sink, retryPendingOnSubscribe) { } internal RuntimePlacementProjectionSubscription( RuntimePlacementProjectionChannel channel, Func generation, IRuntimePlacementProjectionSink sink) : this( channel, generation, sink, retryPendingOnSubscribe: true) { } internal RuntimePlacementProjectionSubscription( RuntimePlacementProjectionChannel channel, Func generation, IRuntimePlacementProjectionSink sink, bool retryPendingOnSubscribe) { _channel = channel ?? throw new ArgumentNullException(nameof(channel)); _generation = generation ?? throw new ArgumentNullException(nameof(generation)); _sink = sink ?? throw new ArgumentNullException(nameof(sink)); _subscription = _channel.Subscribe(this); if (retryPendingOnSubscribe) _ = RetryPending(); } public bool HasAppliedReceiptAwaitingAcknowledgement => _appliedAwaitingAcknowledgement.IsValid; /// /// C2-2 review fix (delta round, B5(b)): true when Runtime's placement /// FIFO has at least one outstanding receipt, so a host can early-out /// before calling without reaching the /// Runtime placement channel directly — the architectural boundary /// RuntimePhysicsOwnershipTests.ProductionHostsUseSharedPlacementSubscriptionWithoutDirectChannel /// enforces (hosts consume placement state ONLY through this /// subscription). Only closes the EMPTY-FIFO case: when the count is /// nonzero, RetryPending still reaches /// RuntimeSetPositionState.RetryPendingProjections's per-call /// array snapshot — see docs/ISSUES.md for that residual. /// public bool HasPendingReceipts => _channel.PendingCount != 0; /// /// Republishes Runtime's complete still-pending FIFO. Later receipts are /// ignored until the exact oldest receipt projects and acknowledges. /// public bool RetryPending() { ObjectDisposedException.ThrowIf(_disposed, this); RuntimeGenerationToken generation = _generation(); if (_appliedAwaitingAcknowledgement.IsValid && (!_channel.TryPeek( generation, out RuntimePlacementProjectionSnapshot head) || head.Token != _appliedAwaitingAcknowledgement)) { _appliedAwaitingAcknowledgement = default; } return _channel.RetryPending(generation); } public void OnPlacement(in RuntimePlacementDelta delta) { if (_disposed || !_channel.TryPeek( delta.Stamp.Generation, out RuntimePlacementProjectionSnapshot head) || head != delta.Placement) { return; } RuntimePlacementProjectionToken token = head.Token; if (_appliedAwaitingAcknowledgement != token) { if (!_sink.TryApply(in head)) return; // A sink can synchronously tear down its host while applying a // receipt. Leave that receipt pending for the replacement host; // disposal is never permission to acknowledge afterward. if (_disposed) return; _appliedAwaitingAcknowledgement = token; } if (_channel.Acknowledge(delta.Stamp.Generation, token) && _appliedAwaitingAcknowledgement == token) { _appliedAwaitingAcknowledgement = default; } } public void Dispose() { if (_disposed) return; _disposed = true; Interlocked.Exchange(ref _subscription, null)?.Dispose(); _appliedAwaitingAcknowledgement = default; } }