fix(runtime): preserve hidden setposition collision ownership
This commit is contained in:
parent
9b0f59bd1b
commit
0fbc7a1fb7
8 changed files with 343 additions and 43 deletions
|
|
@ -1883,7 +1883,7 @@ public sealed class RuntimeCollisionReportingStateTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void HitGroundHiddenTransitionCannotResumeCollisionTracking()
|
||||
public void HitGroundHiddenTransitionCommitsPlacementWithoutResumingCollisionTracking()
|
||||
{
|
||||
using var lifetime = Lifetime();
|
||||
RuntimeEntityRecord owner = Entity(
|
||||
|
|
@ -1937,11 +1937,20 @@ public sealed class RuntimeCollisionReportingStateTests
|
|||
PlacementCommand(owner, new Vector3(16f, 12f, 7f)));
|
||||
|
||||
Assert.True(hidden);
|
||||
Assert.Equal(RuntimeSetPositionStatus.Cancelled, outcome.Status);
|
||||
Assert.Equal(
|
||||
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
|
||||
outcome.Status);
|
||||
Assert.True(lifetime.Physics.SetPosition.TryPeekProjection(
|
||||
out RuntimePlacementProjectionSnapshot projection));
|
||||
Assert.True(owner.FinalPhysicsState.HasFlag(PhysicsStateFlags.Hidden));
|
||||
Assert.Equal(Cell, owner.FullCellId);
|
||||
Assert.Equal(projection.WorldPosition, owner.PhysicsBody!.Position);
|
||||
Assert.True(owner.PhysicsBody.InWorld);
|
||||
Assert.Empty(observer.Reports);
|
||||
Assert.Equal(0, lifetime.Physics.CollisionReports.CaptureOwnership()
|
||||
.TrackedObjectCount);
|
||||
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
||||
outcome.Projection));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -2010,7 +2019,7 @@ public sealed class RuntimeCollisionReportingStateTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void CollisionReportHiddenTransitionStopsResponseAndShadowReflood()
|
||||
public void CollisionReportHiddenTransitionPreservesCommittedPlacement()
|
||||
{
|
||||
using var lifetime = Lifetime();
|
||||
RuntimeEntityRecord owner = Entity(
|
||||
|
|
@ -2023,7 +2032,6 @@ public sealed class RuntimeCollisionReportingStateTests
|
|||
RegisterDynamicShadow(lifetime, owner);
|
||||
RegisterDynamicShadow(lifetime, target);
|
||||
uint targetId = target.Key!.Value.LocalEntityId;
|
||||
Vector3 initial = owner.PhysicsBody!.Position;
|
||||
lifetime.Physics.Engine.TransitionCellCollisionTestHook =
|
||||
(transition, phase, _, _) =>
|
||||
{
|
||||
|
|
@ -2062,14 +2070,217 @@ public sealed class RuntimeCollisionReportingStateTests
|
|||
PlacementCommand(owner, new Vector3(18f, 12f, 7f)));
|
||||
|
||||
Assert.True(hidden);
|
||||
Assert.Equal(RuntimeSetPositionStatus.Cancelled, outcome.Status);
|
||||
Assert.Equal(
|
||||
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
|
||||
outcome.Status);
|
||||
Assert.True(owner.FinalPhysicsState.HasFlag(PhysicsStateFlags.Hidden));
|
||||
Assert.Equal(0, lifetime.Physics.CollisionReports.CaptureOwnership()
|
||||
.TrackedObjectCount);
|
||||
ShadowEntry shadow = Assert.Single(
|
||||
lifetime.Physics.Engine.ShadowObjects.AllEntriesForDebug(),
|
||||
Assert.Equal(Cell, owner.FullCellId);
|
||||
Assert.Equal(new Vector3(18f, 12f, 7f), owner.PhysicsBody!.Position);
|
||||
ShadowObjectRegistry shadows = lifetime.Physics.Engine.ShadowObjects;
|
||||
uint ownerId = owner.Key!.Value.LocalEntityId;
|
||||
ShadowEntry hiddenShadow = Assert.Single(
|
||||
shadows.AllEntriesForDebug(),
|
||||
entry => entry.EntityId == ownerId);
|
||||
Assert.Equal(owner.PhysicsBody.Position, hiddenShadow.Position);
|
||||
Assert.Equal(0, shadows.SuspendedRegistrationCount);
|
||||
Assert.True(shadows.TryGetCollisionOwner(
|
||||
ownerId,
|
||||
out uint hiddenShadowState,
|
||||
out _));
|
||||
Assert.Equal((uint)owner.FinalPhysicsState, hiddenShadowState);
|
||||
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
||||
outcome.Projection));
|
||||
|
||||
Assert.True(lifetime.TryApplyState(
|
||||
new SetState.Parsed(
|
||||
owner.ServerGuid,
|
||||
(uint)PhysicsStateFlags.ReportCollisions,
|
||||
owner.Incarnation,
|
||||
StateSequence: 3),
|
||||
acknowledgeProjection: null,
|
||||
out _,
|
||||
out _));
|
||||
Assert.Equal(0, shadows.SuspendedRegistrationCount);
|
||||
ShadowEntry restored = Assert.Single(
|
||||
shadows.AllEntriesForDebug(),
|
||||
entry => entry.EntityId == ownerId);
|
||||
Assert.Equal(owner.PhysicsBody.Position, restored.Position);
|
||||
Assert.Equal((uint)owner.FinalPhysicsState, restored.State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PreparedCollisionBatchStopsAfterCallbackHidesOwner()
|
||||
{
|
||||
using var lifetime = Lifetime();
|
||||
RuntimeEntityRecord owner = Entity(
|
||||
lifetime,
|
||||
0x7000207Eu,
|
||||
1,
|
||||
PhysicsStateFlags.ReportCollisions);
|
||||
RuntimeEntityRecord first = Entity(
|
||||
lifetime, 0x7000207Fu, 1, PhysicsStateFlags.None);
|
||||
RuntimeEntityRecord second = Entity(
|
||||
lifetime, 0x70002080u, 1, PhysicsStateFlags.None);
|
||||
RegisterDynamicShadow(lifetime, owner);
|
||||
RegisterDynamicShadow(lifetime, first);
|
||||
RegisterDynamicShadow(lifetime, second);
|
||||
bool hidden = false;
|
||||
var observer = new CollisionObserver(report =>
|
||||
{
|
||||
if (hidden
|
||||
|| report.Kind is not RuntimeCollisionReportKind.ObjectCollision
|
||||
|| report.Recipient != owner.Key)
|
||||
{
|
||||
return;
|
||||
}
|
||||
hidden = lifetime.TryApplyState(
|
||||
new SetState.Parsed(
|
||||
owner.ServerGuid,
|
||||
(uint)(PhysicsStateFlags.ReportCollisions
|
||||
| PhysicsStateFlags.Hidden),
|
||||
owner.Incarnation,
|
||||
StateSequence: 2),
|
||||
acknowledgeProjection: null,
|
||||
out _,
|
||||
out _);
|
||||
});
|
||||
using IDisposable subscription = lifetime.Physics.CollisionReports
|
||||
.Subscribe(observer);
|
||||
Assert.True(lifetime.Physics.CollisionReports.TryPrepareSetPositionBatch(
|
||||
owner,
|
||||
owner.PhysicsBody!,
|
||||
physicsTime: 12d,
|
||||
previousContact: false,
|
||||
previousOnWalkable: false,
|
||||
finalOnWalkable: false,
|
||||
collidedWithEnvironment: false,
|
||||
[
|
||||
first.Key!.Value.LocalEntityId,
|
||||
second.Key!.Value.LocalEntityId,
|
||||
],
|
||||
out var prepared));
|
||||
Assert.True(lifetime.Physics.CollisionReports.TryInstallSetPositionBatch(
|
||||
prepared!, out var receipt));
|
||||
|
||||
SetPositionCollisionBatchDispatchResult result = lifetime.Physics
|
||||
.CollisionReports.DispatchSetPositionBatchResult(receipt);
|
||||
|
||||
Assert.True(hidden);
|
||||
Assert.Equal(SetPositionCollisionBatchDispatchStatus.Completed,
|
||||
result.Status);
|
||||
RuntimeCollisionReport report = Assert.Single(
|
||||
observer.Reports,
|
||||
candidate => candidate.Kind
|
||||
is RuntimeCollisionReportKind.ObjectCollision);
|
||||
Assert.Equal(first.Key, report.Other);
|
||||
RuntimeCollisionReportingOwnershipSnapshot ownership = lifetime.Physics
|
||||
.CollisionReports.CaptureOwnership();
|
||||
Assert.Equal(0, ownership.OwnerCount);
|
||||
Assert.Equal(0, ownership.TrackedObjectCount);
|
||||
Assert.Equal(0, ownership.ReversePeerCount);
|
||||
Assert.Equal(0, ownership.PendingSetPositionDispatchCount);
|
||||
ShadowObjectRegistry shadows = lifetime.Physics.Engine.ShadowObjects;
|
||||
Assert.Equal(0, shadows.SuspendedRegistrationCount);
|
||||
Assert.Contains(
|
||||
shadows.AllEntriesForDebug(),
|
||||
entry => entry.EntityId == owner.Key!.Value.LocalEntityId);
|
||||
Assert.Equal(initial, shadow.Position);
|
||||
Assert.True(shadows.TryGetCollisionOwner(
|
||||
owner.Key!.Value.LocalEntityId,
|
||||
out uint shadowState,
|
||||
out _));
|
||||
Assert.Equal((uint)owner.FinalPhysicsState, shadowState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoDrawOwnerStillPublishesItsCollisionReport()
|
||||
{
|
||||
using var lifetime = Lifetime();
|
||||
RuntimeEntityRecord owner = Entity(
|
||||
lifetime,
|
||||
0x7000207Au,
|
||||
1,
|
||||
PhysicsStateFlags.ReportCollisions | PhysicsStateFlags.NoDraw);
|
||||
RuntimeEntityRecord target = Entity(
|
||||
lifetime, 0x7000207Bu, 1, PhysicsStateFlags.None);
|
||||
RegisterDynamicShadow(lifetime, owner);
|
||||
RegisterDynamicShadow(lifetime, target);
|
||||
uint targetId = target.Key!.Value.LocalEntityId;
|
||||
lifetime.Physics.Engine.TransitionCellCollisionTestHook =
|
||||
(transition, phase, _, _) =>
|
||||
{
|
||||
if (phase is TransitionCellCollisionPhase.Environment)
|
||||
{
|
||||
transition.CollisionInfo.CollideObjectGuids.Add(targetId);
|
||||
transition.CollisionInfo.SetCollisionNormal(Vector3.UnitX);
|
||||
}
|
||||
return TransitionState.OK;
|
||||
};
|
||||
var observer = new CollisionObserver();
|
||||
using IDisposable subscription = lifetime.Physics.CollisionReports
|
||||
.Subscribe(observer);
|
||||
|
||||
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
|
||||
owner,
|
||||
owner.PositionAuthorityVersion,
|
||||
PlacementCommand(owner, new Vector3(19f, 12f, 7f)));
|
||||
|
||||
Assert.Equal(
|
||||
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
|
||||
outcome.Status);
|
||||
RuntimeCollisionReport report = Assert.Single(
|
||||
observer.Reports,
|
||||
report => report.Kind is RuntimeCollisionReportKind.ObjectCollision);
|
||||
Assert.Equal(owner.Key, report.Recipient);
|
||||
Assert.Equal(target.Key, report.Other);
|
||||
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
||||
outcome.Projection));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoDrawOwnerStillAllowsReciprocalPeerCollisionReport()
|
||||
{
|
||||
using var lifetime = Lifetime();
|
||||
RuntimeEntityRecord owner = Entity(
|
||||
lifetime, 0x7000207Cu, 1, PhysicsStateFlags.NoDraw);
|
||||
RuntimeEntityRecord target = Entity(
|
||||
lifetime,
|
||||
0x7000207Du,
|
||||
1,
|
||||
PhysicsStateFlags.ReportCollisions);
|
||||
RegisterDynamicShadow(lifetime, owner);
|
||||
RegisterDynamicShadow(lifetime, target);
|
||||
uint targetId = target.Key!.Value.LocalEntityId;
|
||||
lifetime.Physics.Engine.TransitionCellCollisionTestHook =
|
||||
(transition, phase, _, _) =>
|
||||
{
|
||||
if (phase is TransitionCellCollisionPhase.Environment)
|
||||
{
|
||||
transition.CollisionInfo.CollideObjectGuids.Add(targetId);
|
||||
transition.CollisionInfo.SetCollisionNormal(Vector3.UnitX);
|
||||
}
|
||||
return TransitionState.OK;
|
||||
};
|
||||
var observer = new CollisionObserver();
|
||||
using IDisposable subscription = lifetime.Physics.CollisionReports
|
||||
.Subscribe(observer);
|
||||
|
||||
RuntimeSetPositionOutcome outcome = lifetime.Physics.SetPosition.Apply(
|
||||
owner,
|
||||
owner.PositionAuthorityVersion,
|
||||
PlacementCommand(owner, new Vector3(20f, 12f, 7f)));
|
||||
|
||||
Assert.Equal(
|
||||
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
|
||||
outcome.Status);
|
||||
RuntimeCollisionReport report = Assert.Single(
|
||||
observer.Reports,
|
||||
report => report.Kind is RuntimeCollisionReportKind.ObjectCollision);
|
||||
Assert.Equal(target.Key, report.Recipient);
|
||||
Assert.Equal(owner.Key, report.Other);
|
||||
Assert.True(lifetime.Physics.SetPosition.AcknowledgeProjection(
|
||||
outcome.Projection));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue