feat(runtime): publish dormant local physics ownership

This commit is contained in:
Erik 2026-08-01 10:01:30 +02:00
parent 442cb8f97b
commit 22651c823d
10 changed files with 1617 additions and 25 deletions

View file

@ -27,6 +27,7 @@ public sealed class RuntimeLocalPlayerMovementState
{
private PlayerMovementController? _controller;
private PlayerMovementController? _preparingMotionOwner;
private RuntimeLocalPlayerPhysicsPublicationState? _physicsPublication;
private bool _autoRunActive;
private bool _hasCommandInput;
private MovementInput _commandInput;
@ -41,7 +42,9 @@ public sealed class RuntimeLocalPlayerMovementState
ObjectDisposedException.ThrowIf(_disposed, this);
if (ReferenceEquals(_controller, value))
return;
_controller?.RetireRuntimePublication();
_controller = value;
ControllerOwnershipEpoch++;
Interlocked.Increment(ref _revision);
}
}
@ -50,8 +53,13 @@ public sealed class RuntimeLocalPlayerMovementState
public bool HasCommandInput => _hasCommandInput;
public MovementInput CommandInput => _commandInput;
public long Revision => Interlocked.Read(ref _revision);
public ulong ControllerOwnershipEpoch { get; private set; }
public IRuntimeMovementView View => this;
internal RuntimeLocalPlayerPhysicsPublicationState PhysicsPublication =>
_physicsPublication ?? throw new InvalidOperationException(
"The Runtime local-player physics publication owner is not bound.");
MotionInterpreter? IRuntimeLocalPlayerMotionSource.Motion =>
_preparingMotionOwner?.Motion ?? _controller?.Motion;
@ -236,6 +244,7 @@ public sealed class RuntimeLocalPlayerMovementState
public void ResetSession()
{
ObjectDisposedException.ThrowIf(_disposed, this);
_physicsPublication?.ResetSession();
bool changed =
_autoRunActive
|| _hasCommandInput
@ -244,7 +253,12 @@ public sealed class RuntimeLocalPlayerMovementState
_autoRunActive = false;
_hasCommandInput = false;
_commandInput = default;
_controller = null;
if (_controller is not null)
{
_controller.RetireRuntimePublication();
_controller = null;
ControllerOwnershipEpoch++;
}
_preparingMotionOwner = null;
if (changed)
Interlocked.Increment(ref _revision);
@ -257,7 +271,9 @@ public sealed class RuntimeLocalPlayerMovementState
_preparingMotionOwner is not null,
_autoRunActive,
_hasCommandInput,
Revision);
Revision,
ControllerOwnershipEpoch,
_physicsPublication?.CaptureOwnership() ?? default);
public void Dispose()
{
@ -266,12 +282,46 @@ public sealed class RuntimeLocalPlayerMovementState
_autoRunActive = false;
_hasCommandInput = false;
_commandInput = default;
_controller = null;
_physicsPublication?.Dispose();
if (_controller is not null)
{
_controller.RetireRuntimePublication();
_controller = null;
ControllerOwnershipEpoch++;
}
_preparingMotionOwner = null;
Interlocked.Increment(ref _revision);
_disposed = true;
}
internal void AttachPhysicsPublication(
RuntimeLocalPlayerPhysicsPublicationState publication)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentNullException.ThrowIfNull(publication);
if (_physicsPublication is not null)
{
throw new InvalidOperationException(
"The Runtime local-player physics publication owner is already bound.");
}
_physicsPublication = publication;
}
internal bool CanCommitRuntimeOwnedController(
ulong expectedEpoch,
PlayerMovementController? expectedController) =>
!_disposed
&& ControllerOwnershipEpoch == expectedEpoch
&& ReferenceEquals(_controller, expectedController);
internal void CommitRuntimeOwnedController(PlayerMovementController controller)
{
_controller?.RetireRuntimePublication();
_controller = controller;
ControllerOwnershipEpoch++;
Interlocked.Increment(ref _revision);
}
private void EndMotionPreparation(PlayerMovementController controller)
{
// Terminal disposal clears the unpublished construction seam. A
@ -310,12 +360,15 @@ public readonly record struct RuntimeLocalMovementOwnershipSnapshot(
bool HasPreparingMotionOwner,
bool AutoRunActive,
bool HasCommandInput,
long Revision)
long Revision,
ulong ControllerOwnershipEpoch,
RuntimeLocalPlayerPhysicsPublicationOwnershipSnapshot PhysicsPublication)
{
public bool IsConverged =>
IsDisposed
&& !HasController
&& !HasPreparingMotionOwner
&& !AutoRunActive
&& !HasCommandInput;
&& !HasCommandInput
&& PhysicsPublication.IsConverged;
}