Each of these was temporary apparatus added to chase one bug, and each was supposed to be deleted in the commit that fixed it. Fourteen closed issues later they were still here: #337's support/wire-mesh trio, #171's sticky timeline, #119's viewer and entity dumps, #113's phantom probe, and a dozen more. 3,493 lines removed; the client now reads 144 environment variables instead of 161, and 47 temporary probes remain instead of 64. This is not only tidying. Every probe leaves a branch on its hot path when unset, several re-read the environment per call rather than caching, and the volume buries the diagnostics that are actually load-bearing. It is also a headless correctness matter: HeadlessStaticStateAudit reflects over PhysicsDiagnostics' flags to refuse a multi-session host when any is set, and cannot see probes that live outside that owner. Four files went entirely — WalkMissDiagnostic.cs, CollisionMeshWireframe.cs and two test files whose only subject was a deleted probe. TransitionTypes.SetContactPlane also sheds its CallerMemberName / CallerLineNumber parameters, which existed solely for #337's cpSrc= attribution and carried the instruction to strip them with the probe family; no call site passed them, so no behavior changes. F2's collision overlay survives and reverts to its proxy-cylinder form, which is what removing the ACDREAM_WIRE_MESH upgrade means. LaunchOptionsDocumentationTests earned its keep here: it refused the deletion until docs/launch-options.md moved the 17 rows into Retired and the frozen direct-read counts came down (PhysicsEngine.cs to zero, TransitionTypes.cs 3 to 2). The documentation could not drift during a cleanup this wide. The 14 probes that name no owning issue are deliberately NOT deleted. Nothing records when they became safe to remove, and guessing is how a future investigation loses apparatus it needed; #435 stays open for their attribution. Full hermetic suite 15,321 passed / 0 failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1189 lines
50 KiB
C#
1189 lines
50 KiB
C#
using System.Numerics;
|
|
using AcDream.Core.Physics;
|
|
using AcDream.Core.Physics.Motion;
|
|
using AcDream.Runtime.Entities;
|
|
using AcDream.Runtime.Physics;
|
|
|
|
namespace AcDream.Runtime.Gameplay;
|
|
|
|
internal enum RuntimeLocalPlayerPhysicsPublicationStatus
|
|
{
|
|
Prepared,
|
|
Committed,
|
|
RejectedAuthority,
|
|
RejectedToken,
|
|
Discarded,
|
|
}
|
|
|
|
internal enum RuntimeLocalPlayerPhysicsActivationStatus
|
|
{
|
|
Evaluated,
|
|
DeferredCell,
|
|
RejectedPlacement,
|
|
RejectedAuthority,
|
|
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,
|
|
ulong PublicationId,
|
|
uint LocalPlayerServerGuid,
|
|
long LocalPlayerIdentityRevision,
|
|
ulong PhysicsOwnershipEpoch,
|
|
ulong ObjectClockEpoch,
|
|
ulong ControllerOwnershipEpoch,
|
|
ulong SessionGenerationAuthority)
|
|
{
|
|
internal bool IsValid => PublicationId != 0UL
|
|
&& LocalPlayerServerGuid != 0u
|
|
&& Placement.IsValid
|
|
&& Entity == Placement.Entity;
|
|
}
|
|
|
|
internal readonly record struct RuntimeLocalPlayerPhysicsActivationToken(
|
|
RuntimeEntityKey Entity,
|
|
RuntimeEntityPlacementToken Placement,
|
|
ulong ActivationId,
|
|
uint LocalPlayerServerGuid,
|
|
long LocalPlayerIdentityRevision,
|
|
ulong PhysicsOwnershipEpoch,
|
|
ulong ObjectClockEpoch,
|
|
ulong ControllerOwnershipEpoch,
|
|
ulong SessionGenerationAuthority)
|
|
{
|
|
internal bool IsValid => ActivationId != 0UL
|
|
&& LocalPlayerServerGuid != 0u
|
|
&& Placement.IsValid
|
|
&& Entity == Placement.Entity;
|
|
}
|
|
|
|
internal readonly record struct RuntimeLocalPlayerPhysicsActivationReceipt(
|
|
RuntimeLocalPlayerPhysicsActivationToken Token,
|
|
ulong EvaluationId,
|
|
RuntimeDormantSetPositionEvaluation Placement)
|
|
{
|
|
internal bool IsValid => Token.IsValid
|
|
&& EvaluationId != 0UL
|
|
&& Placement.Placement == Token.Placement;
|
|
}
|
|
|
|
internal readonly record struct RuntimeLocalPlayerPhysicsCandidateSnapshot(
|
|
Vector3 Position,
|
|
Quaternion Orientation,
|
|
uint CellId,
|
|
Vector3 CellLocalPosition,
|
|
PhysicsStateFlags State,
|
|
TransientStateFlags TransientState,
|
|
bool InWorld);
|
|
|
|
public readonly record struct
|
|
RuntimeLocalPlayerPhysicsPublicationOwnershipSnapshot(
|
|
bool IsBound,
|
|
bool IsDisposed,
|
|
int CandidateCount,
|
|
int PendingActivationCount,
|
|
ulong LastPublicationId)
|
|
{
|
|
internal bool IsConverged => !IsBound
|
|
|| (IsDisposed
|
|
&& CandidateCount == 0
|
|
&& PendingActivationCount == 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dormant, presentation-independent owner of local-player body/controller
|
|
/// candidates. Preparation owns a private body and clock. Commit is one
|
|
/// callback-free update-thread transaction which assigns that exact body to
|
|
/// the canonical entity and a dormant movement controller. It deliberately
|
|
/// does not activate the controller or
|
|
/// consume SetPosition or publish world residence, host, shadow, ordinary
|
|
/// workset, FullCell, or presentation state; those belong to the subsequent
|
|
/// activation transaction.
|
|
/// </summary>
|
|
internal sealed class RuntimeLocalPlayerPhysicsPublicationState : IDisposable
|
|
{
|
|
private sealed class Candidate
|
|
{
|
|
internal required RuntimeLocalPlayerPhysicsPublicationToken Token
|
|
{ get; init; }
|
|
internal required RuntimeEntityRecord Record { get; init; }
|
|
internal required RuntimeSetPositionCommand PlacementCommand
|
|
{ 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
|
|
{
|
|
internal required RuntimeLocalPlayerPhysicsActivationToken Token
|
|
{ get; init; }
|
|
internal required RuntimeEntityRecord Record { get; init; }
|
|
internal required RuntimeSetPositionCommand PlacementCommand
|
|
{ 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;
|
|
private readonly RuntimePhysicsState _physics;
|
|
private readonly RuntimeLocalPlayerMovementState _movement;
|
|
private readonly RuntimeLocalPlayerIdentityState _identity;
|
|
private Candidate? _candidate;
|
|
private Activation? _activation;
|
|
private ulong _nextPublicationId;
|
|
private ulong _nextActivationId;
|
|
private ulong _nextEvaluationId;
|
|
private long _activationDispatchFailureCount;
|
|
private bool _disposed;
|
|
|
|
internal RuntimeLocalPlayerPhysicsPublicationState(
|
|
RuntimeEntityDirectory entities,
|
|
RuntimePhysicsState physics,
|
|
RuntimeLocalPlayerMovementState movement,
|
|
RuntimeLocalPlayerIdentityState identity)
|
|
{
|
|
_entities = entities ?? throw new ArgumentNullException(nameof(entities));
|
|
_physics = physics ?? throw new ArgumentNullException(nameof(physics));
|
|
_movement = movement ?? throw new ArgumentNullException(nameof(movement));
|
|
_identity = identity ?? throw new ArgumentNullException(nameof(identity));
|
|
}
|
|
|
|
internal RuntimeLocalPlayerPhysicsPublicationStatus Prepare(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeEntityPlacementToken placement,
|
|
in RuntimeSetPositionCommand command,
|
|
PlayerMovementConstructionOptions options,
|
|
in RuntimeLocalPlayerPhysicsActivationPreparation activationPreparation,
|
|
out RuntimeLocalPlayerPhysicsPublicationToken token)
|
|
{
|
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
ArgumentNullException.ThrowIfNull(record);
|
|
token = default;
|
|
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);
|
|
controller.LocalEntityId = record.Key!.Value.LocalEntityId;
|
|
controller.StepUpHeight = command.Physics.StepUpHeight;
|
|
controller.StepDownHeight = command.Physics.StepDownHeight;
|
|
|
|
controller.SphereList = command.Physics.Spheres;
|
|
controller.ObjectScale = command.Physics.Scale;
|
|
controller.PreparePositionForCommit(
|
|
command.Physics.Position,
|
|
command.Physics.CellId,
|
|
command.Physics.CellLocalPosition);
|
|
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(),
|
|
// App/Runtime movement managers use one normalized world
|
|
// coordinate frame. PhysicsBody.CellPosition deliberately
|
|
// retains retail's landblock-local origin for cell transit;
|
|
// publishing it here made every remote TargetManager chase a
|
|
// different point whenever the player and world origin were
|
|
// not the same landblock.
|
|
getPosition: () => new Position(
|
|
body.CellPosition.ObjCellId,
|
|
body.Position,
|
|
body.Orientation),
|
|
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: () => new Position(
|
|
body.CellPosition.ObjCellId,
|
|
body.Position,
|
|
body.Orientation),
|
|
getVelocity: () => body.Velocity,
|
|
getRadius: () => preparedActivation.Radius,
|
|
inContact: () => body.InContact,
|
|
minterpMaxSpeed: () => motion.GetAdjustedMaxSpeed(),
|
|
curTime: () => controller.SimTimeSeconds,
|
|
physicsTimerTime: () => controller.SimTimeSeconds,
|
|
// 2026-08-08 vendor-approach root cause: this seam is retail's
|
|
// CObjectMaint::GetObjectA — it must resolve ANY in-world object
|
|
// so TargetManager.SetTarget's add_voyeur can deliver the
|
|
// immediate initial snapshot that a deferred MoveToObject/
|
|
// TurnToObject needs before it queues a single node (UseTime's
|
|
// object-move gate stays closed until that first
|
|
// HandleUpdateTarget). The previous binding went straight to
|
|
// _physics.TryGetPhysicsHost, which answers only entities whose
|
|
// host is ALREADY installed (remote-motion-bound movers) — a
|
|
// never-animated NPC/static target resolved to null, add_voyeur
|
|
// never ran, and the armed approach sat inert (no nodes, no
|
|
// movement, no completion) until user input or the 10 s
|
|
// staleness timeout cancelled it. ResolveObjectTableHost routes
|
|
// through the host-bound canonical resolver (the SAME
|
|
// lazy-minimal-host lookup every remote host's GetObjectA uses)
|
|
// and falls back to the exact installed-host lookup when no
|
|
// resolver is bound (no-window hosts).
|
|
getObjectA: _physics.ResolveObjectTableHost,
|
|
// C3c: the [autowalk-target]/[autowalk-end] probes moved here
|
|
// with controller construction (previously App-side in
|
|
// PlayerModeController.BuildControllerAndCamera); they stay on
|
|
// the PhysicsDiagnostics owner exactly as before.
|
|
handleUpdateTarget: info =>
|
|
{
|
|
movement.HandleUpdateTarget(info);
|
|
},
|
|
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.
|
|
body.InWorld = false;
|
|
body.TransientState &= ~TransientStateFlags.Active;
|
|
controller.SealPublicationCandidate();
|
|
|
|
// Candidate construction is intentionally private, but every accepted
|
|
// authority is rechecked after it so future content/configuration work
|
|
// cannot accidentally create a callback-shaped stale publication.
|
|
if (!CanPrepare(record, placement, command))
|
|
{
|
|
controller.DiscardRuntimeCandidate();
|
|
return RuntimeLocalPlayerPhysicsPublicationStatus.RejectedAuthority;
|
|
}
|
|
|
|
DiscardCurrent();
|
|
_nextPublicationId = publicationId;
|
|
_nextActivationId = activationId;
|
|
token = new RuntimeLocalPlayerPhysicsPublicationToken(
|
|
record.Key.Value,
|
|
placement,
|
|
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,
|
|
Record = record,
|
|
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 _);
|
|
|
|
internal RuntimeLocalPlayerPhysicsPublicationStatus Commit(
|
|
in RuntimeLocalPlayerPhysicsPublicationToken token,
|
|
out RuntimeLocalPlayerPhysicsActivationToken activationToken)
|
|
{
|
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
activationToken = default;
|
|
if (!token.IsValid
|
|
|| _candidate is not { } candidate
|
|
|| candidate.Token != token)
|
|
{
|
|
return RuntimeLocalPlayerPhysicsPublicationStatus.RejectedToken;
|
|
}
|
|
if (!IsCurrent(candidate))
|
|
{
|
|
DiscardCurrent();
|
|
return RuntimeLocalPlayerPhysicsPublicationStatus.RejectedAuthority;
|
|
}
|
|
|
|
// Seal the exact SetPosition owner before the irreversible no-fail
|
|
// suffix. Any internal invariant failure therefore leaves every
|
|
// canonical body/controller owner untouched.
|
|
_physics.SetPosition.PrepareDormantLocalActivationOwnership(
|
|
candidate.Record,
|
|
candidate.Body,
|
|
candidate.PreparedActivation.Token.Placement);
|
|
|
|
// All validation is complete. The remaining stores are callback-free,
|
|
// non-allocating, and cannot fail on this single Runtime update thread.
|
|
// The controller remains RuntimeOwnedDormant; the subsequent world
|
|
// activation transaction is the only authority allowed to make it live.
|
|
candidate.Controller.CommitRuntimeOwnership(
|
|
candidate.Record.ObjectClock);
|
|
candidate.Record.SetPhysicsBody(candidate.Body);
|
|
_movement.CommitRuntimeOwnedController(candidate.Controller);
|
|
activationToken = candidate.PreparedActivation.Token;
|
|
_activation = candidate.PreparedActivation;
|
|
_candidate = null;
|
|
return RuntimeLocalPlayerPhysicsPublicationStatus.Committed;
|
|
}
|
|
|
|
internal RuntimeLocalPlayerPhysicsActivationStatus EvaluateActivation(
|
|
in RuntimeLocalPlayerPhysicsActivationToken token,
|
|
out RuntimeLocalPlayerPhysicsActivationReceipt receipt)
|
|
{
|
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
receipt = default;
|
|
if (!token.IsValid
|
|
|| _activation is not { } activation
|
|
|| activation.Token != token)
|
|
{
|
|
return RuntimeLocalPlayerPhysicsActivationStatus.RejectedToken;
|
|
}
|
|
if (!IsActivationCurrent(activation))
|
|
{
|
|
DiscardActivation();
|
|
return RuntimeLocalPlayerPhysicsActivationStatus.RejectedAuthority;
|
|
}
|
|
if (!_physics.SetPosition.TryEvaluateDormantLocalActivation(
|
|
activation.Record,
|
|
activation.Body,
|
|
token.Placement,
|
|
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;
|
|
}
|
|
// #357: an evaluation abort while the dormant lease is STILL
|
|
// CURRENT is transient — the collision-authority seal was refused
|
|
// by a still-registered admission (a login recenter admits nine
|
|
// landblocks at once and the placement's ring search can touch a
|
|
// neighbour mid-admission) or a reentrant collision/restriction
|
|
// mutation observed mid-transaction. The operation remains in
|
|
// AwaitingPreparation and the SAME token evaluates successfully
|
|
// once the authority settles, so the correct status is
|
|
// DeferredCell (retry). Falling through to RejectedAuthority here
|
|
// is what terminally dropped the local player from the
|
|
// first-entry conductor and hung login at ready=True with the
|
|
// world never revealed.
|
|
if (ReferenceEquals(_activation, activation)
|
|
&& IsActivationCurrent(activation)
|
|
&& _physics.SetPosition.IsDormantLocalActivationLeaseCurrent(
|
|
activation.Record,
|
|
activation.Body,
|
|
token.Placement,
|
|
activation.PlacementCommand))
|
|
{
|
|
return RuntimeLocalPlayerPhysicsActivationStatus.DeferredCell;
|
|
}
|
|
if (ReferenceEquals(_activation, activation)
|
|
&& !IsActivationCurrent(activation))
|
|
{
|
|
DiscardActivation();
|
|
}
|
|
return RuntimeLocalPlayerPhysicsActivationStatus.RejectedAuthority;
|
|
}
|
|
|
|
receipt = new RuntimeLocalPlayerPhysicsActivationReceipt(
|
|
token,
|
|
checked(++_nextEvaluationId),
|
|
placement);
|
|
activation.Receipt = receipt;
|
|
if (placement.Result.IsDeferred)
|
|
return RuntimeLocalPlayerPhysicsActivationStatus.DeferredCell;
|
|
return placement.Result.IsCommitted
|
|
? RuntimeLocalPlayerPhysicsActivationStatus.Evaluated
|
|
: RuntimeLocalPlayerPhysicsActivationStatus.RejectedPlacement;
|
|
}
|
|
|
|
internal bool IsEvaluationCurrent(
|
|
in RuntimeLocalPlayerPhysicsActivationReceipt receipt)
|
|
{
|
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
if (!receipt.IsValid
|
|
|| _activation is not { } activation
|
|
|| activation.Token != receipt.Token
|
|
|| activation.Receipt != receipt)
|
|
{
|
|
return false;
|
|
}
|
|
return IsActivationCurrent(activation)
|
|
&& _physics.SetPosition.IsDormantLocalEvaluationCurrent(
|
|
activation.Record,
|
|
activation.Body,
|
|
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;
|
|
ArmFirstEntryConstraintLeash(activation);
|
|
SettleFirstEntryGroundContact(activation);
|
|
_physics.SetPosition.DispatchDormantLocalActivationPlacement(committed);
|
|
projection = committed.Projection.Token;
|
|
return committed.Status;
|
|
}
|
|
|
|
/// <summary>
|
|
/// C3c-R1: the login-entry constraint-leash arm the flip deleted with
|
|
/// the App-side <c>CommitPreparedPosition</c> caller. Ordering, with
|
|
/// file:line justification:
|
|
/// <list type="bullet">
|
|
/// <item>NOT at <c>Prepare</c> — <c>PreparePositionForCommit</c> (:219)
|
|
/// runs with <c>publishSharedState: false</c> and the controller's
|
|
/// <c>PositionManager</c> binds only later at :318, so the leash cannot
|
|
/// exist there (nor should it: the position is not accepted yet).</item>
|
|
/// <item>NOT at publication <c>Commit</c> — the activation's placement
|
|
/// evaluation (retail find-placement ring search) may still move or
|
|
/// reject the position.</item>
|
|
/// <item>HERE, after <c>TryApplyDormantLocalActivationFinalCommit</c>
|
|
/// (RuntimeSetPositionState.cs:2494-2516 commits the final cell,
|
|
/// activates the controller, and publishes the shared current cell) and
|
|
/// inside the same <c>IsCommittedActivationSuffixCurrent</c> gate the
|
|
/// settle uses — a stale suffix skips the arm exactly like the settle
|
|
/// (never armed on stale authority).</item>
|
|
/// <item>BEFORE <see cref="SettleFirstEntryGroundContact"/> — retail
|
|
/// arms anchored to the RECEIVED position
|
|
/// (<c>SmartBox::HandleReceivedPosition</c> 0x00453FD0) and only then
|
|
/// simulates the first gravity frame, which the settle compresses; the
|
|
/// anchor is therefore the committed placement, not the post-settle
|
|
/// pose.</item>
|
|
/// <item>Exactly once — <c>_activation</c> is nulled at :716 before
|
|
/// this suffix, so a resumed <c>AwaitingFinalShadowPreparation</c>
|
|
/// retry can never re-enter it after a successful final commit.</item>
|
|
/// </list>
|
|
/// </summary>
|
|
private void ArmFirstEntryConstraintLeash(Activation activation)
|
|
{
|
|
// Same containment as the settle below: the placement commit has
|
|
// already succeeded; a leash-arm failure must not unwind the suffix.
|
|
try
|
|
{
|
|
activation.Controller.ArmConstraintLeashAtCommittedPlacement();
|
|
}
|
|
catch
|
|
{
|
|
_activationDispatchFailureCount++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// C3c-F5: retail seeds the LOCAL player's ground contact from the first
|
|
/// gravity frame after <c>enter_world</c>, never from the placement
|
|
/// itself — <c>SmartBox::HandleCreateObject</c> (0x00454C80) runs
|
|
/// <c>init_player</c> (0x00455010) then <c>CPhysicsObj::enter_world</c>
|
|
/// (0x00455095 → 0x00516170), whose <c>SetPosition</c> validates the
|
|
/// spot but records no touch and whose tail only sets ACTIVE (0x80).
|
|
/// Every retail CPhysicsObj then simulates, falls the few centimetres
|
|
/// onto the floor, and the transition's touch grants the contact plane
|
|
/// + CONTACT/ON_WALKABLE. The dormant activation's just-finished commit
|
|
/// is the faithful SetPosition port, so a fresh login body would start
|
|
/// airborne here; this compresses the settle exactly like the #270
|
|
/// remote-spawn seed (the shared <see cref="SpawnPlacementSettler"/>):
|
|
/// a short downward sweep whose real touch produces the state retail's
|
|
/// first frame would. No floor within reach (a genuine airborne spawn)
|
|
/// leaves the body airborne — the ordinary per-tick gravity fall owns
|
|
/// it from there. The body transients this commits ARE the controller's
|
|
/// grounded state (<c>PlayerMovementController.CanSendPositionEvent</c>
|
|
/// reads <c>InContact && OnWalkable</c> off the same body) and
|
|
/// the outbound wire contact bit (<c>LocalPlayerOutboundController</c>
|
|
/// serializes that predicate) — the flag ACE's "You can't do that while
|
|
/// in the air!" gate reads.
|
|
/// </summary>
|
|
private void SettleFirstEntryGroundContact(Activation activation)
|
|
{
|
|
// Same post-commit callback-dispatch containment as the ground-edge
|
|
// dispatch in CommitActivation: the placement commit has already
|
|
// succeeded; a HitGround-side failure must not unwind the suffix.
|
|
try
|
|
{
|
|
_ = SpawnPlacementSettler.TrySettle(
|
|
_physics.Engine,
|
|
activation.Body,
|
|
activation.Body.Position,
|
|
activation.Body.CellPosition.ObjCellId,
|
|
activation.ActivationPreparation.Radius,
|
|
activation.ActivationPreparation.Height,
|
|
ObjectInfoState.IsPlayer
|
|
| ObjectInfoState.EdgeSlide
|
|
| activation.Controller.OwnPvpFlags,
|
|
activation.Controller.LocalEntityId,
|
|
activation.Movement.HitGround,
|
|
activation.Motion.LeaveGround);
|
|
}
|
|
catch
|
|
{
|
|
_activationDispatchFailureCount++;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
if (!token.IsValid
|
|
|| _activation is not { } activation
|
|
|| activation.Token != token)
|
|
{
|
|
return false;
|
|
}
|
|
DiscardActivation();
|
|
return true;
|
|
}
|
|
|
|
internal RuntimeLocalPlayerPhysicsPublicationStatus Discard(
|
|
in RuntimeLocalPlayerPhysicsPublicationToken token)
|
|
{
|
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
if (!token.IsValid
|
|
|| _candidate is not { } candidate
|
|
|| candidate.Token != token)
|
|
{
|
|
return RuntimeLocalPlayerPhysicsPublicationStatus.RejectedToken;
|
|
}
|
|
DiscardCurrent();
|
|
return RuntimeLocalPlayerPhysicsPublicationStatus.Discarded;
|
|
}
|
|
|
|
internal bool TryCaptureCandidateSnapshot(
|
|
in RuntimeLocalPlayerPhysicsPublicationToken token,
|
|
out RuntimeLocalPlayerPhysicsCandidateSnapshot snapshot)
|
|
{
|
|
if (!_disposed
|
|
&& token.IsValid
|
|
&& _candidate is { } candidate
|
|
&& candidate.Token == token)
|
|
{
|
|
PhysicsBody body = candidate.Body;
|
|
snapshot = new RuntimeLocalPlayerPhysicsCandidateSnapshot(
|
|
body.Position,
|
|
body.Orientation,
|
|
body.CellPosition.ObjCellId,
|
|
body.CellPosition.Frame.Origin,
|
|
body.State,
|
|
body.TransientState,
|
|
body.InWorld);
|
|
return true;
|
|
}
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
internal RuntimeLocalPlayerPhysicsPublicationOwnershipSnapshot
|
|
CaptureOwnership() => new(
|
|
IsBound: true,
|
|
_disposed,
|
|
_candidate is null ? 0 : 1,
|
|
_activation is null ? 0 : 1,
|
|
_nextPublicationId);
|
|
|
|
internal long ActivationDispatchFailureCount =>
|
|
_activationDispatchFailureCount;
|
|
|
|
internal void ResetSession()
|
|
{
|
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
DiscardCurrent();
|
|
DiscardActivation();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
DiscardCurrent();
|
|
DiscardActivation();
|
|
_disposed = true;
|
|
}
|
|
|
|
private bool CanPrepare(
|
|
RuntimeEntityRecord record,
|
|
in RuntimeEntityPlacementToken placement,
|
|
in RuntimeSetPositionCommand command) =>
|
|
_activation is null
|
|
&& record.Key is { } key
|
|
&& key == placement.Entity
|
|
&& _entities.IsCurrent(record)
|
|
&& !record.DeleteAcceptedForTeardown
|
|
&& command.Kind is RuntimeSetPositionOperationKind.InitialLogin
|
|
or RuntimeSetPositionOperationKind.LocalAuthoritative
|
|
&& command.Physics.MovingEntityId == key.LocalEntityId
|
|
&& !_identity.IsDisposed
|
|
&& _identity.ServerGuid != 0u
|
|
&& _identity.ServerGuid == record.ServerGuid
|
|
&& record.PhysicsBody is null
|
|
&& _movement.Controller is null
|
|
&& record.PhysicsHost is null
|
|
&& record.RemoteMotion is null
|
|
&& record.Projectile is null
|
|
&& !record.PhysicsBodyAcquisitionInProgress
|
|
&& !record.RemoteMotionBindingInProgress
|
|
&& !record.ProjectileBindingInProgress
|
|
&& !record.RequiresRemotePlacementRuntime
|
|
&& _physics.SetPosition.IsExactPreparedPlacementCurrent(
|
|
record,
|
|
placement,
|
|
command);
|
|
|
|
private bool IsCurrent(Candidate candidate) =>
|
|
_activation is null
|
|
&& candidate.Controller.IsSealedPublicationCandidate
|
|
&& candidate.Controller.OwnsPhysicsBody(candidate.Body)
|
|
&& _entities.SessionLifetimeVersion
|
|
== candidate.Token.SessionGenerationAuthority
|
|
&& _entities.IsCurrent(candidate.Record)
|
|
&& candidate.Record.Key == candidate.Token.Entity
|
|
&& !_identity.IsDisposed
|
|
&& _identity.ServerGuid == candidate.Token.LocalPlayerServerGuid
|
|
&& _identity.ServerGuid == candidate.Record.ServerGuid
|
|
&& _identity.Revision == candidate.Token.LocalPlayerIdentityRevision
|
|
&& candidate.Record.PhysicsOwnershipEpoch
|
|
== candidate.Token.PhysicsOwnershipEpoch
|
|
&& candidate.Record.ObjectClockEpoch
|
|
== candidate.Token.ObjectClockEpoch
|
|
&& _movement.CanCommitRuntimeOwnedController(
|
|
candidate.Token.ControllerOwnershipEpoch,
|
|
expectedController: null)
|
|
&& candidate.Record.PhysicsBody is null
|
|
&& candidate.Record.PhysicsHost is null
|
|
&& candidate.Record.RemoteMotion is null
|
|
&& candidate.Record.Projectile is null
|
|
&& !candidate.Record.PhysicsBodyAcquisitionInProgress
|
|
&& !candidate.Record.RemoteMotionBindingInProgress
|
|
&& !candidate.Record.ProjectileBindingInProgress
|
|
&& !candidate.Record.RequiresRemotePlacementRuntime
|
|
&& !candidate.Record.DeleteAcceptedForTeardown
|
|
&& _physics.SetPosition.IsExactPreparedPlacementCurrent(
|
|
candidate.Record,
|
|
candidate.Token.Placement,
|
|
candidate.PlacementCommand);
|
|
|
|
private void DiscardCurrent()
|
|
{
|
|
Candidate? candidate = _candidate;
|
|
_candidate = null;
|
|
candidate?.Controller.DiscardRuntimeCandidate();
|
|
}
|
|
|
|
private bool IsActivationCurrent(Activation activation) =>
|
|
activation.Controller.IsRuntimeOwnedDormant
|
|
&& 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
|
|
&& _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(
|
|
activation.Record.PhysicsBody,
|
|
activation.Body))
|
|
{
|
|
_entities.SetPhysicsBody(activation.Record, null);
|
|
}
|
|
if (activation is not null
|
|
&& ReferenceEquals(_movement.Controller, activation.Controller))
|
|
{
|
|
_movement.Controller = null;
|
|
}
|
|
}
|
|
}
|