feat(runtime): commit dormant SetPosition activation
This commit is contained in:
parent
99f867f053
commit
5785a07b3e
13 changed files with 4674 additions and 111 deletions
|
|
@ -4,6 +4,7 @@ using AcDream.Core.Items;
|
|||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.Runtime.Physics;
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ internal enum RuntimeEntityPlacementStage
|
|||
AwaitingPreparation,
|
||||
AwaitingWithdrawalAcknowledgement,
|
||||
AwaitingCell,
|
||||
AwaitingFinalShadowPreparation,
|
||||
AwaitingCommitAcknowledgement,
|
||||
CancelledAwaitingAcknowledgement,
|
||||
}
|
||||
|
|
@ -109,6 +111,65 @@ internal readonly record struct RuntimeDormantSetPositionEvaluation(
|
|||
&& CollisionAuthority.IsValid;
|
||||
}
|
||||
|
||||
internal enum RuntimeDormantSetPositionCommitStatus : byte
|
||||
{
|
||||
None,
|
||||
AwaitingFinalShadowPreparation,
|
||||
Committed,
|
||||
DeferredCell,
|
||||
RejectedPlacement,
|
||||
RejectedAuthority,
|
||||
}
|
||||
|
||||
internal sealed class PreparedDormantSetPositionCommit
|
||||
{
|
||||
internal required RuntimeDormantSetPositionEvaluation Evaluation
|
||||
{ get; init; }
|
||||
internal required RuntimeEntityKey Entity { get; init; }
|
||||
internal required ulong OperationId { get; init; }
|
||||
internal required ulong ExpectedProjectionSequence { get; init; }
|
||||
internal required ShadowObjectRegistry.PreparedSetPositionShadowCommit?
|
||||
Shadow { get; init; }
|
||||
internal required RuntimeCollisionReportingState
|
||||
.PreparedSetPositionCollisionBatch? Collision { get; init; }
|
||||
internal required RuntimePlacementProjectionSnapshot Projection
|
||||
{ get; init; }
|
||||
internal required SortedDictionary<ulong, RuntimePlacementProjectionSnapshot>?
|
||||
PendingProjection { get; init; }
|
||||
internal required ulong DeferredCollisionGeneration { get; init; }
|
||||
internal required List<RuntimeEntityKey>? DeferredBucket { get; init; }
|
||||
internal required bool DeferredBucketIsNew { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class PreparedDormantActivationFinalCommit
|
||||
{
|
||||
internal required RuntimeEntityKey Entity { get; init; }
|
||||
internal required ulong OperationId { get; init; }
|
||||
internal required ulong ExpectedProjectionSequence { get; init; }
|
||||
internal required ShadowObjectRegistry.PreparedSetPositionShadowCommit
|
||||
Shadow { get; init; }
|
||||
internal required RuntimePlacementProjectionSnapshot Projection
|
||||
{ get; init; }
|
||||
internal required SortedDictionary<ulong, RuntimePlacementProjectionSnapshot>
|
||||
PendingProjection { get; init; }
|
||||
}
|
||||
|
||||
internal readonly record struct RuntimeDormantSetPositionCommitReceipt(
|
||||
RuntimeDormantSetPositionCommitStatus Status,
|
||||
RuntimeEntityKey Entity,
|
||||
ulong OperationId,
|
||||
RuntimePlacementProjectionSnapshot Projection,
|
||||
RuntimeCollisionReportingState.SetPositionCollisionBatchReceipt Collision,
|
||||
ShadowObjectRegistry.SetPositionShadowCommitReceipt Shadow,
|
||||
RuntimeCollisionEvaluationAuthority CollisionAuthority,
|
||||
ulong SourceVectorAuthorityVersion,
|
||||
bool HitGround,
|
||||
bool LeaveGround)
|
||||
{
|
||||
internal bool IsCommitted => Status
|
||||
is RuntimeDormantSetPositionCommitStatus.Committed;
|
||||
}
|
||||
|
||||
public readonly record struct RuntimePlacementProjectionToken(
|
||||
ulong Sequence,
|
||||
ulong Revision,
|
||||
|
|
@ -258,6 +319,7 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
internal List<RuntimeEntityKey>? LostFamilyKeys { get; set; }
|
||||
internal bool InheritedLostDeadline { get; set; }
|
||||
internal bool EnteringWorldFromCelllessResidence { get; set; }
|
||||
internal bool DormantLocalActivation { get; set; }
|
||||
internal RuntimeSetPositionCommand? PreparedCommandAwaitingWithdrawalAck
|
||||
{
|
||||
get;
|
||||
|
|
@ -289,7 +351,7 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
private readonly Dictionary<RuntimeEntityKey, Operation> _operations = [];
|
||||
private readonly Dictionary<CellGenerationKey, List<RuntimeEntityKey>>
|
||||
_deferredByCellGeneration = [];
|
||||
private readonly SortedDictionary<ulong, RuntimePlacementProjectionSnapshot>
|
||||
private SortedDictionary<ulong, RuntimePlacementProjectionSnapshot>
|
||||
_pendingProjection = [];
|
||||
private readonly List<CellGenerationKey> _deferredBucketOrder = [];
|
||||
private readonly Dictionary<uint, List<RuntimeEntityKey>>
|
||||
|
|
@ -558,8 +620,12 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
if (!token.IsValid
|
||||
|| !_operations.TryGetValue(token.Entity, out Operation? operation)
|
||||
|| operation.Token != token
|
||||
|| operation.Stage
|
||||
is not RuntimeEntityPlacementStage.AwaitingPreparation
|
||||
|| operation.Stage is not (
|
||||
RuntimeEntityPlacementStage.AwaitingPreparation
|
||||
or RuntimeEntityPlacementStage.AwaitingCell)
|
||||
|| operation.Stage is RuntimeEntityPlacementStage.AwaitingCell
|
||||
&& (!operation.DormantLocalActivation
|
||||
|| !operation.WakeableLostCell)
|
||||
|| !IsCurrent(operation)
|
||||
|| !_moverPreparationAuthorities.TryGetValue(
|
||||
token.Entity,
|
||||
|
|
@ -641,6 +707,20 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
ArgumentNullException.ThrowIfNull(record);
|
||||
ArgumentNullException.ThrowIfNull(body);
|
||||
evaluation = default;
|
||||
if (!IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
token,
|
||||
command,
|
||||
out _)
|
||||
&& !TryRearmDeferredDormantLocalActivation(
|
||||
record,
|
||||
body,
|
||||
token,
|
||||
command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
|
|
@ -716,6 +796,779 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
&& _physics.IsCollisionEvaluationAuthorityCurrent(
|
||||
evaluation.CollisionAuthority);
|
||||
|
||||
internal bool IsDormantLocalActivationLeaseCurrent(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command) =>
|
||||
IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
token,
|
||||
command,
|
||||
out _,
|
||||
allowDeferredLease: true);
|
||||
|
||||
internal bool IsDormantLocalActivationAwaitingCell(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command)
|
||||
{
|
||||
return IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
token,
|
||||
command,
|
||||
out Operation? operation,
|
||||
allowDeferredLease: true)
|
||||
&& operation is not null
|
||||
&& operation.Stage is RuntimeEntityPlacementStage.AwaitingCell
|
||||
&& operation.DormantLocalActivation
|
||||
&& operation.WakeableLostCell;
|
||||
}
|
||||
|
||||
private bool TryRearmDeferredDormantLocalActivation(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command)
|
||||
{
|
||||
if (!IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
token,
|
||||
command,
|
||||
out Operation? operation,
|
||||
allowDeferredLease: true)
|
||||
|| operation is null
|
||||
|| operation.Stage is not RuntimeEntityPlacementStage.AwaitingCell
|
||||
|| !operation.DormantLocalActivation
|
||||
|| !operation.WakeableLostCell
|
||||
|| !operation.CollisionGenerationReady
|
||||
|| operation.ProjectionSequence != 0UL
|
||||
|| operation.CollisionGeneration != _physics
|
||||
.ExpectedCollisionGeneration(operation.ExactCellId)
|
||||
|| !_physics.Engine.IsSpawnCellReady(operation.ExactCellId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UnindexDeferred(operation);
|
||||
operation.WakeableLostCell = false;
|
||||
operation.CollisionGenerationReady = false;
|
||||
operation.Stage = RuntimeEntityPlacementStage.AwaitingPreparation;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool TryPrepareDormantLocalActivationCommit(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionEvaluation evaluation,
|
||||
bool provenShapeless,
|
||||
out PreparedDormantSetPositionCommit? prepared)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
ArgumentNullException.ThrowIfNull(body);
|
||||
prepared = null;
|
||||
if (!IsDormantLocalEvaluationCurrent(record, body, evaluation)
|
||||
|| !IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
evaluation.Placement,
|
||||
evaluation.Command,
|
||||
out Operation? operation,
|
||||
allowCanonicalCommand: true)
|
||||
|| operation is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PhysicsSetPositionResult result = evaluation.Result;
|
||||
ShadowObjectRegistry.PreparedSetPositionShadowCommit? shadow = null;
|
||||
RuntimeCollisionReportingState.PreparedSetPositionCollisionBatch?
|
||||
collision = null;
|
||||
RuntimePlacementProjectionSnapshot projection = default;
|
||||
SortedDictionary<ulong, RuntimePlacementProjectionSnapshot>?
|
||||
pendingProjection = null;
|
||||
ulong deferredCollisionGeneration = 0UL;
|
||||
List<RuntimeEntityKey>? deferredBucket = null;
|
||||
bool deferredBucketIsNew = false;
|
||||
|
||||
if (!result.IsDeferred)
|
||||
{
|
||||
if (!_physics.CollisionReports.TryPrepareSetPositionBatch(
|
||||
record,
|
||||
body,
|
||||
evaluation.Command.GameTime,
|
||||
result.IsCommitted && operation.PreviousContact,
|
||||
result.IsCommitted && operation.PreviousOnWalkable,
|
||||
result.IsCommitted && result.OnWalkable,
|
||||
result.CollidedWithEnvironment,
|
||||
result.CollidedObjectIds,
|
||||
out collision)
|
||||
|| collision is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.IsDeferred)
|
||||
{
|
||||
deferredCollisionGeneration = _physics
|
||||
.ExpectedCollisionGeneration(result.CellId);
|
||||
_preparedMovers.EnsureCapacity(_preparedMovers.Count + 1);
|
||||
if (result.CellId != 0u && deferredCollisionGeneration != 0UL)
|
||||
{
|
||||
var bucketKey = new CellGenerationKey(
|
||||
result.CellId,
|
||||
deferredCollisionGeneration);
|
||||
if (_deferredByCellGeneration.TryGetValue(
|
||||
bucketKey,
|
||||
out deferredBucket))
|
||||
{
|
||||
deferredBucket.EnsureCapacity(deferredBucket.Count + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
deferredBucket = [operation.Key];
|
||||
deferredBucketIsNew = true;
|
||||
_deferredByCellGeneration.EnsureCapacity(
|
||||
_deferredByCellGeneration.Count + 1);
|
||||
_deferredBucketOrder.EnsureCapacity(
|
||||
_deferredBucketOrder.Count + 1);
|
||||
}
|
||||
}
|
||||
if (!_physics.Engine.ShadowObjects.TryPrepareSetPosition(
|
||||
operation.Key.LocalEntityId,
|
||||
result.Position,
|
||||
result.Orientation,
|
||||
result.CellId,
|
||||
evaluation.Command.ShadowWorldOffsetX,
|
||||
evaluation.Command.ShadowWorldOffsetY,
|
||||
PhysicsShadowCommitAction.Preserve,
|
||||
ImmutableArray<uint>.Empty,
|
||||
provenShapeless,
|
||||
suspendOwner: true,
|
||||
out shadow)
|
||||
|| shadow is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ulong expectedProjectionSequence = _nextProjectionSequence;
|
||||
if (result.IsCommitted)
|
||||
{
|
||||
_ = checked(record.ObjectClockEpoch + 1UL);
|
||||
_ = checked(record.PlacementCommitVersion + 1UL);
|
||||
if (record.FullCellId != result.CellId)
|
||||
_ = checked(record.SpatialAuthorityVersion + 1UL);
|
||||
if (!_physics.TryPrepareSpatialRootAdmission(record))
|
||||
return false;
|
||||
|
||||
ulong sequence = checked(expectedProjectionSequence + 1UL);
|
||||
ulong spatial = record.SpatialAuthorityVersion
|
||||
+ (record.FullCellId == result.CellId ? 0UL : 1UL);
|
||||
ulong placement = checked(record.PlacementCommitVersion + 1UL);
|
||||
var token = new RuntimePlacementProjectionToken(
|
||||
sequence,
|
||||
Revision: 1UL,
|
||||
operation.Key,
|
||||
operation.PositionAuthorityVersion,
|
||||
spatial,
|
||||
placement,
|
||||
operation.SessionLifetimeVersion,
|
||||
result.CellId,
|
||||
operation.CollisionGeneration,
|
||||
evaluation.Command.Portal);
|
||||
projection = new RuntimePlacementProjectionSnapshot(
|
||||
token,
|
||||
RuntimePlacementProjectionKind.Place,
|
||||
result.Position,
|
||||
result.Orientation,
|
||||
result.CellLocalPosition,
|
||||
result.InContact,
|
||||
result.OnWalkable);
|
||||
pendingProjection = new SortedDictionary<
|
||||
ulong,
|
||||
RuntimePlacementProjectionSnapshot>(_pendingProjection)
|
||||
{
|
||||
[sequence] = projection,
|
||||
};
|
||||
}
|
||||
|
||||
prepared = new PreparedDormantSetPositionCommit
|
||||
{
|
||||
Evaluation = evaluation,
|
||||
Entity = operation.Key,
|
||||
OperationId = operation.Token.OperationId,
|
||||
ExpectedProjectionSequence = expectedProjectionSequence,
|
||||
Shadow = shadow,
|
||||
Collision = collision,
|
||||
Projection = projection,
|
||||
PendingProjection = pendingProjection,
|
||||
DeferredCollisionGeneration = deferredCollisionGeneration,
|
||||
DeferredBucket = deferredBucket,
|
||||
DeferredBucketIsNew = deferredBucketIsNew,
|
||||
};
|
||||
return IsPreparedDormantCommitCurrent(record, body, prepared);
|
||||
}
|
||||
|
||||
internal bool TryApplyDormantLocalActivationCommit(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
PlayerMovementController controller,
|
||||
EntityPhysicsHost physicsHost,
|
||||
PreparedDormantSetPositionCommit prepared,
|
||||
out RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
ArgumentNullException.ThrowIfNull(body);
|
||||
ArgumentNullException.ThrowIfNull(controller);
|
||||
ArgumentNullException.ThrowIfNull(physicsHost);
|
||||
ArgumentNullException.ThrowIfNull(prepared);
|
||||
receipt = default;
|
||||
if (!IsPreparedDormantCommitCurrent(record, body, prepared)
|
||||
|| !_operations.TryGetValue(
|
||||
prepared.Entity,
|
||||
out Operation? operation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PhysicsSetPositionResult result = prepared.Evaluation.Result;
|
||||
operation.Body = body;
|
||||
operation.DormantLocalActivation = true;
|
||||
if (result.IsDeferred)
|
||||
{
|
||||
if (prepared.Shadow is null
|
||||
|| !_physics.Engine.ShadowObjects.TryApplySetPosition(
|
||||
prepared.Shadow,
|
||||
out ShadowObjectRegistry.SetPositionShadowCommitReceipt
|
||||
deferredShadowReceipt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
body.Orientation = result.Orientation;
|
||||
body.StageDormantCellFrame(
|
||||
result.CellId,
|
||||
result.Position,
|
||||
result.CellLocalPosition);
|
||||
body.InWorld = false;
|
||||
body.TransientState &= ~TransientStateFlags.Active;
|
||||
operation.Result = result;
|
||||
operation.ExactCellId = result.CellId;
|
||||
operation.WakeableLostCell = true;
|
||||
operation.CollisionGeneration = prepared
|
||||
.DeferredCollisionGeneration;
|
||||
operation.CollisionGenerationReady = false;
|
||||
operation.Stage = RuntimeEntityPlacementStage.AwaitingCell;
|
||||
_preparedMovers[operation.Key] = prepared.Evaluation.Command.Physics;
|
||||
if (prepared.DeferredBucket is { } deferredBucket)
|
||||
{
|
||||
var bucketKey = new CellGenerationKey(
|
||||
result.CellId,
|
||||
prepared.DeferredCollisionGeneration);
|
||||
if (prepared.DeferredBucketIsNew)
|
||||
{
|
||||
_deferredByCellGeneration.Add(bucketKey, deferredBucket);
|
||||
_deferredBucketOrder.Add(bucketKey);
|
||||
}
|
||||
else if (!deferredBucket.Contains(operation.Key))
|
||||
{
|
||||
deferredBucket.Add(operation.Key);
|
||||
}
|
||||
}
|
||||
receipt = new RuntimeDormantSetPositionCommitReceipt(
|
||||
RuntimeDormantSetPositionCommitStatus.DeferredCell,
|
||||
operation.Key,
|
||||
operation.Token.OperationId,
|
||||
default,
|
||||
default,
|
||||
deferredShadowReceipt,
|
||||
prepared.Evaluation.CollisionAuthority,
|
||||
operation.Record.VectorAuthorityVersion,
|
||||
HitGround: false,
|
||||
LeaveGround: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prepared.Collision is null
|
||||
|| !_physics.CollisionReports.TryInstallSetPositionBatch(
|
||||
prepared.Collision,
|
||||
out RuntimeCollisionReportingState
|
||||
.SetPositionCollisionBatchReceipt collisionReceipt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!result.IsCommitted)
|
||||
{
|
||||
operation.Result = result;
|
||||
receipt = new RuntimeDormantSetPositionCommitReceipt(
|
||||
RuntimeDormantSetPositionCommitStatus.RejectedPlacement,
|
||||
operation.Key,
|
||||
operation.Token.OperationId,
|
||||
default,
|
||||
collisionReceipt,
|
||||
default,
|
||||
prepared.Evaluation.CollisionAuthority,
|
||||
operation.Record.VectorAuthorityVersion,
|
||||
HitGround: false,
|
||||
LeaveGround: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool previousOnWalkable = operation.PreviousOnWalkable;
|
||||
bool hitGround = !previousOnWalkable
|
||||
&& result.InContact
|
||||
&& result.OnWalkable;
|
||||
bool leaveGround = previousOnWalkable
|
||||
&& !(result.InContact && result.OnWalkable);
|
||||
|
||||
UnindexDeferred(operation);
|
||||
body.Orientation = result.Orientation;
|
||||
body.StageDormantCellFrame(
|
||||
result.CellId,
|
||||
result.Position,
|
||||
result.CellLocalPosition);
|
||||
body.LastUpdateTime = prepared.Evaluation.Command.GameTime;
|
||||
body.ContactPlaneValid = result.InContact;
|
||||
body.ContactPlane = result.ContactPlane;
|
||||
body.ContactPlaneCellId = result.ContactPlaneCellId;
|
||||
body.ContactPlaneIsWater = result.ContactPlaneIsWater;
|
||||
if (result.InContact)
|
||||
body.GroundNormal = result.ContactPlane.Normal;
|
||||
_ = PhysicsObjUpdate.CommitSetPositionContactPrefix(
|
||||
body,
|
||||
result.InContact,
|
||||
result.OnWalkable,
|
||||
previousOnWalkable);
|
||||
operation.Result = result;
|
||||
operation.ExactCellId = result.CellId;
|
||||
operation.WakeableLostCell = false;
|
||||
operation.CollisionGenerationReady = false;
|
||||
operation.EnteringWorldFromCelllessResidence = false;
|
||||
operation.Stage = RuntimeEntityPlacementStage
|
||||
.AwaitingFinalShadowPreparation;
|
||||
receipt = new RuntimeDormantSetPositionCommitReceipt(
|
||||
RuntimeDormantSetPositionCommitStatus
|
||||
.AwaitingFinalShadowPreparation,
|
||||
operation.Key,
|
||||
operation.Token.OperationId,
|
||||
prepared.Projection,
|
||||
collisionReceipt,
|
||||
default,
|
||||
prepared.Evaluation.CollisionAuthority,
|
||||
operation.Record.VectorAuthorityVersion,
|
||||
hitGround,
|
||||
leaveGround);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal SetPositionCollisionBatchDispatchResult
|
||||
DispatchDormantLocalActivationCollision(
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (receipt.Status
|
||||
is RuntimeDormantSetPositionCommitStatus.DeferredCell
|
||||
or RuntimeDormantSetPositionCommitStatus.RejectedAuthority)
|
||||
{
|
||||
return new(
|
||||
SetPositionCollisionBatchDispatchStatus.RejectedReceipt,
|
||||
Reported: false);
|
||||
}
|
||||
SetPositionCollisionBatchDispatchResult dispatch = _physics
|
||||
.CollisionReports.DispatchSetPositionBatchResult(receipt.Collision);
|
||||
bool reported = dispatch.Reported;
|
||||
if (receipt.Status
|
||||
is RuntimeDormantSetPositionCommitStatus.RejectedPlacement
|
||||
&& _operations.TryGetValue(receipt.Entity, out Operation? operation)
|
||||
&& operation.Token.OperationId == receipt.OperationId
|
||||
&& IsCurrent(operation)
|
||||
&& !operation.Result.IsCommitted
|
||||
&& !operation.Result.IsDeferred)
|
||||
{
|
||||
operation.Result = operation.Result with
|
||||
{
|
||||
Error = reported
|
||||
? PhysicsSetPositionError.Collided
|
||||
: PhysicsSetPositionError.NoValidPosition,
|
||||
CollisionHandlerResult = reported,
|
||||
};
|
||||
}
|
||||
return dispatch;
|
||||
}
|
||||
|
||||
internal bool IsDormantLocalActivationPrephaseCurrent(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
return receipt.Status is RuntimeDormantSetPositionCommitStatus
|
||||
.AwaitingFinalShadowPreparation
|
||||
&& _operations.TryGetValue(receipt.Entity, out Operation? operation)
|
||||
&& operation.Token.OperationId == receipt.OperationId
|
||||
&& operation.Stage is RuntimeEntityPlacementStage
|
||||
.AwaitingFinalShadowPreparation
|
||||
&& operation.DormantLocalActivation
|
||||
&& IsCurrent(operation)
|
||||
&& ReferenceEquals(operation.Record, record)
|
||||
&& ReferenceEquals(record.PhysicsBody, body)
|
||||
&& !body.InWorld
|
||||
&& (body.TransientState & TransientStateFlags.Active) == 0
|
||||
&& record.PhysicsHost is null
|
||||
&& record.RemoteMotion is null
|
||||
&& record.Projectile is null
|
||||
&& !_physics.IsSpatialRoot(record)
|
||||
&& _moverPreparationAuthorities.TryGetValue(
|
||||
receipt.Entity,
|
||||
out MoverPreparationAuthority authority)
|
||||
&& authority.OperationId == receipt.OperationId
|
||||
&& authority.Prepared
|
||||
&& record.PositionAuthorityVersion
|
||||
== authority.PositionAuthorityVersion
|
||||
&& record.ObjDescAuthorityVersion
|
||||
== authority.ObjDescAuthorityVersion
|
||||
&& record.CreateIntegrationVersion
|
||||
== authority.CreateIntegrationVersion
|
||||
&& CanonicalSetupTableId(record) == authority.SetupTableId;
|
||||
}
|
||||
|
||||
internal bool IsDormantLocalActivationResponseCurrent(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (receipt.Status is RuntimeDormantSetPositionCommitStatus
|
||||
.AwaitingFinalShadowPreparation)
|
||||
return IsDormantLocalActivationPrephaseCurrent(record, body, receipt);
|
||||
return receipt.Status is RuntimeDormantSetPositionCommitStatus
|
||||
.RejectedPlacement
|
||||
&& _operations.TryGetValue(receipt.Entity, out Operation? operation)
|
||||
&& operation.Token.OperationId == receipt.OperationId
|
||||
&& operation.DormantLocalActivation
|
||||
&& IsCurrent(operation)
|
||||
&& ReferenceEquals(operation.Record, record)
|
||||
&& ReferenceEquals(record.PhysicsBody, body)
|
||||
&& !operation.Result.IsCommitted
|
||||
&& !operation.Result.IsDeferred
|
||||
&& !body.InWorld
|
||||
&& (body.TransientState & TransientStateFlags.Active) == 0
|
||||
&& record.PhysicsHost is null
|
||||
&& record.RemoteMotion is null
|
||||
&& record.Projectile is null
|
||||
&& !_physics.IsSpatialRoot(record);
|
||||
}
|
||||
|
||||
internal bool CommitDormantLocalActivationPostGround(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (!IsDormantLocalActivationPrephaseCurrent(record, body, receipt))
|
||||
return false;
|
||||
PhysicsObjUpdate.CommitSetPositionPostGround(body);
|
||||
PhysicsSetPositionResult result = _operations[receipt.Entity].Result;
|
||||
body.SlidingNormal = result.SlidingNormal;
|
||||
if (result.SlidingNormalValid)
|
||||
body.TransientState |= TransientStateFlags.Sliding;
|
||||
else
|
||||
body.TransientState &= ~TransientStateFlags.Sliding;
|
||||
return IsDormantLocalActivationPrephaseCurrent(record, body, receipt);
|
||||
}
|
||||
|
||||
internal bool CommitDormantLocalActivationPostCollision(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (!_operations.TryGetValue(receipt.Entity, out Operation? operation)
|
||||
|| operation.Token.OperationId != receipt.OperationId
|
||||
|| !IsCurrent(operation)
|
||||
|| !ReferenceEquals(operation.Record, record)
|
||||
|| !ReferenceEquals(record.PhysicsBody, body))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (receipt.Status is RuntimeDormantSetPositionCommitStatus
|
||||
.AwaitingFinalShadowPreparation
|
||||
&& !IsDormantLocalActivationPrephaseCurrent(record, body, receipt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PhysicsSetPositionResult result = operation.Result;
|
||||
body.FramesStationaryFall = result.FramesStationaryFall;
|
||||
if (IsVelocityCurrent(operation))
|
||||
{
|
||||
PhysicsObjUpdate.HandleAllCollisions(
|
||||
body,
|
||||
result.CollisionNormalValid,
|
||||
result.CollisionNormal,
|
||||
operation.PreviousContact,
|
||||
operation.PreviousOnWalkable,
|
||||
body.OnWalkable);
|
||||
}
|
||||
CommitStationaryBits(body, result.FramesStationaryFall);
|
||||
return IsCurrent(operation);
|
||||
}
|
||||
|
||||
internal bool TryPrepareDormantLocalActivationFinalCommit(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt,
|
||||
bool provenShapeless,
|
||||
out PreparedDormantActivationFinalCommit? prepared)
|
||||
{
|
||||
prepared = null;
|
||||
if (!IsDormantLocalActivationPrephaseCurrent(record, body, receipt)
|
||||
|| !_operations.TryGetValue(receipt.Entity, out Operation? operation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PhysicsSetPositionResult result = operation.Result;
|
||||
if (!_physics.Engine.ShadowObjects.TryPrepareSetPosition(
|
||||
operation.Key.LocalEntityId,
|
||||
result.Position,
|
||||
result.Orientation,
|
||||
result.CellId,
|
||||
operation.Command.ShadowWorldOffsetX,
|
||||
operation.Command.ShadowWorldOffsetY,
|
||||
result.ShadowAction,
|
||||
result.CrossCellIds,
|
||||
provenShapeless,
|
||||
suspendOwner: false,
|
||||
out ShadowObjectRegistry.PreparedSetPositionShadowCommit? shadow)
|
||||
|| shadow is null
|
||||
|| _nextProjectionSequence + 1UL
|
||||
!= receipt.Projection.Token.Sequence)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var pending = new SortedDictionary<
|
||||
ulong,
|
||||
RuntimePlacementProjectionSnapshot>(_pendingProjection)
|
||||
{
|
||||
[receipt.Projection.Token.Sequence] = receipt.Projection,
|
||||
};
|
||||
prepared = new PreparedDormantActivationFinalCommit
|
||||
{
|
||||
Entity = receipt.Entity,
|
||||
OperationId = receipt.OperationId,
|
||||
ExpectedProjectionSequence = _nextProjectionSequence,
|
||||
Shadow = shadow,
|
||||
Projection = receipt.Projection,
|
||||
PendingProjection = pending,
|
||||
};
|
||||
bool current = IsDormantLocalActivationPrephaseCurrent(
|
||||
record, body, receipt);
|
||||
bool shadowCurrent = _physics.Engine.ShadowObjects
|
||||
.IsPreparedSetPositionCurrent(shadow);
|
||||
return current && shadowCurrent;
|
||||
}
|
||||
|
||||
internal bool TryApplyDormantLocalActivationFinalCommit(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
PlayerMovementController controller,
|
||||
EntityPhysicsHost physicsHost,
|
||||
in RuntimeDormantSetPositionCommitReceipt prephase,
|
||||
PreparedDormantActivationFinalCommit prepared,
|
||||
out RuntimeDormantSetPositionCommitReceipt committed)
|
||||
{
|
||||
committed = default;
|
||||
if (prepared.Entity != prephase.Entity
|
||||
|| prepared.OperationId != prephase.OperationId
|
||||
|| prepared.ExpectedProjectionSequence != _nextProjectionSequence
|
||||
|| prepared.Projection != prephase.Projection
|
||||
|| !IsDormantLocalActivationPrephaseCurrent(record, body, prephase)
|
||||
|| !_physics.Engine.ShadowObjects.TryApplySetPosition(
|
||||
prepared.Shadow,
|
||||
out ShadowObjectRegistry.SetPositionShadowCommitReceipt shadow))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Operation operation = _operations[prephase.Entity];
|
||||
PhysicsSetPositionResult result = operation.Result;
|
||||
if (record.FullCellId != result.CellId)
|
||||
{
|
||||
_entities.SetFullCell(record, result.CellId,
|
||||
(result.CellId & 0xFFFF0000u) | 0xFFFFu);
|
||||
}
|
||||
operation.SpatialAuthorityVersion = record.SpatialAuthorityVersion;
|
||||
_entities.AdvancePlacementCommit(record);
|
||||
operation.PlacementCommitVersion = record.PlacementCommitVersion;
|
||||
body.InWorld = true;
|
||||
bool isStatic = (record.FinalPhysicsState & PhysicsStateFlags.Static) != 0;
|
||||
if (!isStatic)
|
||||
body.TransientState |= TransientStateFlags.Active;
|
||||
_entities.SetPhysicsHost(record, physicsHost);
|
||||
controller.CommitRuntimeActivationFrame();
|
||||
_physics.Engine.UpdatePlayerCurrCell(result.CellId);
|
||||
_physics.AcknowledgeSpatialProjection(record, spatial: true);
|
||||
_entities.ResetObjectClockForEnterWorld(record, isStatic);
|
||||
operation.Stage = RuntimeEntityPlacementStage.AwaitingCommitAcknowledgement;
|
||||
operation.ProjectionSequence = prepared.Projection.Token.Sequence;
|
||||
_pendingProjection = prepared.PendingProjection;
|
||||
_nextProjectionSequence = prepared.Projection.Token.Sequence;
|
||||
CancelLostFamilyDeadlines(operation);
|
||||
controller.ActivateRuntimePublication();
|
||||
committed = prephase with
|
||||
{
|
||||
Status = RuntimeDormantSetPositionCommitStatus.Committed,
|
||||
Shadow = shadow,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void DispatchDormantLocalActivationShadow(
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (receipt.Status is not (
|
||||
RuntimeDormantSetPositionCommitStatus.Committed
|
||||
or RuntimeDormantSetPositionCommitStatus.DeferredCell))
|
||||
return;
|
||||
_physics.Engine.ShadowObjects.DispatchSetPositionCommit(receipt.Shadow);
|
||||
}
|
||||
|
||||
internal void DispatchDormantLocalActivationPlacement(
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (!receipt.IsCommitted)
|
||||
return;
|
||||
PublishPlacement(receipt.Projection);
|
||||
}
|
||||
|
||||
internal void DiscardDormantLocalActivationDispatches(
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt,
|
||||
bool collisionAlreadyDispatched)
|
||||
{
|
||||
if (!collisionAlreadyDispatched)
|
||||
_physics.CollisionReports.DiscardSetPositionBatch(receipt.Collision);
|
||||
_physics.Engine.ShadowObjects.DiscardSetPositionCommit(receipt.Shadow);
|
||||
}
|
||||
|
||||
internal void RetireDormantLocalActivation(
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt,
|
||||
bool collisionAlreadyDispatched)
|
||||
{
|
||||
DiscardDormantLocalActivationDispatches(
|
||||
receipt,
|
||||
collisionAlreadyDispatched);
|
||||
_physics.CollisionReports.RetireSetPositionBatchOwner(
|
||||
receipt.Collision);
|
||||
if (_operations.TryGetValue(receipt.Entity, out Operation? operation)
|
||||
&& operation.Token.OperationId == receipt.OperationId)
|
||||
{
|
||||
_ = CancelCore(operation);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RetireDormantLocalActivationToken(
|
||||
RuntimeEntityRecord record,
|
||||
in RuntimeEntityPlacementToken token)
|
||||
{
|
||||
if (!token.IsValid
|
||||
|| record.Key != token.Entity
|
||||
|| !_operations.TryGetValue(token.Entity, out Operation? operation)
|
||||
|| operation.Token != token
|
||||
|| !ReferenceEquals(operation.Record, record))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ = CancelCore(operation);
|
||||
}
|
||||
|
||||
internal bool IsDormantLocalActivationCommitCurrent(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionCommitReceipt receipt)
|
||||
{
|
||||
if (!receipt.IsCommitted
|
||||
|| !receipt.Projection.Token.IsValid
|
||||
|| record.Key != receipt.Projection.Token.Entity
|
||||
|| !ReferenceEquals(record.PhysicsBody, body)
|
||||
|| !_pendingProjection.TryGetValue(
|
||||
receipt.Projection.Token.Sequence,
|
||||
out RuntimePlacementProjectionSnapshot pending)
|
||||
|| pending != receipt.Projection
|
||||
|| !_operations.TryGetValue(
|
||||
receipt.Projection.Token.Entity,
|
||||
out Operation? operation)
|
||||
|| operation.Stage is not RuntimeEntityPlacementStage
|
||||
.AwaitingCommitAcknowledgement
|
||||
|| operation.ProjectionSequence
|
||||
!= receipt.Projection.Token.Sequence
|
||||
|| !IsCurrent(operation)
|
||||
|| !body.InWorld
|
||||
|| !_physics.IsSpatialRoot(record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool TryCaptureDormantLocalActivationResult(
|
||||
in RuntimeEntityPlacementToken token,
|
||||
out PhysicsSetPositionResult result)
|
||||
{
|
||||
if (!_disposed
|
||||
&& token.IsValid
|
||||
&& _operations.TryGetValue(token.Entity, out Operation? operation)
|
||||
&& operation.Token == token)
|
||||
{
|
||||
result = operation.Result;
|
||||
return true;
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsPreparedDormantCommitCurrent(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
PreparedDormantSetPositionCommit prepared)
|
||||
{
|
||||
if (_nextProjectionSequence != prepared.ExpectedProjectionSequence
|
||||
|| !IsDormantLocalEvaluationCurrent(
|
||||
record,
|
||||
body,
|
||||
prepared.Evaluation)
|
||||
|| !_operations.TryGetValue(
|
||||
prepared.Entity,
|
||||
out Operation? operation)
|
||||
|| operation.Token.OperationId != prepared.OperationId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (prepared.Collision is not null
|
||||
&& !_physics.CollisionReports.IsPreparedSetPositionBatchCurrent(
|
||||
prepared.Collision))
|
||||
return false;
|
||||
return prepared.Shadow is null
|
||||
|| _physics.Engine.ShadowObjects.IsPreparedSetPositionCurrent(
|
||||
prepared.Shadow);
|
||||
}
|
||||
|
||||
private static void CommitStationaryBits(
|
||||
PhysicsBody body,
|
||||
int framesStationaryFall)
|
||||
{
|
||||
body.TransientState &= ~(TransientStateFlags.StationaryFall
|
||||
| TransientStateFlags.StationaryStop
|
||||
| TransientStateFlags.StationaryStuck);
|
||||
body.TransientState |= framesStationaryFall switch
|
||||
{
|
||||
1 => TransientStateFlags.StationaryFall,
|
||||
2 => TransientStateFlags.StationaryStop,
|
||||
3 => TransientStateFlags.StationaryStuck,
|
||||
_ => TransientStateFlags.None,
|
||||
};
|
||||
}
|
||||
|
||||
internal RuntimeSetPositionOutcome SubmitPreparedPlacement(
|
||||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command) =>
|
||||
|
|
@ -1559,6 +2412,12 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
|
||||
private void RetryDeferred(Operation operation)
|
||||
{
|
||||
// The local-player activation lease owns its dormant body/controller
|
||||
// and must re-enter through the same sealed evaluation/commit path.
|
||||
// A collision-generation wake only marks readiness; it must never
|
||||
// bypass that path through the ordinary remote CommitCanonical tail.
|
||||
if (operation.DormantLocalActivation)
|
||||
return;
|
||||
if (!IsCurrent(operation)
|
||||
|| !operation.WakeableLostCell
|
||||
|| operation.RequiresPreparation
|
||||
|
|
@ -2006,7 +2865,8 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command,
|
||||
out Operation? operation,
|
||||
bool allowCanonicalCommand = false)
|
||||
bool allowCanonicalCommand = false,
|
||||
bool allowDeferredLease = false)
|
||||
{
|
||||
operation = null;
|
||||
if (!token.IsValid
|
||||
|
|
@ -2021,6 +2881,14 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
|| operation.Token != token
|
||||
|| operation.Stage
|
||||
is not RuntimeEntityPlacementStage.AwaitingPreparation
|
||||
&& !(allowDeferredLease
|
||||
&& operation.DormantLocalActivation
|
||||
&& (operation.Stage
|
||||
is RuntimeEntityPlacementStage.AwaitingCell
|
||||
&& operation.WakeableLostCell
|
||||
|| operation.Stage is RuntimeEntityPlacementStage
|
||||
.AwaitingFinalShadowPreparation)
|
||||
&& operation.ProjectionSequence == 0UL)
|
||||
|| !ReferenceEquals(operation.Record, record)
|
||||
|| !IsCurrent(operation)
|
||||
|| !ReferenceEquals(record.PhysicsBody, body)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue