fix(physics): #299 — port retail's mover-side IsImpenetrable exemption branch
CollisionExemption checked only the TARGET's IsImpenetrable, and the class doc
asserted "retail's pseudo-C only checks the target's IsImpenetrable(); acdream
follows retail" while blaming ACE for checking both. That was backwards: ACE was
retail-faithful and acdream was missing half the check.
Retail short-circuits on EITHER the mover's own state & IS_IMPENETRABLE (0x80)
OR the target's IsImpenetrable(); either alone exempts. Verified at the byte
level rather than from the decompiler's rendering — Binary Ninja shows the mover
test as `int16_t state_1 ... if (state_1 < 0)`, which reads like a 0x8000 test,
but decoding the PDB-paired binary at the mapped offset gives:
8b 43 04 mov eax,[ebx+4] ; mover object_info.state
f6 c4 01 test ah,1 ; 0x100 IsPlayer
84 c0 test al,al ; sign bit of AL = state & 0x80
78 3d js ... ; -> collide
`test al, al; js` is a byte-level sign test on AL, i.e. 0x80, not 0x8000.
Corroborated downstream in the same block (`test ah,8` = 0x800 IsPK,
`test ah,0x10` = 0x1000 IsPKLite) and by OBJECTINFO::init @0x0050cf30 setting
state |= 0x80 from the object's own IsImpenetrable().
Also corrected: ACCWeenieObject::IsImpenetrable @0x0058c8c0 returns
(_bitfield >> 0x15) & 1 — retail genuinely conflates BF_FREE_PKSTATUS with
"impenetrable", so acdream's FromPwdBitfield decode was already right.
Both retail arms set collide, so ordering between them is semantically free and
a misreading here could only ever produce spurious collisions, never a
walk-through.
Found while investigating #297; not symptom-causing on its own. No divergence
row: this retires a missing port rather than introducing a deviation, and
nothing in the register or the collision digest's DO-NOT-RETRY tables covers it.
Gates: complete Release solution 10,887 passed / 4 skipped / 0 failed
(baseline 10,867/4/0). Adversarial + retail-conformance review PASS on this
change specifically. Both new tests discrimination-verified by reverting the
branch and confirming failure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
40f5721354
commit
88348f6791
2 changed files with 54 additions and 7 deletions
|
|
@ -32,10 +32,21 @@ namespace AcDream.Core.Physics;
|
|||
/// <para>
|
||||
/// Cross-checked against ACE
|
||||
/// <c>references/ACE/Source/ACE.Server/Physics/PhysicsObj.cs:381-405</c>
|
||||
/// (line-for-line C# port of the same logic). Note: ACE adds
|
||||
/// <c>state.HasFlag(IsImpenetrable)</c> (mover-impenetrable) to the
|
||||
/// collide list; retail's pseudo-C only checks the target's
|
||||
/// <c>IsImpenetrable()</c>. acdream follows retail.
|
||||
/// (line-for-line C# port of the same logic). #299: retail's pseudo-C
|
||||
/// (<c>acclient_2013_pseudo_c.txt:276824-276827</c>) checks the MOVER's own
|
||||
/// <c>state & IS_IMPENETRABLE (0x80)</c> — the sign-bit test on the
|
||||
/// truncated 16-bit <c>ebx->object_info.state</c> the decompiler renders as
|
||||
/// <c>if (state_1 < 0)</c>, sitting immediately before the target's
|
||||
/// <c>IsImpenetrable()</c> check at the same nesting level — in addition to
|
||||
/// the target's own <c>IsImpenetrable()</c>, either alone disqualifying the
|
||||
/// tentative PvP exemption. ACE's port has both; acdream previously had only
|
||||
/// the target half. The bit position is confirmed against
|
||||
/// <c>OBJECTINFO::init@0x0050cf30</c> (<c>this->state |= 0x80</c> when
|
||||
/// <c>weenie_obj->IsImpenetrable()</c>) and ACE's own
|
||||
/// <c>ObjectInfoState.IsImpenetrable = 0x80</c>
|
||||
/// (<c>ACE.Server/Physics/ObjectInfo.cs:17</c>) — the earlier comment here
|
||||
/// blaming ACE for an addition was itself the divergence; ACE was
|
||||
/// retail-faithful and acdream was missing the mover half.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class CollisionExemption
|
||||
|
|
@ -98,9 +109,14 @@ public static class CollisionExemption
|
|||
// if any of the COLLIDE conditions hold.
|
||||
bool collide = false;
|
||||
|
||||
// 4a. Impenetrable target → collide.
|
||||
// acclient_2013_pseudo_c.txt:276826.
|
||||
if ((targetFlags & EntityCollisionFlags.IsImpenetrable) != 0)
|
||||
// 4a. Impenetrable target OR impenetrable mover → collide.
|
||||
// acclient_2013_pseudo_c.txt:276824-276827 — retail checks
|
||||
// BOTH: the mover's own state&0x80 (IS_IMPENETRABLE) short-
|
||||
// circuits first, then the target's IsImpenetrable(). #299:
|
||||
// acdream previously ported only the target half.
|
||||
if ((moverState & ObjectInfoState.IsImpenetrable) != 0)
|
||||
collide = true;
|
||||
if (!collide && (targetFlags & EntityCollisionFlags.IsImpenetrable) != 0)
|
||||
collide = true;
|
||||
|
||||
// 4b. Both PK → collide.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue