refactor(runtime): own local movement and outbound cadence
Move the canonical local movement controller, body/motion managers, object clock, movement wire data, and MTS/jump/AP sender into AcDream.Runtime. Replace process skill defaults with typed Runtime character options, make graphical and direct commands borrow one autorun owner, retain the construction-time PartArray seam, and include movement in terminal ownership convergence. Preserve the accepted pre-inbound movement/jump and post-inbound autonomous-position order while moving the exact packet/cadence fixtures into Runtime tests. Add graphical/direct parity, two-instance isolation, teardown, allocation, architecture, and divergence-path coverage. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
3456dff038
commit
aa3f4a60f8
36 changed files with 878 additions and 276 deletions
|
|
@ -16,89 +16,6 @@ internal sealed class LocalPlayerIdentityState : ILocalPlayerIdentitySource
|
|||
public uint ServerGuid { get; set; }
|
||||
}
|
||||
|
||||
internal interface ILocalPlayerControllerSource
|
||||
{
|
||||
PlayerMovementController? Controller { get; }
|
||||
}
|
||||
|
||||
internal interface ILocalPlayerMotionSource
|
||||
{
|
||||
MotionInterpreter? Motion { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The one mutable local movement-controller slot. Player-mode lifecycle owns
|
||||
/// assignment; update and presentation owners receive the read-only seam.
|
||||
/// </summary>
|
||||
internal sealed class LocalPlayerControllerSlot
|
||||
: ILocalPlayerControllerSource,
|
||||
ILocalPlayerMotionSource
|
||||
{
|
||||
private PlayerMovementController? _preparingMotionOwner;
|
||||
|
||||
public PlayerMovementController? Controller { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The motion owner visible to the local PartArray completion relay.
|
||||
/// During player-mode construction this is the fully animation-bound
|
||||
/// candidate controller; all other consumers continue to see only the
|
||||
/// committed <see cref="Controller"/>.
|
||||
/// </summary>
|
||||
MotionInterpreter? ILocalPlayerMotionSource.Motion =>
|
||||
_preparingMotionOwner?.Motion ?? Controller?.Motion;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the narrow construction-time ownership seam required by retail's
|
||||
/// CPhysicsObj/PartArray lifetime. Initial placement synchronously queues
|
||||
/// and completes StopCompletely, so its MotionDone relay must reach the
|
||||
/// candidate interpreter before the complete controller is published.
|
||||
/// </summary>
|
||||
public IDisposable BeginMotionPreparation(
|
||||
PlayerMovementController controller,
|
||||
Action? drainPriorAnimationQueue = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(controller);
|
||||
if (_preparingMotionOwner is not null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"A local player motion owner is already being prepared.");
|
||||
}
|
||||
|
||||
// The live PartArray exists before the local player controller. Drain
|
||||
// any animation completions produced under that prior ownership while
|
||||
// they still resolve to the prior (or null) interpreter. Publishing
|
||||
// the fresh interpreter first would let those old callbacks pop its
|
||||
// new pending_motions queue and leave an unmatched Ready sentinel.
|
||||
drainPriorAnimationQueue?.Invoke();
|
||||
_preparingMotionOwner = controller;
|
||||
return new MotionPreparation(this, controller);
|
||||
}
|
||||
|
||||
private void EndMotionPreparation(PlayerMovementController controller)
|
||||
{
|
||||
if (!ReferenceEquals(_preparingMotionOwner, controller))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The local player motion preparation owner changed unexpectedly.");
|
||||
}
|
||||
|
||||
_preparingMotionOwner = null;
|
||||
}
|
||||
|
||||
private sealed class MotionPreparation(
|
||||
LocalPlayerControllerSlot owner,
|
||||
PlayerMovementController controller) : IDisposable
|
||||
{
|
||||
private LocalPlayerControllerSlot? _owner = owner;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LocalPlayerControllerSlot? current = Interlocked.Exchange(ref _owner, null);
|
||||
current?.EndMotionPreparation(controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface ILocalPlayerPhysicsHostSource
|
||||
{
|
||||
EntityPhysicsHost? Host { get; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue