feat(runtime): own SetPosition collision reports

This commit is contained in:
Erik 2026-08-01 00:15:11 +02:00
parent ec627c13a2
commit 237d1184d2
19 changed files with 3744 additions and 103 deletions

View file

@ -23,6 +23,14 @@ public readonly record struct RuntimePhysicsOwnershipSnapshot(
int UnboundDeferredSetPositionCellCount,
int UnboundDeferredSetPositionCellOrderCount,
int PreparedSetPositionMoverCount,
int CollisionReportOwnerCount,
int TrackedCollisionObjectCount,
int CollisionReportReversePeerCount,
int CollisionReportObserverCount,
int PendingCollisionReportCount,
int LeavingCollisionReportOwnerCount,
int CollisionReportAdmissionBlockedOwnerCount,
bool IsCollisionReportDispatching,
int CollisionAdmissionCount,
int CollisionGenerationCount,
bool OwnsProductionDataCache,
@ -49,6 +57,14 @@ public readonly record struct RuntimePhysicsOwnershipSnapshot(
&& UnboundDeferredSetPositionCellCount == 0
&& UnboundDeferredSetPositionCellOrderCount == 0
&& PreparedSetPositionMoverCount == 0
&& CollisionReportOwnerCount == 0
&& TrackedCollisionObjectCount == 0
&& CollisionReportReversePeerCount == 0
&& CollisionReportObserverCount == 0
&& PendingCollisionReportCount == 0
&& LeavingCollisionReportOwnerCount == 0
&& CollisionReportAdmissionBlockedOwnerCount == 0
&& !IsCollisionReportDispatching
&& CollisionAdmissionCount == 0
&& CollisionGenerationCount == 0
&& OwnsProductionDataCache;
@ -1063,6 +1079,9 @@ public sealed class RuntimePhysicsState : IDisposable
Engine.ShadowObjects.OwnerMutated += OnCollisionOwnerMutated;
Engine.ShadowObjects.OwnerPrefixMembershipChanged +=
OnCollisionOwnerPrefixMembershipChanged;
CollisionReports = new RuntimeCollisionReportingState(
Entities,
Engine.ShadowObjects);
SetPosition = new RuntimeSetPositionState(this, Entities);
}
@ -1082,12 +1101,16 @@ public sealed class RuntimePhysicsState : IDisposable
Engine.ShadowObjects.OwnerMutated += OnCollisionOwnerMutated;
Engine.ShadowObjects.OwnerPrefixMembershipChanged +=
OnCollisionOwnerPrefixMembershipChanged;
CollisionReports = new RuntimeCollisionReportingState(
Entities,
Engine.ShadowObjects);
SetPosition = new RuntimeSetPositionState(this, Entities);
}
internal RuntimeEntityDirectory Entities { get; }
public PhysicsEngine Engine { get; }
public PhysicsDataCache DataCache { get; }
internal RuntimeCollisionReportingState CollisionReports { get; }
internal RuntimeSetPositionState SetPosition { get; }
public int SpatialRootCount => _spatialRoots.Count;
public int SpatialRemoteCount => _spatialRemotes.Count;
@ -1107,6 +1130,8 @@ public sealed class RuntimePhysicsState : IDisposable
{
RuntimeSetPositionOwnershipSnapshot setPosition =
SetPosition.CaptureOwnership();
RuntimeCollisionReportingOwnershipSnapshot collisionReports =
CollisionReports.CaptureOwnership();
return new(
Engine.LandblockCount,
Engine.ShadowObjects.RetainedRegistrationCount,
@ -1127,6 +1152,14 @@ public sealed class RuntimePhysicsState : IDisposable
setPosition.UnboundDeferredCellCount,
setPosition.UnboundDeferredCellOrderCount,
setPosition.PreparedMoverCount,
collisionReports.OwnerCount,
collisionReports.TrackedObjectCount,
collisionReports.ReversePeerCount,
collisionReports.ObserverCount,
collisionReports.PendingReportCount,
collisionReports.LeavingOwnerCount,
collisionReports.AdmissionBlockedOwnerCount,
collisionReports.IsDispatching,
_collisionAdmissions.Count,
_collisionGenerations.Count,
ReferenceEquals(Engine.DataCache, DataCache),
@ -2303,6 +2336,7 @@ public sealed class RuntimePhysicsState : IDisposable
}
_preparedCollisionGenerations.Clear();
SetPosition.Dispose();
CollisionReports.Dispose();
_collisionOwnerJournal.Clear();
_collisionOwnerSubscribers.Clear();
Engine.Clear();
@ -2511,6 +2545,7 @@ public sealed class RuntimePhysicsState : IDisposable
ulong positionAuthorityVersion,
ulong spatialAuthorityVersion,
ulong velocityAuthorityVersion,
double physicsTime,
bool previousContact,
bool previousOnWalkable,
in PhysicsSetPositionCollisionReport report)
@ -2518,22 +2553,39 @@ public sealed class RuntimePhysicsState : IDisposable
if (!Entities.IsCurrent(record)
|| record.PositionAuthorityVersion != positionAuthorityVersion
|| record.SpatialAuthorityVersion != spatialAuthorityVersion
|| (velocityAuthorityVersion != 0UL
&& record.VelocityAuthorityVersion
!= velocityAuthorityVersion)
|| record.PhysicsBody is not { } body)
{
return false;
}
_ = previousContact;
_ = previousOnWalkable;
bool current = HandleSetPositionCollisionReports(
record,
positionAuthorityVersion,
spatialAuthorityVersion,
physicsTime,
previousContact: false,
previousOnWalkable: false,
collidedWithEnvironment: report.CollidedWithEnvironment,
collidedObjectIds: report.CollidedObjectIds,
collisionHandlerResult: out bool collisionHandlerResult);
if (!current)
return collisionHandlerResult;
if (velocityAuthorityVersion != 0UL
&& record.VelocityAuthorityVersion != velocityAuthorityVersion)
{
return collisionHandlerResult;
}
body.FramesStationaryFall = report.FramesStationaryFall;
PhysicsObjUpdate.HandleAllCollisions(
body,
report.CollisionNormalValid,
report.CollisionNormal,
previousContact,
previousOnWalkable,
body.OnWalkable);
prevContact: false,
prevOnWalkable: false,
nowOnWalkable: body.OnWalkable);
body.TransientState &= ~(TransientStateFlags.StationaryFall
| TransientStateFlags.StationaryStop
| TransientStateFlags.StationaryStuck);
@ -2544,12 +2596,49 @@ public sealed class RuntimePhysicsState : IDisposable
3 => TransientStateFlags.StationaryStuck,
_ => TransientStateFlags.None,
};
// Retail returns the result of collision reporting, not a collision-
// presence guess. Runtime does not yet own the per-object report/
// tracking table required to reproduce that return value, so fail
// closed. This preserves ordinary placement rejection and leaves the
// already-registered reporting seam explicit for the 4B2 cutover.
return false;
return collisionHandlerResult;
}
/// <summary>
/// Runs only retail's report/tracking half of handle_all_collisions. A
/// successful SetPosition invokes this after contact/ground callbacks and
/// before physical response and shadow reflood; invalid placement invokes
/// it before its one response pass.
/// </summary>
internal bool HandleSetPositionCollisionReports(
RuntimeEntityRecord record,
ulong positionAuthorityVersion,
ulong spatialAuthorityVersion,
double physicsTime,
bool previousContact,
bool previousOnWalkable,
bool collidedWithEnvironment,
System.Collections.Immutable.ImmutableArray<uint> collidedObjectIds,
out bool collisionHandlerResult)
{
collisionHandlerResult = false;
if (!Entities.IsCurrent(record)
|| record.PositionAuthorityVersion != positionAuthorityVersion
|| record.SpatialAuthorityVersion != spatialAuthorityVersion
|| record.PhysicsBody is not { } body)
{
return false;
}
collisionHandlerResult = CollisionReports.HandleReports(
record,
body,
physicsTime,
previousContact,
previousOnWalkable,
collidedWithEnvironment,
collidedObjectIds);
return Entities.IsCurrent(record)
&& record.PositionAuthorityVersion == positionAuthorityVersion
&& record.SpatialAuthorityVersion == spatialAuthorityVersion
&& ReferenceEquals(record.PhysicsBody, body)
&& body.InWorld
&& (body.State & PhysicsStateFlags.Hidden) == 0;
}
private void OnCollisionOwnerMutated(uint ownerId, ulong version)