feat(runtime): commit dormant SetPosition activation
This commit is contained in:
parent
99f867f053
commit
5785a07b3e
13 changed files with 4674 additions and 111 deletions
|
|
@ -707,6 +707,57 @@ public sealed class PlayerMovementController
|
|||
internal bool IsRuntimeOwnedDormant => _publicationLifecycle
|
||||
is PlayerMovementControllerPublicationLifecycle.RuntimeOwnedDormant;
|
||||
|
||||
internal bool IsRuntimePublished => _publicationLifecycle
|
||||
is PlayerMovementControllerPublicationLifecycle.RuntimePublished;
|
||||
|
||||
private bool _dormantSetPositionGroundPhase;
|
||||
|
||||
internal void BeginDormantSetPositionGroundPhase()
|
||||
{
|
||||
if (!IsRuntimeOwnedDormant || _dormantSetPositionGroundPhase)
|
||||
throw new InvalidOperationException(
|
||||
"Dormant SetPosition ground phase requires one dormant Runtime owner.");
|
||||
_dormantSetPositionGroundPhase = true;
|
||||
}
|
||||
|
||||
internal void EndDormantSetPositionGroundPhase()
|
||||
{
|
||||
if (!_dormantSetPositionGroundPhase)
|
||||
throw new InvalidOperationException(
|
||||
"Dormant SetPosition ground phase is not active.");
|
||||
_body.TransientState &= ~TransientStateFlags.Active;
|
||||
_dormantSetPositionGroundPhase = false;
|
||||
}
|
||||
|
||||
internal bool IsDormantSetPositionGroundPhaseActive =>
|
||||
_dormantSetPositionGroundPhase;
|
||||
|
||||
internal void RefreshDormantRuntimePhysicsState(
|
||||
PhysicsStateFlags state,
|
||||
bool recalculateAcceleration)
|
||||
{
|
||||
if (!IsRuntimeOwnedDormant || _dormantSetPositionGroundPhase)
|
||||
throw new InvalidOperationException(
|
||||
"Only an idle dormant Runtime owner can refresh physics state.");
|
||||
_body.State = state;
|
||||
if (recalculateAcceleration)
|
||||
_body.calc_acceleration();
|
||||
}
|
||||
|
||||
internal void RefreshDormantRuntimeVector(
|
||||
Vector3? velocity,
|
||||
Vector3? omega)
|
||||
{
|
||||
if (!IsRuntimeOwnedDormant || _dormantSetPositionGroundPhase)
|
||||
throw new InvalidOperationException(
|
||||
"Only an idle dormant Runtime owner can refresh vector state.");
|
||||
if (velocity is { } liveVelocity)
|
||||
_body.set_velocity(liveVelocity);
|
||||
if (omega is { } liveOmega)
|
||||
_body.Omega = liveOmega;
|
||||
_body.TransientState &= ~TransientStateFlags.Active;
|
||||
}
|
||||
|
||||
internal void SealPublicationCandidate()
|
||||
{
|
||||
if (_publicationLifecycle
|
||||
|
|
@ -738,7 +789,8 @@ public sealed class PlayerMovementController
|
|||
{
|
||||
if (_publicationLifecycle
|
||||
is not PlayerMovementControllerPublicationLifecycle
|
||||
.RuntimeOwnedDormant)
|
||||
.RuntimeOwnedDormant
|
||||
|| _dormantSetPositionGroundPhase)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Only a dormant Runtime-owned movement controller can be activated.");
|
||||
|
|
@ -747,6 +799,30 @@ public sealed class PlayerMovementController
|
|||
.RuntimePublished;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs the already-committed Runtime SetPosition frame into the
|
||||
/// controller's interpolation/cell sidecar without invoking the public
|
||||
/// teleport path. The canonical body is written by Runtime first; this
|
||||
/// method only makes the controller's private frame agree while it is
|
||||
/// still dormant. It deliberately does not touch CellGraph, movement,
|
||||
/// PositionManager, the object clock, or callbacks.
|
||||
/// </summary>
|
||||
internal void CommitRuntimeActivationFrame()
|
||||
{
|
||||
if (_publicationLifecycle
|
||||
is not PlayerMovementControllerPublicationLifecycle
|
||||
.RuntimeOwnedDormant
|
||||
|| _dormantSetPositionGroundPhase)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Only a dormant Runtime-owned movement controller can accept its activation frame.");
|
||||
}
|
||||
|
||||
_prevPhysicsPos = _body.Position;
|
||||
_currPhysicsPos = _body.Position;
|
||||
CellId = _body.CellPosition.ObjCellId;
|
||||
}
|
||||
|
||||
internal void DiscardRuntimeCandidate()
|
||||
{
|
||||
if (_publicationLifecycle
|
||||
|
|
@ -775,7 +851,8 @@ public sealed class PlayerMovementController
|
|||
is PlayerMovementControllerPublicationLifecycle.StandalonePublished
|
||||
or PlayerMovementControllerPublicationLifecycle
|
||||
.CandidatePreparing
|
||||
or PlayerMovementControllerPublicationLifecycle.RuntimePublished)
|
||||
or PlayerMovementControllerPublicationLifecycle.RuntimePublished
|
||||
|| IsRuntimeOwnedDormant && _dormantSetPositionGroundPhase)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -807,6 +884,9 @@ public sealed class PlayerMovementController
|
|||
if ((_body.State & PhysicsStateFlags.Static) != 0)
|
||||
return;
|
||||
|
||||
if (IsRuntimeOwnedDormant && _dormantSetPositionGroundPhase)
|
||||
return;
|
||||
|
||||
_objectClock.Activate();
|
||||
_body.TransientState |= TransientStateFlags.Active;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Physics.Motion;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Physics;
|
||||
|
||||
|
|
@ -23,6 +24,26 @@ internal enum RuntimeLocalPlayerPhysicsActivationStatus
|
|||
RejectedToken,
|
||||
}
|
||||
|
||||
internal enum RuntimeLocalPlayerShadowDisposition : byte
|
||||
{
|
||||
RegisteredAuthoredPayload,
|
||||
ProvenShapeless,
|
||||
}
|
||||
|
||||
internal readonly record struct RuntimeLocalPlayerPhysicsActivationPreparation(
|
||||
float Radius,
|
||||
float Height,
|
||||
RuntimeLocalPlayerShadowDisposition ShadowDisposition)
|
||||
{
|
||||
internal bool IsValid => float.IsFinite(Radius)
|
||||
&& Radius >= 0f
|
||||
&& float.IsFinite(Height)
|
||||
&& Height >= 0f
|
||||
&& ShadowDisposition is RuntimeLocalPlayerShadowDisposition
|
||||
.RegisteredAuthoredPayload
|
||||
or RuntimeLocalPlayerShadowDisposition.ProvenShapeless;
|
||||
}
|
||||
|
||||
internal readonly record struct RuntimeLocalPlayerPhysicsPublicationToken(
|
||||
RuntimeEntityKey Entity,
|
||||
RuntimeEntityPlacementToken Placement,
|
||||
|
|
@ -111,6 +132,12 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
{ get; init; }
|
||||
internal required PlayerMovementController Controller { get; init; }
|
||||
internal required PhysicsBody Body { get; init; }
|
||||
internal required EntityPhysicsHost PhysicsHost { get; init; }
|
||||
internal required MovementManager Movement { get; init; }
|
||||
internal required MotionInterpreter Motion { get; init; }
|
||||
internal required RuntimeLocalPlayerPhysicsActivationPreparation
|
||||
ActivationPreparation { get; init; }
|
||||
internal required Activation PreparedActivation { get; init; }
|
||||
}
|
||||
|
||||
private sealed class Activation
|
||||
|
|
@ -122,8 +149,15 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
{ get; init; }
|
||||
internal required PlayerMovementController Controller { get; init; }
|
||||
internal required PhysicsBody Body { get; init; }
|
||||
internal required EntityPhysicsHost PhysicsHost { get; init; }
|
||||
internal required MovementManager Movement { get; init; }
|
||||
internal required MotionInterpreter Motion { get; init; }
|
||||
internal required RuntimeLocalPlayerPhysicsActivationPreparation
|
||||
ActivationPreparation { get; init; }
|
||||
internal RuntimeLocalPlayerPhysicsActivationReceipt Receipt
|
||||
{ get; set; }
|
||||
internal RuntimeDormantSetPositionCommitReceipt PendingFinalCommit
|
||||
{ get; set; }
|
||||
}
|
||||
|
||||
private readonly RuntimeEntityDirectory _entities;
|
||||
|
|
@ -135,6 +169,7 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
private ulong _nextPublicationId;
|
||||
private ulong _nextActivationId;
|
||||
private ulong _nextEvaluationId;
|
||||
private long _activationDispatchFailureCount;
|
||||
private bool _disposed;
|
||||
|
||||
internal RuntimeLocalPlayerPhysicsPublicationState(
|
||||
|
|
@ -154,14 +189,25 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
in RuntimeEntityPlacementToken placement,
|
||||
in RuntimeSetPositionCommand command,
|
||||
PlayerMovementConstructionOptions options,
|
||||
in RuntimeLocalPlayerPhysicsActivationPreparation activationPreparation,
|
||||
out RuntimeLocalPlayerPhysicsPublicationToken token)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
token = default;
|
||||
if (!CanPrepare(record, placement, command))
|
||||
if (!activationPreparation.IsValid
|
||||
|| !CanPrepare(record, placement, command))
|
||||
return RuntimeLocalPlayerPhysicsPublicationStatus.RejectedAuthority;
|
||||
|
||||
// Exhaustion is checked before allocating or replacing a private
|
||||
// candidate. A checked overflow must not strand an unowned controller
|
||||
// or discard an already-prepared exact receipt.
|
||||
ulong publicationId = checked(_nextPublicationId + 1UL);
|
||||
ulong activationId = checked(_nextActivationId + 1UL);
|
||||
|
||||
RuntimeLocalPlayerPhysicsActivationPreparation preparedActivation =
|
||||
activationPreparation;
|
||||
|
||||
var controller = PlayerMovementController.CreatePublicationCandidate(
|
||||
_physics.Engine,
|
||||
options);
|
||||
|
|
@ -177,6 +223,69 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
controller.SetBodyOrientation(command.Physics.Orientation);
|
||||
controller.ApplyPhysicsState(record.FinalPhysicsState);
|
||||
PhysicsBody body = controller.PhysicsBody;
|
||||
var physics = record.Snapshot.Physics;
|
||||
body.Friction = NormalizeFriction(
|
||||
physics?.Friction ?? record.Snapshot.Friction);
|
||||
body.Elasticity = NormalizeElasticity(
|
||||
physics?.Elasticity
|
||||
?? record.Snapshot.Elasticity
|
||||
?? body.Elasticity);
|
||||
if (physics?.Velocity is { } initialVelocity)
|
||||
body.set_velocity(initialVelocity);
|
||||
if (physics?.AngularVelocity is { } initialOmega)
|
||||
body.Omega = initialOmega;
|
||||
MovementManager movement = controller.Movement;
|
||||
MotionInterpreter motion = controller.Motion;
|
||||
EntityPhysicsHost physicsHost = null!;
|
||||
movement.MoveToFactory = () =>
|
||||
{
|
||||
var moveTo = new MoveToManager(
|
||||
motion,
|
||||
stopCompletely: () =>
|
||||
_ = controller.StopCompletelyAtPhysicsObjectBoundary(),
|
||||
getPosition: () => body.CellPosition,
|
||||
getHeading: () => MoveToMath.GetHeading(body.Orientation),
|
||||
setHeading: (heading, _) => body.Orientation =
|
||||
MoveToMath.SetHeading(body.Orientation, heading),
|
||||
getOwnRadius: () => preparedActivation.Radius,
|
||||
getOwnHeight: () => preparedActivation.Height,
|
||||
contact: () => body.InContact,
|
||||
isInterpolating: static () => false,
|
||||
getVelocity: () => body.Velocity,
|
||||
getSelfId: () => record.ServerGuid,
|
||||
setTarget: (context, target, radius, quantum) =>
|
||||
physicsHost.SetTarget(context, target, radius, quantum),
|
||||
clearTarget: () => physicsHost.ClearTarget(),
|
||||
getTargetQuantum: () =>
|
||||
physicsHost.TargetManager.GetTargetQuantum(),
|
||||
setTargetQuantum: quantum =>
|
||||
physicsHost.TargetManager.SetTargetQuantum(quantum),
|
||||
curTime: () => controller.SimTimeSeconds);
|
||||
moveTo.StickTo = (target, radius, height) =>
|
||||
physicsHost.PositionManager.StickTo(target, radius, height);
|
||||
moveTo.Unstick = physicsHost.PositionManager.UnStick;
|
||||
return moveTo;
|
||||
};
|
||||
physicsHost = new EntityPhysicsHost(
|
||||
record.ServerGuid,
|
||||
getPosition: () => body.CellPosition,
|
||||
getVelocity: () => body.Velocity,
|
||||
getRadius: () => preparedActivation.Radius,
|
||||
inContact: () => body.InContact,
|
||||
minterpMaxSpeed: () => motion.GetAdjustedMaxSpeed(),
|
||||
curTime: () => controller.SimTimeSeconds,
|
||||
physicsTimerTime: () => controller.SimTimeSeconds,
|
||||
getObjectA: id => _physics.TryGetPhysicsHost(id, out var host)
|
||||
? host
|
||||
: null,
|
||||
handleUpdateTarget: movement.HandleUpdateTarget,
|
||||
interruptCurrentMovement: () =>
|
||||
movement.CancelMoveTo(WeenieError.ActionCancelled));
|
||||
movement.MakeMoveToManager();
|
||||
motion.UnstickFromObject = physicsHost.PositionManager.UnStick;
|
||||
motion.InterruptCurrentMovement = () =>
|
||||
movement.CancelMoveTo(WeenieError.ActionCancelled);
|
||||
controller.PositionManager = physicsHost.PositionManager;
|
||||
// This checkpoint publishes ownership only. The subsequent canonical
|
||||
// SetPosition transaction is the sole authority which may enter the
|
||||
// body into world simulation and activate its ordinary workset.
|
||||
|
|
@ -194,16 +303,41 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
}
|
||||
|
||||
DiscardCurrent();
|
||||
_nextPublicationId = publicationId;
|
||||
_nextActivationId = activationId;
|
||||
token = new RuntimeLocalPlayerPhysicsPublicationToken(
|
||||
record.Key.Value,
|
||||
placement,
|
||||
checked(++_nextPublicationId),
|
||||
publicationId,
|
||||
_identity.ServerGuid,
|
||||
_identity.Revision,
|
||||
record.PhysicsOwnershipEpoch,
|
||||
record.ObjectClockEpoch,
|
||||
_movement.ControllerOwnershipEpoch,
|
||||
_entities.SessionLifetimeVersion);
|
||||
ulong expectedControllerEpoch = checked(
|
||||
_movement.ControllerOwnershipEpoch + 1UL);
|
||||
var activationEnvelope = new Activation
|
||||
{
|
||||
Token = new RuntimeLocalPlayerPhysicsActivationToken(
|
||||
token.Entity,
|
||||
token.Placement,
|
||||
activationId,
|
||||
token.LocalPlayerServerGuid,
|
||||
token.LocalPlayerIdentityRevision,
|
||||
checked(token.PhysicsOwnershipEpoch + 1UL),
|
||||
token.ObjectClockEpoch,
|
||||
expectedControllerEpoch,
|
||||
token.SessionGenerationAuthority),
|
||||
Record = record,
|
||||
PlacementCommand = command,
|
||||
Controller = controller,
|
||||
Body = body,
|
||||
PhysicsHost = physicsHost,
|
||||
Movement = movement,
|
||||
Motion = motion,
|
||||
ActivationPreparation = preparedActivation,
|
||||
};
|
||||
_candidate = new Candidate
|
||||
{
|
||||
Token = token,
|
||||
|
|
@ -211,10 +345,27 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
PlacementCommand = command,
|
||||
Controller = controller,
|
||||
Body = body,
|
||||
PhysicsHost = physicsHost,
|
||||
Movement = movement,
|
||||
Motion = motion,
|
||||
ActivationPreparation = preparedActivation,
|
||||
PreparedActivation = activationEnvelope,
|
||||
};
|
||||
return RuntimeLocalPlayerPhysicsPublicationStatus.Prepared;
|
||||
}
|
||||
|
||||
private static float NormalizeFriction(float? value) =>
|
||||
value is >= 0f and <= 1f && float.IsFinite(value.Value)
|
||||
? value.Value
|
||||
: PhysicsBody.DefaultFriction;
|
||||
|
||||
private static float NormalizeElasticity(float value)
|
||||
{
|
||||
if (float.IsNaN(value) || value <= 0f)
|
||||
return 0f;
|
||||
return MathF.Min(value, 0.1f);
|
||||
}
|
||||
|
||||
internal RuntimeLocalPlayerPhysicsPublicationStatus Commit(
|
||||
in RuntimeLocalPlayerPhysicsPublicationToken token) =>
|
||||
Commit(token, out _);
|
||||
|
|
@ -245,24 +396,8 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
candidate.Record.ObjectClock);
|
||||
candidate.Record.SetPhysicsBody(candidate.Body);
|
||||
_movement.CommitRuntimeOwnedController(candidate.Controller);
|
||||
activationToken = new RuntimeLocalPlayerPhysicsActivationToken(
|
||||
candidate.Token.Entity,
|
||||
candidate.Token.Placement,
|
||||
checked(++_nextActivationId),
|
||||
candidate.Token.LocalPlayerServerGuid,
|
||||
candidate.Token.LocalPlayerIdentityRevision,
|
||||
candidate.Record.PhysicsOwnershipEpoch,
|
||||
candidate.Record.ObjectClockEpoch,
|
||||
_movement.ControllerOwnershipEpoch,
|
||||
_entities.SessionLifetimeVersion);
|
||||
_activation = new Activation
|
||||
{
|
||||
Token = activationToken,
|
||||
Record = candidate.Record,
|
||||
PlacementCommand = candidate.PlacementCommand,
|
||||
Controller = candidate.Controller,
|
||||
Body = candidate.Body,
|
||||
};
|
||||
activationToken = candidate.PreparedActivation.Token;
|
||||
_activation = candidate.PreparedActivation;
|
||||
_candidate = null;
|
||||
return RuntimeLocalPlayerPhysicsPublicationStatus.Committed;
|
||||
}
|
||||
|
|
@ -291,6 +426,16 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
activation.PlacementCommand,
|
||||
out RuntimeDormantSetPositionEvaluation placement))
|
||||
{
|
||||
if (ReferenceEquals(_activation, activation)
|
||||
&& IsActivationCurrent(activation)
|
||||
&& _physics.SetPosition.IsDormantLocalActivationAwaitingCell(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
token.Placement,
|
||||
activation.PlacementCommand))
|
||||
{
|
||||
return RuntimeLocalPlayerPhysicsActivationStatus.DeferredCell;
|
||||
}
|
||||
if (ReferenceEquals(_activation, activation)
|
||||
&& !IsActivationCurrent(activation))
|
||||
{
|
||||
|
|
@ -329,6 +474,304 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
receipt.Placement);
|
||||
}
|
||||
|
||||
internal RuntimeDormantSetPositionCommitStatus CommitActivation(
|
||||
in RuntimeLocalPlayerPhysicsActivationReceipt receipt,
|
||||
out RuntimePlacementProjectionToken projection)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
projection = default;
|
||||
if (!receipt.IsValid
|
||||
|| _activation is not { } activation
|
||||
|| activation.Token != receipt.Token
|
||||
|| activation.Receipt != receipt)
|
||||
{
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
if (activation.PendingFinalCommit.Status
|
||||
is RuntimeDormantSetPositionCommitStatus
|
||||
.AwaitingFinalShadowPreparation)
|
||||
{
|
||||
return FinalizeActivation(
|
||||
activation,
|
||||
activation.PendingFinalCommit,
|
||||
out projection);
|
||||
}
|
||||
if (!IsActivationCurrent(activation)
|
||||
|| !_physics.SetPosition.IsDormantLocalEvaluationCurrent(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
receipt.Placement))
|
||||
{
|
||||
activation.Receipt = default;
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
|
||||
bool provenShapeless = activation.ActivationPreparation
|
||||
.ShadowDisposition
|
||||
is RuntimeLocalPlayerShadowDisposition.ProvenShapeless;
|
||||
if (!_physics.SetPosition.TryPrepareDormantLocalActivationCommit(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
receipt.Placement,
|
||||
provenShapeless,
|
||||
out PreparedDormantSetPositionCommit? prepared)
|
||||
|| prepared is null
|
||||
|| !IsActivationCurrent(activation))
|
||||
{
|
||||
activation.Receipt = default;
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
|
||||
if (!_physics.SetPosition.TryApplyDormantLocalActivationCommit(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
activation.Controller,
|
||||
activation.PhysicsHost,
|
||||
prepared,
|
||||
out RuntimeDormantSetPositionCommitReceipt committed))
|
||||
{
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
if (committed.Status is RuntimeDormantSetPositionCommitStatus.DeferredCell)
|
||||
{
|
||||
activation.Receipt = default;
|
||||
_physics.SetPosition.DispatchDormantLocalActivationShadow(committed);
|
||||
return committed.Status;
|
||||
}
|
||||
|
||||
// Named retail SetPosition: contact prefix, ground edge, second
|
||||
// acceleration/sliding, collision callbacks, physical response, then
|
||||
// shadow/live publication.
|
||||
try
|
||||
{
|
||||
activation.Controller.BeginDormantSetPositionGroundPhase();
|
||||
if (committed.HitGround)
|
||||
activation.Movement.HitGround();
|
||||
else if (committed.LeaveGround)
|
||||
activation.Motion.LeaveGround();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_activationDispatchFailureCount++;
|
||||
}
|
||||
finally
|
||||
{
|
||||
activation.Controller.EndDormantSetPositionGroundPhase();
|
||||
}
|
||||
if (committed.Status is RuntimeDormantSetPositionCommitStatus
|
||||
.AwaitingFinalShadowPreparation
|
||||
&& (!IsActivationPrephaseEnvelopeCurrent(activation, committed)
|
||||
|| !RefreshDormantVector(activation, committed)
|
||||
|| !RefreshDormantState(activation)
|
||||
|| !_physics.SetPosition.CommitDormantLocalActivationPostGround(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
committed)))
|
||||
{
|
||||
AbortActivation(
|
||||
activation, committed, collisionAlreadyDispatched: false);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
try
|
||||
{
|
||||
SetPositionCollisionBatchDispatchResult collisionDispatch =
|
||||
_physics.SetPosition.DispatchDormantLocalActivationCollision(
|
||||
committed);
|
||||
if (collisionDispatch.Status
|
||||
is not SetPositionCollisionBatchDispatchStatus.Completed)
|
||||
{
|
||||
AbortActivation(
|
||||
activation, committed, collisionAlreadyDispatched: true);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_activationDispatchFailureCount++;
|
||||
AbortActivation(
|
||||
activation, committed, collisionAlreadyDispatched: true);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
if (!IsActivationResponseEnvelopeCurrent(activation, committed))
|
||||
{
|
||||
AbortActivation(
|
||||
activation, committed, collisionAlreadyDispatched: true);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
activation.Controller.RefreshDormantRuntimePhysicsState(
|
||||
activation.Record.FinalPhysicsState,
|
||||
recalculateAcceleration: false);
|
||||
_ = RefreshDormantVector(activation, committed);
|
||||
if (committed.Status is RuntimeDormantSetPositionCommitStatus
|
||||
.AwaitingFinalShadowPreparation
|
||||
&& !IsActivationPrephaseEnvelopeCurrent(activation, committed)
|
||||
|| !_physics.SetPosition.CommitDormantLocalActivationPostCollision(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
committed))
|
||||
{
|
||||
AbortActivation(
|
||||
activation, committed, collisionAlreadyDispatched: true);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
if (committed.Status is RuntimeDormantSetPositionCommitStatus
|
||||
.RejectedPlacement)
|
||||
{
|
||||
activation.Receipt = default;
|
||||
activation.PendingFinalCommit = committed;
|
||||
return committed.Status;
|
||||
}
|
||||
activation.PendingFinalCommit = committed;
|
||||
return FinalizeActivation(activation, committed, out projection);
|
||||
}
|
||||
|
||||
private RuntimeDormantSetPositionCommitStatus FinalizeActivation(
|
||||
Activation activation,
|
||||
in RuntimeDormantSetPositionCommitReceipt prephase,
|
||||
out RuntimePlacementProjectionToken projection)
|
||||
{
|
||||
projection = default;
|
||||
if (!IsActivationPrephaseEnvelopeCurrent(activation, prephase))
|
||||
{
|
||||
AbortActivation(
|
||||
activation, prephase, collisionAlreadyDispatched: true);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
activation.Controller.RefreshDormantRuntimePhysicsState(
|
||||
activation.Record.FinalPhysicsState,
|
||||
recalculateAcceleration: false);
|
||||
bool provenShapeless = activation.ActivationPreparation
|
||||
.ShadowDisposition is RuntimeLocalPlayerShadowDisposition.ProvenShapeless;
|
||||
if (!_physics.SetPosition.TryPrepareDormantLocalActivationFinalCommit(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
prephase,
|
||||
provenShapeless,
|
||||
out PreparedDormantActivationFinalCommit? prepared)
|
||||
|| prepared is null)
|
||||
{
|
||||
if (IsActivationPrephaseEnvelopeCurrent(activation, prephase))
|
||||
return prephase.Status;
|
||||
AbortActivation(
|
||||
activation, prephase, collisionAlreadyDispatched: true);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
if (!IsActivationPrephaseEnvelopeCurrent(activation, prephase))
|
||||
{
|
||||
AbortActivation(
|
||||
activation, prephase, collisionAlreadyDispatched: true);
|
||||
return RuntimeDormantSetPositionCommitStatus.RejectedAuthority;
|
||||
}
|
||||
if (!_physics.SetPosition.TryApplyDormantLocalActivationFinalCommit(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
activation.Controller,
|
||||
activation.PhysicsHost,
|
||||
prephase,
|
||||
prepared,
|
||||
out RuntimeDormantSetPositionCommitReceipt committed))
|
||||
{
|
||||
return prephase.Status;
|
||||
}
|
||||
activation.Receipt = default;
|
||||
activation.PendingFinalCommit = default;
|
||||
_activation = null;
|
||||
_physics.SetPosition.DispatchDormantLocalActivationShadow(committed);
|
||||
if (!IsCommittedActivationSuffixCurrent(activation, committed))
|
||||
return committed.Status;
|
||||
_physics.SetPosition.DispatchDormantLocalActivationPlacement(committed);
|
||||
projection = committed.Projection.Token;
|
||||
return committed.Status;
|
||||
}
|
||||
|
||||
private bool IsActivationPrephaseEnvelopeCurrent(
|
||||
Activation activation,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt) =>
|
||||
IsActivationOwnershipEnvelopeCurrent(activation)
|
||||
&& _physics.IsCollisionEvaluationFatalAuthorityCurrent(
|
||||
receipt.CollisionAuthority)
|
||||
&& _physics.SetPosition.IsDormantLocalActivationPrephaseCurrent(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
receipt);
|
||||
|
||||
private bool IsActivationResponseEnvelopeCurrent(
|
||||
Activation activation,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt) =>
|
||||
IsActivationOwnershipEnvelopeCurrent(activation)
|
||||
&& _physics.IsCollisionEvaluationFatalAuthorityCurrent(
|
||||
receipt.CollisionAuthority)
|
||||
&& _physics.SetPosition.IsDormantLocalActivationResponseCurrent(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
receipt);
|
||||
|
||||
private bool IsActivationOwnershipEnvelopeCurrent(
|
||||
Activation activation) =>
|
||||
activation.Controller.IsRuntimeOwnedDormant
|
||||
&& !activation.Controller.IsDormantSetPositionGroundPhaseActive
|
||||
&& activation.Controller.OwnsPhysicsBody(activation.Body)
|
||||
&& _entities.SessionLifetimeVersion
|
||||
== activation.Token.SessionGenerationAuthority
|
||||
&& _entities.IsCurrent(activation.Record)
|
||||
&& activation.Record.Key == activation.Token.Entity
|
||||
&& !_identity.IsDisposed
|
||||
&& _identity.ServerGuid == activation.Token.LocalPlayerServerGuid
|
||||
&& _identity.ServerGuid == activation.Record.ServerGuid
|
||||
&& _identity.Revision == activation.Token.LocalPlayerIdentityRevision
|
||||
&& activation.Record.PhysicsOwnershipEpoch
|
||||
== activation.Token.PhysicsOwnershipEpoch
|
||||
&& activation.Record.ObjectClockEpoch
|
||||
== activation.Token.ObjectClockEpoch
|
||||
&& _movement.CanCommitRuntimeOwnedController(
|
||||
activation.Token.ControllerOwnershipEpoch,
|
||||
activation.Controller)
|
||||
&& ReferenceEquals(activation.Record.PhysicsBody, activation.Body)
|
||||
&& activation.Record.PhysicsHost is null
|
||||
&& activation.Record.RemoteMotion is null
|
||||
&& activation.Record.Projectile is null
|
||||
&& !activation.Record.PhysicsBodyAcquisitionInProgress
|
||||
&& !activation.Record.RemoteMotionBindingInProgress
|
||||
&& !activation.Record.ProjectileBindingInProgress
|
||||
&& !activation.Record.RequiresRemotePlacementRuntime
|
||||
&& !activation.Record.DeleteAcceptedForTeardown;
|
||||
|
||||
private static bool RefreshDormantState(Activation activation)
|
||||
{
|
||||
activation.Controller.RefreshDormantRuntimePhysicsState(
|
||||
activation.Record.FinalPhysicsState,
|
||||
recalculateAcceleration: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool RefreshDormantVector(
|
||||
Activation activation,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (activation.Record.VectorAuthorityVersion
|
||||
== receipt.SourceVectorAuthorityVersion)
|
||||
return true;
|
||||
var physics = activation.Record.Snapshot.Physics;
|
||||
activation.Controller.RefreshDormantRuntimeVector(
|
||||
physics?.Velocity,
|
||||
physics?.AngularVelocity);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AbortActivation(
|
||||
Activation activation,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt,
|
||||
bool collisionAlreadyDispatched)
|
||||
{
|
||||
_physics.SetPosition.RetireDormantLocalActivation(
|
||||
receipt,
|
||||
collisionAlreadyDispatched);
|
||||
activation.Receipt = default;
|
||||
activation.PendingFinalCommit = default;
|
||||
if (ReferenceEquals(_activation, activation))
|
||||
DiscardActivation();
|
||||
}
|
||||
|
||||
internal bool DiscardActivation(
|
||||
in RuntimeLocalPlayerPhysicsActivationToken token)
|
||||
{
|
||||
|
|
@ -389,6 +832,9 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
_activation is null ? 0 : 1,
|
||||
_nextPublicationId);
|
||||
|
||||
internal long ActivationDispatchFailureCount =>
|
||||
_activationDispatchFailureCount;
|
||||
|
||||
internal void ResetSession()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
|
@ -501,15 +947,67 @@ internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|||
&& !activation.Record.ProjectileBindingInProgress
|
||||
&& !activation.Record.RequiresRemotePlacementRuntime
|
||||
&& !activation.Record.DeleteAcceptedForTeardown
|
||||
&& _physics.SetPosition.IsExactPreparedPlacementCurrent(
|
||||
&& _physics.SetPosition.IsDormantLocalActivationLeaseCurrent(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
activation.Token.Placement,
|
||||
activation.PlacementCommand);
|
||||
|
||||
private bool IsCommittedActivationSuffixCurrent(
|
||||
Activation activation,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
return activation.Token.ObjectClockEpoch != ulong.MaxValue
|
||||
&& _entities.SessionLifetimeVersion
|
||||
== activation.Token.SessionGenerationAuthority
|
||||
&& _entities.IsCurrent(activation.Record)
|
||||
&& activation.Record.Key == activation.Token.Entity
|
||||
&& !_identity.IsDisposed
|
||||
&& _identity.ServerGuid == activation.Token.LocalPlayerServerGuid
|
||||
&& _identity.ServerGuid == activation.Record.ServerGuid
|
||||
&& _identity.Revision
|
||||
== activation.Token.LocalPlayerIdentityRevision
|
||||
&& activation.Record.PhysicsOwnershipEpoch
|
||||
== activation.Token.PhysicsOwnershipEpoch
|
||||
&& activation.Record.ObjectClockEpoch
|
||||
== activation.Token.ObjectClockEpoch + 1UL
|
||||
&& _movement.ControllerOwnershipEpoch
|
||||
== activation.Token.ControllerOwnershipEpoch
|
||||
&& ReferenceEquals(_movement.Controller, activation.Controller)
|
||||
&& activation.Controller.IsRuntimePublished
|
||||
&& activation.Controller.OwnsPhysicsBody(activation.Body)
|
||||
&& ReferenceEquals(
|
||||
activation.Record.PhysicsBody,
|
||||
activation.Body)
|
||||
&& ReferenceEquals(
|
||||
activation.Record.PhysicsHost,
|
||||
activation.PhysicsHost)
|
||||
&& activation.Record.RemoteMotion is null
|
||||
&& activation.Record.Projectile is null
|
||||
&& !activation.Record.DeleteAcceptedForTeardown
|
||||
&& _physics.SetPosition.IsDormantLocalActivationCommitCurrent(
|
||||
activation.Record,
|
||||
activation.Body,
|
||||
receipt);
|
||||
}
|
||||
|
||||
private void DiscardActivation()
|
||||
{
|
||||
Activation? activation = _activation;
|
||||
_activation = null;
|
||||
if (activation is not null)
|
||||
{
|
||||
if (activation.PendingFinalCommit.Status
|
||||
is not RuntimeDormantSetPositionCommitStatus.None)
|
||||
{
|
||||
_physics.SetPosition.RetireDormantLocalActivation(
|
||||
activation.PendingFinalCommit,
|
||||
collisionAlreadyDispatched: true);
|
||||
}
|
||||
_physics.SetPosition.RetireDormantLocalActivationToken(
|
||||
activation.Record,
|
||||
activation.Token.Placement);
|
||||
}
|
||||
if (activation is not null
|
||||
&& _entities.IsCurrent(activation.Record)
|
||||
&& ReferenceEquals(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue