feat(runtime): seal dormant SetPosition evaluations
This commit is contained in:
parent
22651c823d
commit
99f867f053
16 changed files with 2298 additions and 62 deletions
|
|
@ -1,3 +1,5 @@
|
|||
using System.Collections.Immutable;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime.Entities;
|
||||
|
||||
|
|
@ -1042,6 +1044,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
private bool _suppressCollisionOwnerJournal;
|
||||
private long _nextCollisionPreparationSequence;
|
||||
private long _latestCollisionPreparationStartSequence;
|
||||
private ulong _collisionWorldAuthority = 1UL;
|
||||
private readonly List<Action<RuntimeCollisionGenerationCommitted>>
|
||||
_collisionGenerationCommittedObservers = new();
|
||||
private bool _disposed;
|
||||
|
|
@ -1898,6 +1901,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
EnsureNotDisposed();
|
||||
EnsureCollisionMutationThread();
|
||||
uint canonical = CanonicalLandblock(landblockId);
|
||||
AdvanceCollisionWorldAuthority();
|
||||
if (_collisionAdmissions.Remove(
|
||||
canonical,
|
||||
out RuntimeCollisionAdmission? superseded))
|
||||
|
|
@ -2013,6 +2017,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
admission.Generation);
|
||||
_collisionGenerations[admission.LandblockId] = checked(
|
||||
admission.Generation + 1UL);
|
||||
AdvanceCollisionWorldAuthority();
|
||||
}
|
||||
TrimCollisionOwnerJournal();
|
||||
}
|
||||
|
|
@ -2226,6 +2231,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
try
|
||||
{
|
||||
Engine.CommitLandblockReplacement(replacement);
|
||||
AdvanceCollisionWorldAuthority();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -2496,6 +2502,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
|
||||
private void InvalidateCollisionAdmission(uint landblockId)
|
||||
{
|
||||
AdvanceCollisionWorldAuthority();
|
||||
ulong currentGeneration = _collisionGenerations.TryGetValue(
|
||||
landblockId,
|
||||
out ulong current)
|
||||
|
|
@ -2540,6 +2547,130 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
: 1UL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exact collision-prefix generation authority used by private
|
||||
/// SetPosition evaluations. Beginning a replacement generation advances
|
||||
/// this authority immediately, so a receipt cannot survive a prepared
|
||||
/// world replacement and later observe different collision rows.
|
||||
/// </summary>
|
||||
internal ulong CollisionGenerationAuthority(uint exactCellId)
|
||||
{
|
||||
uint landblockId = CanonicalLandblock(exactCellId);
|
||||
return landblockId != 0u
|
||||
&& _collisionGenerations.TryGetValue(
|
||||
landblockId,
|
||||
out ulong generation)
|
||||
? generation
|
||||
: 0UL;
|
||||
}
|
||||
|
||||
internal ulong CollisionWorldAuthority => _collisionWorldAuthority;
|
||||
|
||||
internal ulong ShadowWorldAuthority =>
|
||||
Engine.ShadowObjects.MutationRevision;
|
||||
|
||||
internal ClientObjectTable? ObjectTable => Engine.Objects;
|
||||
|
||||
internal ulong ObjectTableBindingAuthority =>
|
||||
Engine.ObjectsBindingRevision;
|
||||
|
||||
internal ulong ObjectTableAuthority =>
|
||||
ObjectTable?.MutationRevision ?? 0UL;
|
||||
|
||||
private void AdvanceCollisionWorldAuthority() =>
|
||||
_collisionWorldAuthority = checked(_collisionWorldAuthority + 1UL);
|
||||
|
||||
internal bool TrySealCollisionEvaluationAuthority(
|
||||
in PhysicsSetPositionResult result,
|
||||
ulong expectedCollisionWorldAuthority,
|
||||
ulong expectedShadowWorldAuthority,
|
||||
ClientObjectTable? expectedObjectTable,
|
||||
ulong expectedObjectTableBindingAuthority,
|
||||
ulong expectedObjectTableAuthority,
|
||||
out RuntimeCollisionEvaluationAuthority authority)
|
||||
{
|
||||
authority = default;
|
||||
if (_collisionWorldAuthority != expectedCollisionWorldAuthority
|
||||
|| ShadowWorldAuthority != expectedShadowWorldAuthority
|
||||
|| !ReferenceEquals(ObjectTable, expectedObjectTable)
|
||||
|| ObjectTableBindingAuthority
|
||||
!= expectedObjectTableBindingAuthority
|
||||
|| ObjectTableAuthority != expectedObjectTableAuthority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var prefixes = new HashSet<uint>();
|
||||
var authorities = ImmutableArray.CreateBuilder<
|
||||
RuntimeCollisionGenerationAuthority>();
|
||||
void Add(uint cellId)
|
||||
{
|
||||
uint landblock = CanonicalLandblock(cellId);
|
||||
if (landblock == 0u || !prefixes.Add(landblock))
|
||||
return;
|
||||
authorities.Add(new RuntimeCollisionGenerationAuthority(
|
||||
landblock,
|
||||
CollisionGenerationAuthority(landblock)));
|
||||
}
|
||||
|
||||
Add(result.CellId);
|
||||
if (!result.QueriedCellIds.IsDefaultOrEmpty)
|
||||
{
|
||||
foreach (uint cellId in result.QueriedCellIds)
|
||||
Add(cellId);
|
||||
}
|
||||
foreach (uint prefix in prefixes)
|
||||
{
|
||||
if (_collisionAdmissions.ContainsKey(prefix))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_collisionWorldAuthority != expectedCollisionWorldAuthority
|
||||
|| ShadowWorldAuthority != expectedShadowWorldAuthority
|
||||
|| !ReferenceEquals(ObjectTable, expectedObjectTable)
|
||||
|| ObjectTableBindingAuthority
|
||||
!= expectedObjectTableBindingAuthority
|
||||
|| ObjectTableAuthority != expectedObjectTableAuthority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
authority = new RuntimeCollisionEvaluationAuthority(
|
||||
expectedCollisionWorldAuthority,
|
||||
expectedShadowWorldAuthority,
|
||||
expectedObjectTable,
|
||||
expectedObjectTableBindingAuthority,
|
||||
expectedObjectTableAuthority,
|
||||
authorities.ToImmutable());
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool IsCollisionEvaluationAuthorityCurrent(
|
||||
in RuntimeCollisionEvaluationAuthority authority)
|
||||
{
|
||||
if (!authority.IsValid
|
||||
|| _collisionWorldAuthority != authority.CollisionWorldAuthority
|
||||
|| ShadowWorldAuthority != authority.ShadowWorldAuthority
|
||||
|| !ReferenceEquals(ObjectTable, authority.ObjectTable)
|
||||
|| ObjectTableBindingAuthority
|
||||
!= authority.ObjectTableBindingAuthority
|
||||
|| ObjectTableAuthority != authority.ObjectTableAuthority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach (RuntimeCollisionGenerationAuthority generation
|
||||
in authority.Generations)
|
||||
{
|
||||
if (generation.LandblockId == 0u
|
||||
|| _collisionAdmissions.ContainsKey(generation.LandblockId)
|
||||
|| CollisionGenerationAuthority(generation.LandblockId)
|
||||
!= generation.Generation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool HandleSetPositionCollisions(
|
||||
RuntimeEntityRecord record,
|
||||
ulong positionAuthorityVersion,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime.Entities;
|
||||
|
|
@ -82,6 +83,32 @@ internal readonly record struct RuntimeSetPositionCommand(
|
|||
float ShadowWorldOffsetY = 0f,
|
||||
RuntimePortalPlacementAuthority Portal = default);
|
||||
|
||||
internal readonly record struct RuntimeCollisionGenerationAuthority(
|
||||
uint LandblockId,
|
||||
ulong Generation);
|
||||
|
||||
internal readonly record struct RuntimeCollisionEvaluationAuthority(
|
||||
ulong CollisionWorldAuthority,
|
||||
ulong ShadowWorldAuthority,
|
||||
ClientObjectTable? ObjectTable,
|
||||
ulong ObjectTableBindingAuthority,
|
||||
ulong ObjectTableAuthority,
|
||||
ImmutableArray<RuntimeCollisionGenerationAuthority> Generations)
|
||||
{
|
||||
internal bool IsValid => CollisionWorldAuthority != 0UL
|
||||
&& !Generations.IsDefault;
|
||||
}
|
||||
|
||||
internal readonly record struct RuntimeDormantSetPositionEvaluation(
|
||||
RuntimeEntityPlacementToken Placement,
|
||||
RuntimeSetPositionCommand Command,
|
||||
PhysicsSetPositionResult Result,
|
||||
RuntimeCollisionEvaluationAuthority CollisionAuthority)
|
||||
{
|
||||
internal bool IsValid => Placement.IsValid
|
||||
&& CollisionAuthority.IsValid;
|
||||
}
|
||||
|
||||
public readonly record struct RuntimePlacementProjectionToken(
|
||||
ulong Sequence,
|
||||
ulong Revision,
|
||||
|
|
@ -597,6 +624,98 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
&& IsPreparationAuthorityCurrent(operation, authority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the exact authored local-player placement without mutating
|
||||
/// the canonical dormant body or any Runtime ownership index. Core's
|
||||
/// SetPosition transaction is pure; collision reporting is deliberately
|
||||
/// omitted until the later atomic activation commit.
|
||||
/// </summary>
|
||||
internal bool TryEvaluateDormantLocalActivation(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command,
|
||||
out RuntimeDormantSetPositionEvaluation evaluation)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
ArgumentNullException.ThrowIfNull(body);
|
||||
evaluation = default;
|
||||
if (!IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
token,
|
||||
command,
|
||||
out Operation? operation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PhysicsSetPositionRequest canonicalRequest = command.Physics with
|
||||
{
|
||||
MoverPhysicsState = record.FinalPhysicsState,
|
||||
MovingEntityId = token.Entity.LocalEntityId,
|
||||
CurrentCellId = null,
|
||||
};
|
||||
if (!IsStructurallyValid(canonicalRequest))
|
||||
return false;
|
||||
|
||||
var canonicalCommand = command with { Physics = canonicalRequest };
|
||||
ulong collisionWorldAuthority = _physics.CollisionWorldAuthority;
|
||||
ulong shadowWorldAuthority = _physics.ShadowWorldAuthority;
|
||||
ClientObjectTable? objectTable = _physics.ObjectTable;
|
||||
ulong objectTableBindingAuthority =
|
||||
_physics.ObjectTableBindingAuthority;
|
||||
ulong objectTableAuthority = objectTable?.MutationRevision ?? 0UL;
|
||||
PhysicsSetPositionResult result = _physics.Engine.SetPosition(
|
||||
canonicalRequest,
|
||||
handleCollisions: null);
|
||||
if (!IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
token,
|
||||
command,
|
||||
out Operation? current)
|
||||
|| !ReferenceEquals(current, operation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_physics.TrySealCollisionEvaluationAuthority(
|
||||
result,
|
||||
collisionWorldAuthority,
|
||||
shadowWorldAuthority,
|
||||
objectTable,
|
||||
objectTableBindingAuthority,
|
||||
objectTableAuthority,
|
||||
out RuntimeCollisionEvaluationAuthority collisionAuthority))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
evaluation = new RuntimeDormantSetPositionEvaluation(
|
||||
token,
|
||||
canonicalCommand,
|
||||
result,
|
||||
collisionAuthority);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool IsDormantLocalEvaluationCurrent(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeDormantSetPositionEvaluation evaluation) =>
|
||||
evaluation.IsValid
|
||||
&& IsExactDormantLocalActivationCurrent(
|
||||
record,
|
||||
body,
|
||||
evaluation.Placement,
|
||||
evaluation.Command,
|
||||
out _,
|
||||
allowCanonicalCommand: true)
|
||||
&& _physics.IsCollisionEvaluationAuthorityCurrent(
|
||||
evaluation.CollisionAuthority);
|
||||
|
||||
internal RuntimeSetPositionOutcome SubmitPreparedPlacement(
|
||||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command) =>
|
||||
|
|
@ -1881,6 +2000,69 @@ internal sealed class RuntimeSetPositionState : IDisposable
|
|||
&& operation.Record.PlacementCommitVersion
|
||||
== operation.PlacementCommitVersion;
|
||||
|
||||
private bool IsExactDormantLocalActivationCurrent(
|
||||
RuntimeEntityRecord record,
|
||||
PhysicsBody body,
|
||||
in RuntimeEntityPlacementToken token,
|
||||
in RuntimeSetPositionCommand command,
|
||||
out Operation? operation,
|
||||
bool allowCanonicalCommand = false)
|
||||
{
|
||||
operation = null;
|
||||
if (!token.IsValid
|
||||
|| token.Entity != record.Key
|
||||
|| token.PreparationKind
|
||||
is not RuntimeEntityPlacementPreparationKind.AuthoredMover
|
||||
|| command.Kind is not (RuntimeSetPositionOperationKind.InitialLogin
|
||||
or RuntimeSetPositionOperationKind.LocalAuthoritative)
|
||||
|| command.Portal != default
|
||||
&& !command.Portal.IsValid
|
||||
|| !_operations.TryGetValue(token.Entity, out operation)
|
||||
|| operation.Token != token
|
||||
|| operation.Stage
|
||||
is not RuntimeEntityPlacementStage.AwaitingPreparation
|
||||
|| !ReferenceEquals(operation.Record, record)
|
||||
|| !IsCurrent(operation)
|
||||
|| !ReferenceEquals(record.PhysicsBody, body)
|
||||
|| body.InWorld
|
||||
|| (body.TransientState & TransientStateFlags.Active) != 0
|
||||
|| record.PhysicsHost is not null
|
||||
|| record.RemoteMotion is not null
|
||||
|| record.Projectile is not null
|
||||
|| record.PhysicsBodyAcquisitionInProgress
|
||||
|| record.RemoteMotionBindingInProgress
|
||||
|| record.ProjectileBindingInProgress
|
||||
|| record.RequiresRemotePlacementRuntime
|
||||
|| record.DeleteAcceptedForTeardown
|
||||
|| _physics.IsSpatialRoot(record)
|
||||
|| !_moverPreparationAuthorities.TryGetValue(
|
||||
token.Entity,
|
||||
out MoverPreparationAuthority authority)
|
||||
|| authority.OperationId != token.OperationId
|
||||
|| !authority.Prepared
|
||||
|| !IsPreparationAuthorityCurrent(operation, authority))
|
||||
{
|
||||
operation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authority.PreparedCommand == command)
|
||||
return true;
|
||||
if (!allowCanonicalCommand)
|
||||
return false;
|
||||
|
||||
RuntimeSetPositionCommand authored = authority.PreparedCommand;
|
||||
return authored with
|
||||
{
|
||||
Physics = authored.Physics with
|
||||
{
|
||||
MoverPhysicsState = record.FinalPhysicsState,
|
||||
MovingEntityId = token.Entity.LocalEntityId,
|
||||
CurrentCellId = null,
|
||||
},
|
||||
} == command;
|
||||
}
|
||||
|
||||
private bool IsVelocityCurrent(Operation operation) =>
|
||||
operation.SourceVelocityAuthorityVersion == 0UL
|
||||
|| operation.Record.VelocityAuthorityVersion
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue