refactor(physics): delete legacy PhysicsEngine.Resolve/ResolvePlacement/HasCellSurface (C5a, AP-1/AD-1)
Member-wise deletion of the three legacy resolver members named in docs/research/2026-08-05-c5a-contract.md: PhysicsEngine.Resolve, PhysicsEngine.HasCellSurface, and PhysicsEngine.ResolvePlacement. An exhaustive receiver census over src/ found zero production callers of any of the three — every production placement writer already reaches the canonical PhysicsEngine.SetPosition transaction exclusively through RuntimeSetPositionState (three call sites total). The deletion is purely member-wise: IsSpawnCellReady and AdjustPosition, which shared the same source region as the deleted members, are preserved byte-identical — every remaining production caller of either (including PhysicsCameraCollisionProbe, AdjustPosition's sole surviving production caller) is unaffected. Companion changes: - PlayerMovementController's 3-argument SetPosition test overload is renamed to SeedPlacementForTest (internal) and CommitPreparedPosition is deleted; 83 call sites across 19 test files were mechanically renamed to match. - Seven pinned test dispositions from the contract are executed: 3.1 (PhysicsEngineTests.cs: 11 legacy-resolver tests deleted, 6 ResolveWithTransition tests kept), 3.2/3.3/3.4 (re-point to canonical SetPosition, with TransitionScratchDifferentialTests.cs additionally gaining positive IsCommitted assertions after each bitwise comparison so the differential proves a placement actually committed, not just that two possibly-uncommitted results match), 3.5 (Runtime rename), and 3.6 (PlayerMovementPlacementTransactionTests.cs rewritten — its xmldoc now states plainly that the render-root publish moved to RuntimeSetPositionState.cs, but the sticky-release relocation claim was false and is retracted; this disposition's coverage loss is the sticky release path, not silently absorbed elsewhere). - Stale `PhysicsEngine.Resolve`/`Resolve` doc citations in CellTransit.cs, PlayerMovementController.cs, and HeadlessSessionWorldProjection.cs are corrected to name the surviving canonical entry points by symbol (SetPosition, AdjustSetPosition/AdjustPosition, ResolveWithTransition) rather than fragile line numbers. Retires AP-1 and AD-1 in docs/architecture/retail-divergence-register.md: both rows described production zero-delta placement routing remaining on the legacy resolver pending the Slice 4B2/4B route cutover; that resolver no longer exists, so the condition each row tracked is now structurally false rather than merely narrowed. AP-145 (routed through the prior commit) and this commit's AP-1/AD-1 together bring the section counts to 101 AP / 47 AD active rows. Builds on the AP-145 fix (previous commit) — this commit's staged tree was independently rebuilt and its four suites independently rerun on top of that commit before this commit was created, in addition to the combined rebuild/rerun below. Full-solution build: 0 errors (21 pre-existing warnings, all unrelated). Suite results (combined tree): Core 4270/4271 passed (1 skip; the single DatSoundCacheTests concurrent-decode-dedup failure is a known load-sensitive race, confirmed passing standalone and unrelated to this change), Runtime 1176/1176, Headless 86/86, App 4132/4135 (3 skips). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f8e55ba5e4
commit
6921a02744
34 changed files with 508 additions and 946 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using AcDream.Core.Physics;
|
||||
|
|
@ -176,57 +177,95 @@ public sealed class TransitionScratchDifferentialTests
|
|||
Coverage: CoverageKind.Failure));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C5a (2026-08-05) re-point: the legacy <c>PhysicsEngine.ResolvePlacement</c>
|
||||
/// this arm originally drove is deleted (zero production callers).
|
||||
/// Canonical <c>PhysicsEngine.SetPosition</c> reaches the exact same
|
||||
/// <c>FindPlacementPos</c> ring search — <c>SetPositionInternal</c> sets
|
||||
/// <c>InsertType.Placement</c> before <c>Transition.FindValidPosition</c>,
|
||||
/// which dispatches to <c>FindPlacementPosition</c>, which calls
|
||||
/// <c>FindPlacementPos</c> — so this is the same scratch-reuse surface
|
||||
/// under a new entry point, not a new code path. The bitwise
|
||||
/// fresh-vs-reused comparison and the second-identity leak check are both
|
||||
/// preserved verbatim, now via <see cref="AssertSetPositionBitwise"/>
|
||||
/// over <see cref="PhysicsSetPositionResult"/>.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ReusedScratch_MatchesFreshPlacementSearch()
|
||||
{
|
||||
PhysicsEngine fresh = BuildPlacementEngine(reuse: false);
|
||||
PhysicsEngine reused = BuildPlacementEngine(reuse: true);
|
||||
|
||||
ResolveResult expected = fresh.ResolvePlacement(
|
||||
new Vector3(10f, 10f, 0f),
|
||||
Cell,
|
||||
0.48f,
|
||||
1.835f,
|
||||
0.40f,
|
||||
0.40f,
|
||||
ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
|
||||
0x50000101u);
|
||||
ResolveResult actual = reused.ResolvePlacement(
|
||||
new Vector3(10f, 10f, 0f),
|
||||
Cell,
|
||||
0.48f,
|
||||
1.835f,
|
||||
0.40f,
|
||||
0.40f,
|
||||
ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
|
||||
0x50000101u);
|
||||
PhysicsSetPositionResult expected = fresh.SetPosition(
|
||||
PlacementRequest(
|
||||
new Vector3(10f, 10f, 0f),
|
||||
ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
|
||||
0x50000101u));
|
||||
PhysicsSetPositionResult actual = reused.SetPosition(
|
||||
PlacementRequest(
|
||||
new Vector3(10f, 10f, 0f),
|
||||
ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
|
||||
0x50000101u));
|
||||
|
||||
AssertResolveBitwise(expected, actual, "placement");
|
||||
AssertSetPositionBitwise(expected, actual, "placement");
|
||||
// A6 (architecture review, 2026-08-05): the bitwise comparison alone
|
||||
// proves fresh and reused AGREE, not that either actually placed
|
||||
// anything — a future regression that makes SetPosition fail
|
||||
// IDENTICALLY on both engines would leave this differential green
|
||||
// while the scratch-reuse surface it exists to guard goes
|
||||
// unexercised. Assert the positive fact the comparison itself can't:
|
||||
// this placement committed.
|
||||
Assert.True(expected.IsCommitted, "fresh engine did not commit the placement");
|
||||
Assert.True(actual.IsCommitted, "reused engine did not commit the placement");
|
||||
|
||||
// A second placement with a different self identity proves that the
|
||||
// previous mover and collision-GUID list cannot leak through the lease.
|
||||
expected = fresh.ResolvePlacement(
|
||||
new Vector3(11f, 10f, 0f),
|
||||
Cell,
|
||||
0.48f,
|
||||
1.835f,
|
||||
0.40f,
|
||||
0.40f,
|
||||
ObjectInfoState.EdgeSlide,
|
||||
0x80000102u);
|
||||
actual = reused.ResolvePlacement(
|
||||
new Vector3(11f, 10f, 0f),
|
||||
Cell,
|
||||
0.48f,
|
||||
1.835f,
|
||||
0.40f,
|
||||
0.40f,
|
||||
ObjectInfoState.EdgeSlide,
|
||||
0x80000102u);
|
||||
expected = fresh.SetPosition(
|
||||
PlacementRequest(
|
||||
new Vector3(11f, 10f, 0f),
|
||||
ObjectInfoState.EdgeSlide,
|
||||
0x80000102u));
|
||||
actual = reused.SetPosition(
|
||||
PlacementRequest(
|
||||
new Vector3(11f, 10f, 0f),
|
||||
ObjectInfoState.EdgeSlide,
|
||||
0x80000102u));
|
||||
|
||||
AssertResolveBitwise(expected, actual, "placement after hostile identity");
|
||||
AssertSetPositionBitwise(expected, actual, "placement after hostile identity");
|
||||
Assert.True(
|
||||
expected.IsCommitted,
|
||||
"fresh engine did not commit the second placement");
|
||||
Assert.True(
|
||||
actual.IsCommitted,
|
||||
"reused engine did not commit the second placement");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the two-sphere capsule request the legacy scalar
|
||||
/// <c>InitPath(sphereRadius: 0.48, sphereHeight: 1.835)</c> reconstructed
|
||||
/// internally (origin (0,0,radius) + (0,0,height-radius), both
|
||||
/// radius-sized), so canonical <c>SetPosition</c> sees the identical
|
||||
/// mover shape the legacy <c>ResolvePlacement</c> call did.
|
||||
/// </summary>
|
||||
private static PhysicsSetPositionRequest PlacementRequest(
|
||||
Vector3 position,
|
||||
ObjectInfoState moverFlags,
|
||||
uint movingEntityId) => new(
|
||||
Position: position,
|
||||
Orientation: Quaternion.Identity,
|
||||
CellId: Cell,
|
||||
CellLocalPosition: position,
|
||||
Spheres: ImmutableArray.Create(
|
||||
new FlatCollisionSphere(new Vector3(0f, 0f, 0.48f), 0.48f),
|
||||
new FlatCollisionSphere(new Vector3(0f, 0f, 1.835f - 0.48f), 0.48f)),
|
||||
Scale: 1f,
|
||||
StepUpHeight: 0.40f,
|
||||
StepDownHeight: 0.40f,
|
||||
MoverFlags: moverFlags,
|
||||
MovingEntityId: movingEntityId,
|
||||
Flags: PhysicsSetPositionFlags.Placement
|
||||
| PhysicsSetPositionFlags.Slide);
|
||||
|
||||
private static void RunSequence(
|
||||
Func<bool, PhysicsEngine> buildEngine,
|
||||
params ResolveSpec[] specs)
|
||||
|
|
@ -441,6 +480,66 @@ public sealed class TransitionScratchDifferentialTests
|
|||
Assert.Equal(expected.ContactPlaneIsWater, actual.ContactPlaneIsWater);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise sibling of <see cref="AssertResolveBitwise"/> for
|
||||
/// <see cref="PhysicsSetPositionResult"/>, used by the re-pointed
|
||||
/// <see cref="ReusedScratch_MatchesFreshPlacementSearch"/> (C5a). Every
|
||||
/// field is compared — float-bearing members bitwise (a stale scratch
|
||||
/// leaking a different -0.0/+0.0 sign bit would otherwise pass a
|
||||
/// value-equality check), enum/bool/id members by value, and the
|
||||
/// immutable id arrays by ordered sequence.
|
||||
/// </summary>
|
||||
private static void AssertSetPositionBitwise(
|
||||
PhysicsSetPositionResult expected,
|
||||
PhysicsSetPositionResult actual,
|
||||
string context)
|
||||
{
|
||||
Assert.Equal(expected.Error, actual.Error);
|
||||
Assert.Equal(expected.Residence, actual.Residence);
|
||||
AssertVectorBitwise(expected.Position, actual.Position, context);
|
||||
AssertQuaternionBitwise(expected.Orientation, actual.Orientation, context);
|
||||
Assert.Equal(expected.CellId, actual.CellId);
|
||||
AssertVectorBitwise(
|
||||
expected.CellLocalPosition,
|
||||
actual.CellLocalPosition,
|
||||
$"{context}.CellLocalPosition");
|
||||
Assert.Equal(expected.InContact, actual.InContact);
|
||||
Assert.Equal(expected.OnWalkable, actual.OnWalkable);
|
||||
AssertPlaneBitwise(expected.ContactPlane, actual.ContactPlane, context);
|
||||
Assert.Equal(expected.ContactPlaneCellId, actual.ContactPlaneCellId);
|
||||
Assert.Equal(expected.ContactPlaneIsWater, actual.ContactPlaneIsWater);
|
||||
Assert.Equal(expected.SlidingNormalValid, actual.SlidingNormalValid);
|
||||
AssertVectorBitwise(
|
||||
expected.SlidingNormal,
|
||||
actual.SlidingNormal,
|
||||
$"{context}.SlidingNormal");
|
||||
Assert.Equal(expected.CollisionNormalValid, actual.CollisionNormalValid);
|
||||
AssertVectorBitwise(
|
||||
expected.CollisionNormal,
|
||||
actual.CollisionNormal,
|
||||
$"{context}.CollisionNormal");
|
||||
Assert.Equal(expected.FramesStationaryFall, actual.FramesStationaryFall);
|
||||
Assert.Equal(expected.CollidedWithEnvironment, actual.CollidedWithEnvironment);
|
||||
Assert.Equal(expected.CollisionHandlerResult, actual.CollisionHandlerResult);
|
||||
Assert.Equal(expected.CellChanged, actual.CellChanged);
|
||||
Assert.Equal(expected.ShadowAction, actual.ShadowAction);
|
||||
// ImmutableArray<T>.Equals(ImmutableArray<T>) compares the BACKING
|
||||
// ARRAY REFERENCE, not the elements — the fresh and reused engines
|
||||
// never share a backing array even when the contents match, so
|
||||
// Assert.Equal on the bare ImmutableArray would false-fail. Compare
|
||||
// as plain arrays (regular array Equals/sequence comparison) instead.
|
||||
Assert.Equal(ToArrayOrEmpty(expected.CrossCellIds), ToArrayOrEmpty(actual.CrossCellIds));
|
||||
Assert.Equal(
|
||||
ToArrayOrEmpty(expected.CollidedObjectIds),
|
||||
ToArrayOrEmpty(actual.CollidedObjectIds));
|
||||
Assert.Equal(
|
||||
ToArrayOrEmpty(expected.QueriedCellIds),
|
||||
ToArrayOrEmpty(actual.QueriedCellIds));
|
||||
}
|
||||
|
||||
private static uint[] ToArrayOrEmpty(ImmutableArray<uint> array) =>
|
||||
array.IsDefault ? Array.Empty<uint>() : array.ToArray();
|
||||
|
||||
private static void AssertCoverage(ResolveSpec spec, ResolveResult result)
|
||||
{
|
||||
switch (spec.Coverage)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue