Byte-decoded GetStart/MaxConstraintDistance (0x0050ebc0/0x0050ec10) from the PDB-paired v11.4186 binary: start = outdoor 10 m / indoor 5 m, max = outdoor 50 m / indoor 20 m; the player-vs-remote branch is vestigial (identical constant pairs). ACE's start mapping is inverted - do not copy. Full SmartBox::HandleReceivedPosition 0x00453fd0 arming flow transcribed (remote self-anchor post-MoveOrTeleport, player anchors to received position, teleport branch zeroes velocity). TS-35 + #167 retire together at the P5 port. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.5 KiB
#167 ConstraintManager leash — constants recovered + arming flow (Campaign P P5)
2026-07-30. Both #167 blockers are now research-solved; only the port
remains. No cdb session was needed: the two "unknown x87 constants" were
recovered by decoding the raw machine code of the matching binary
(C:\Users\erikn\Downloads\acclient.exe, v11.4186, PDB-paired — verified
GUID match per the retail debugger toolchain doc).
1. The getters, byte-decoded (FACT)
CPhysicsObj::GetStartConstraintDistance @ 0x0050ebc0 and
GetMaxConstraintDistance @ 0x0050ec10 are FPU-return getters whose
fld operands Binary Ninja elided (the pseudo-C shows a bare
this->m_position;). Raw bytes (file offset 0x10ebc0/0x10ec10):
3b 0d 58 3d 84 00 cmp ecx, [0x00843d58] ; this == player_object?
75 1d jnz non_player
8b 41 4c mov eax, [ecx+0x4c] ; m_position.objcell_id
25 ff ff 00 00 and eax, 0xFFFF
3d 00 01 00 00 cmp eax, 0x100
73 07 jae indoor ; low16 >= 0x100 = EnvCell
d9 05 <rdata> fld dword [outdoor_const]
c3 ret
indoor: d9 05 <..> fld dword [indoor_const]
c3 ret
non_player: ; identical cell test, second constant pair
Constant values read from .rdata:
| player outdoor | player indoor | remote outdoor | remote indoor | |
|---|---|---|---|---|
| Start (0x007c6abc..c8) | 10.0 | 5.0 | 10.0 | 5.0 |
| Max (0x007c6acc..d8) | 50.0 | 20.0 | 50.0 | 20.0 |
Two consequences (FACT):
- The player-vs-remote branch is vestigial — both sides load
identical values. Effective semantics:
start = outdoor 10 m / indoor 5 m,max = outdoor 50 m / indoor 20 m(indoor = cell low16 ≥ 0x100). - ACE's
GetStartConstraintDistanceis INVERTED (ACE PhysicsObj.cs:620: outdoor 5 / indoor 10). ACE's max mapping (outdoor 50 / indoor 20) matches the binary. Do NOT copy ACE's start mapping. (feedback_acme_oracle / binary-wins rule.)
2. The arming flow — SmartBox::HandleReceivedPosition @ 0x00453fd0 (FACT)
Pseudo-C lines ~92940-93060. After the update-time staleness gates and
unset_parent/SetPlacementFrame handling:
- Remote object (
arg2 != this->player): callMoveOrTeleport(obj, &recvPos, ts, arg5, arg6); only if it returns nonzero (the position was NOT hard-teleport-applied), arm the leash anchored to the object's own current position:ConstrainTo(obj, &obj->m_position, start, max)(0x00454254-72). - Player, teleport-newer (
newer_event(TELEPORT_TS, ts)):SmartBox::TeleportPlayer(&recvPos), thenConstrainTo(player, &recvPos, start, max)— anchored to the received position — thenset_velocity(player, {0,0,0}, 1)(0x0045415f-c0). - Player, normal:
ConstrainTo(player, &recvPos, start, max)anchored to the received position; then, ifcmdinterp->UsePositionFromServer() && arg5,InterpolateTo(&recvPos, -GetAutonomyLevel())(0x004541c9-422c).
The taper/enforcement side (ConstraintManager::UseTime feeding
adjust_offset, IsFullyConstrained = ConstraintDistanceMax * 0.9 < offset) is already ported in
src/AcDream.Core/Physics/Motion/ConstraintManager.cs (R5-V1,
docs/research/2026-07-03-r5-managers/); it has simply never been armed.
3. Port shape for P5 (INFERENCE — implementation guidance)
- Add the four-constant getters (outdoor/indoor by full cell id low16) at the body/host layer; keep the vestigial player/remote split OUT (note it in a code comment with this doc as the cite).
- Arm at acdream's inbound-position equivalents of the three branches:
the remote UpdatePosition acceptance tail (post-
MoveOrTeleportrouting in the live-entity network update path) and the local player's accepted-Position path (normal + teleport). Anchor per §2. PhysicsBody.IsFullyConstrained(register TS-35 stub) becomes a read throughPositionManager/ConstraintManager, sojump_is_allowed's ported gate fires (WeenieError 0x47) while rubber-banding. TS-35 and #167 retire together, same commit.- Conformance tests: constant table incl. the ACE-inversion pin (outdoor start MUST be 10, not 5); leash-armed jump refusal; remote-vs-player anchor difference; teleport-branch velocity zero.
Open questions
None for the constants/flow. Remaining implementation risk is only where acdream's position-acceptance seams sit today (J6.3 moved teleport correlation into Runtime — the implementer must find the current owner rather than trusting older file cites).