# ConstraintManager — retail decomp extract Source: `docs/research/named-retail/acclient_2013_pseudo_c.txt` (Sept 2013 EoR build, PDB-named). Struct source: `docs/research/named-retail/acclient.h`. **Address correction:** the task listed `CPhysicsObj::IsFullyConstrained` at `0x0050f730`. The actual address in the corpus is **`0x0050ec60`**. Verified by grepping the definition line (`276520:0050ec60 int32_t __fastcall CPhysicsObj::IsFullyConstrained(...)`) and cross-checked against its caller in `CMotionInterp::jump_is_allowed` at `0x005282fd`. --- ## struct ConstraintManager (acclient.h, comment `/* 3467 */`) ```c /* 3467 */ struct __cppobj ConstraintManager { CPhysicsObj *physics_obj; int is_constrained; float constraint_pos_offset; Position constraint_pos; float constraint_distance_start; float constraint_distance_max; }; ``` ## struct PositionManager (acclient.h, comment `/* 3468 */`) — the owning object ```c /* 3468 */ struct __cppobj PositionManager { InterpolationManager *interpolation_manager; StickyManager *sticky_manager; ConstraintManager *constraint_manager; CPhysicsObj *physics_obj; }; ``` Note the field order in `PositionManager` (`interpolation_manager`, `sticky_manager`, `constraint_manager`, `physics_obj`) matches the `PositionManager::Create` allocation writes to offsets `0x0`, `0x4`, `0x8`, `0xc` respectively (see below) and the `PositionManager::Destroy` teardown order (`interpolation_manager` → `sticky_manager` → `constraint_manager`). `ConstraintManager` field layout maps onto `ConstraintManager::Create`'s raw offset writes: `physics_obj`=`0x0`, `is_constrained`=`0x4`, `constraint_pos_offset`=`0x8` — wait, per the decompiled writes below the offsets are actually `0x0`/`0x4`/`0x8`/`0xc` (`vtable` field of `Position constraint_pos` at `0xc`)/`0x10`.../`0x48` is `constraint_distance_start`, `0x4c` is `constraint_distance_max`. The compiler emits a `Position` (which itself embeds a `Frame` with its own vtable-looking sentinel field — see NOTE in `~ConstraintManager` below) between `constraint_pos_offset` and `constraint_distance_start`, consistent with the struct's declared member order. --- ## ConstraintManager::SetPhysicsObject — `0x00556090` ```c 00556090 void __thiscall ConstraintManager::SetPhysicsObject(class ConstraintManager* this, class CPhysicsObj* arg2) 00556090 { 00556096 if (this->physics_obj == 0) 00556096 { 005560ad this->physics_obj = arg2; 005560af return; 00556096 } 00556096 00556098 this->physics_obj = 0; 0055609a this->is_constrained = 0; 0055609d this->constraint_pos_offset = 0f; 005560a4 this->physics_obj = arg2; 00556090 } ``` ## ConstraintManager::UnConstrain — `0x005560c0` ```c 005560c0 void __fastcall ConstraintManager::UnConstrain(class ConstraintManager* this) 005560c0 { 005560c0 this->is_constrained = 0; 005560c0 } ``` ## ConstraintManager::IsFullyConstrained — `0x005560d0` ```c 005560d0 int32_t __fastcall ConstraintManager::IsFullyConstrained(class ConstraintManager const* this) 005560d0 { 005560d0 long double x87_r7 = ((long double)this->constraint_pos_offset); 005560d6 long double x87_r6_1 = (((long double)this->constraint_distance_max) * ((long double)0.90000000000000002)); 005560dc (x87_r6_1 - x87_r7); 005560de int32_t eax; 005560de eax = ((((x87_r6_1 < x87_r7) ? 1 : 0) << 8) | ((((0) ? 1 : 0) << 9) | (((((FCMP_UO(x87_r6_1, x87_r7))) ? 1 : 0) << 0xa) | ((((x87_r6_1 == x87_r7) ? 1 : 0) << 0xe) | 0)))); 005560e0 bool p = /* bool p = unimplemented {test ah, 0x5} */; 005560e0 005560e3 if (p) 005560ed return 0; 005560ed 005560ea return 1; 005560d0 } ``` NOTE (garbled bitfield mush / x87 flags mush): the `eax = (...)` line is Binary Ninja's attempt to render the x87 `FCOMI`/`FSTSW`+`SAHF`-style compare-and-test-flags sequence as bit-packed pseudocode. It is computing `constraint_distance_max * 0.9 <=> constraint_pos_offset` and then `test ah, 0x5` checks the ZF/CF-equivalent bits packed into `ah` after `fnstsw ax`. The semantic read: `p` is true when `(constraint_distance_max * 0.9) < constraint_pos_offset` OR the compare was unordered (NaN) — i.e. `test ah,5` tests bits 0 (C0/"below") and 2 (C3/"equal") of the FPU status word as loaded into AH, the classic x87 `jbe`-equivalent pattern. So: **`IsFullyConstrained` returns `false` (0) if `constraint_pos_offset >= 0.9 * constraint_distance_max` (or unordered), else returns `true` (1)**. In plain terms: the object counts as "fully constrained" while it is still within 90% of the max leash distance; once it has drifted past 90% of that distance it is no longer "fully" constrained (this is the gate `CMotionInterp::jump_is_allowed` reads to block jump attempts while straining at the very end of a constraint leash). ## ConstraintManager::~ConstraintManager — `0x005560f0` ```c 005560f0 void __fastcall ConstraintManager::~ConstraintManager(class ConstraintManager* this) 005560f0 { 005560f2 this->is_constrained = 0; 005560f5 this->constraint_pos_offset = 0f; 005560f8 this->physics_obj = 0; 005560fa this->constraint_pos.vtable = 0x79285c; 005560f0 } ``` NOTE: `this->constraint_pos.vtable = 0x79285c` — `constraint_pos` is a `Position` field (struct member, not a pointer), so this is Binary Ninja's rendering of the embedded `Frame`'s vtable-pointer slot being reset to its static vtable address as part of the `Position`/`Frame` subobject's implicit destructor inlining. Not a real "vtable swap"; just the compiler zeroing/resetting the embedded Frame's identity field during teardown. ## ConstraintManager::Create — `0x00556110` (factory) ```c 00556110 class ConstraintManager* ConstraintManager::Create(class CPhysicsObj* arg1) 00556110 { 00556114 void* result = operator new(0x5c); 00556114 00556122 if (result == 0) 00556177 return 0; 00556177 00556124 *(uint32_t*)result = 0; 00556126 *(uint32_t*)((char*)result + 4) = 0; 00556129 *(uint32_t*)((char*)result + 8) = 0; 0055612f *(uint32_t*)((char*)result + 0xc) = 0x796910; 00556136 *(uint32_t*)((char*)result + 0x10) = 0; 00556139 *(uint32_t*)((char*)result + 0x14) = 0x3f800000; 0055613f *(uint32_t*)((char*)result + 0x18) = 0; 00556142 *(uint32_t*)((char*)result + 0x1c) = 0; 00556145 *(uint32_t*)((char*)result + 0x20) = 0; 00556148 *(uint32_t*)((char*)result + 0x48) = 0; 0055614b *(uint32_t*)((char*)result + 0x4c) = 0; 0055614e *(uint32_t*)((char*)result + 0x50) = 0; 00556151 Frame::cache(((char*)result + 0x14)); 00556156 *(uint32_t*)((char*)result + 0x54) = 0; 00556159 *(uint32_t*)((char*)result + 0x58) = 0; 00556159 0055615e if (*(uint32_t*)result != 0) 0055615e { 00556160 *(uint32_t*)((char*)result + 4) = 0; 00556163 *(uint32_t*)((char*)result + 8) = 0; 00556166 *(uint32_t*)result = 0; 0055615e } 0055615e 0055616c *(uint32_t*)result = arg1; 00556172 return result; 00556110 } ``` NOTE: allocation is `0x5c` (92) bytes — `sizeof(ConstraintManager)`. Field-offset mapping against the struct decl: `+0x0`=`physics_obj`, `+0x4`=`is_constrained`, `+0x8`=`constraint_pos_offset`, `+0xc..0x50`=`constraint_pos` (embedded `Position`, whose own `objcell_id`/`frame` subfields explain the `0x796910` vtable-like constant at `+0xc` and the `Frame::cache` call seeding the rotation quaternion identity `w=1.0` i.e. `0x3f800000` at `+0x14`), `+0x48`=`constraint_distance_start`, `+0x4c`=`constraint_distance_max`. The trailing `+0x54`/`+0x58` zeroing is past the declared struct fields in the header excerpt we have — likely padding/alignment or a field the header comment block truncated; not load-bearing for the port (all consumed fields — `physics_obj`, `is_constrained`, `constraint_pos_offset`, `constraint_pos`, `constraint_distance_start`, `constraint_distance_max` — are accounted for). The odd `if (*(uint32_t*)result != 0) { zero everything }` right after construction reads as dead/defensive code from an inlined check that can't actually trigger here (the fields were all just zeroed above) — flagging as NOTE, not altering. ## ConstraintManager::adjust_offset — `0x00556180` ```c 00556180 void __thiscall ConstraintManager::adjust_offset(class ConstraintManager* this, class Frame* arg2, double arg3) 00556180 { 00556186 class CPhysicsObj* physics_obj = this->physics_obj; 00556186 0055618a if (physics_obj != 0) 0055618a { 00556190 int32_t is_constrained = this->is_constrained; 00556190 00556195 if (is_constrained != 0) 00556195 { 005561a7 if ((physics_obj->transient_state & 1) != 0) 005561a7 { 005561a9 long double x87_r7_1 = ((long double)this->constraint_pos_offset); 005561ac long double temp1_1 = ((long double)this->constraint_distance_max); 005561ac (x87_r7_1 - temp1_1); 005561af physics_obj = ((((x87_r7_1 < temp1_1) ? 1 : 0) << 8) | ((((0) ? 1 : 0) << 9) | (((((FCMP_UO(x87_r7_1, temp1_1))) ? 1 : 0) << 0xa) | ((((x87_r7_1 == temp1_1) ? 1 : 0) << 0xe) | 0)))); 005561af 005561b4 if ((*(uint8_t*)((char*)physics_obj)[1] & 1) != 0) 005561b4 { 005561e7 long double x87_r7_2 = ((long double)this->constraint_pos_offset); 005561ea long double temp2_1 = ((long double)this->constraint_distance_start); 005561ea (x87_r7_2 - temp2_1); 005561ed physics_obj = ((((x87_r7_2 < temp2_1) ? 1 : 0) << 8) | ((((0) ? 1 : 0) << 9) | (((((FCMP_UO(x87_r7_2, temp2_1))) ? 1 : 0) << 0xa) | ((((x87_r7_2 == temp2_1) ? 1 : 0) << 0xe) | 0)))); 005561ef bool p_1 = /* bool p_1 = unimplemented {test ah, 0x41} */; 005561ef 005561f2 if (p_1) 005561f2 { 005561f7 int32_t is_constrained_1 = is_constrained; 00556209 Vector3::operator*=(&arg2->m_fOrigin, ((float)((((long double)this->constraint_distance_max) - ((long double)this->constraint_pos_offset)) / (((long double)this->constraint_distance_max) - ((long double)this->constraint_distance_start))))); 005561f2 } 005561b4 } 005561b4 else 005561b4 { 005561c2 arg2->m_fOrigin.x = 0; 005561c2 arg2->m_fOrigin.y = 0f; 005561c2 arg2->m_fOrigin.z = 0f; 005561b4 } 005561a7 } 005561a7 0055620e arg2->m_fOrigin; 00556211 arg2->m_fOrigin; 00556233 this->constraint_pos_offset = ((float)(((long double)arg2->m_fOrigin.x) + ((long double)this->constraint_pos_offset))); 00556195 } 0055618a } 00556180 } ``` NOTE (garbled bitfield mush + BN variable-reuse artifact): `physics_obj` gets *reused* as a scratch int32 to hold the packed x87 comparison-flags value at `005561af` and again at `005561ed` — this is Binary Ninja recycling the SSA slot, NOT a real reassignment of the `CPhysicsObj*` pointer. The `this->physics_obj` local captured at `00556186` is what's actually read at `005561a7` (`physics_obj->transient_state`) and `005561b4` (`*(uint8_t*)((char*)physics_obj)[1] & 1` — this is checking a byte of `transient_state` again, offset `+1`, i.e. a second flag byte inside the same bitfield/word). Do not port the reused `physics_obj` int32 as if it becomes a different physics object; it's the same pointer, just overwritten as dead-value scratch space by the decompiler's register allocator view. `bool p_1 = /* unimplemented {test ah, 0x41} */` is the same x87-flags-in-AH pattern as `IsFullyConstrained` above, testing bits 0 and 6 this time (C0 + C6/C2 combo depending on encoding) — the classic `jbe`-vs-`jae` variant. Given the surrounding compare (`constraint_pos_offset < constraint_distance_start` or equal), and that the guarded body computes a **lerp fraction** `(constraint_distance_max - constraint_pos_offset) / (constraint_distance_max - constraint_distance_start)` applied to `arg2->m_fOrigin` via `*=`, the semantic read is: **when the object has NOT yet reached (or has just reached) `constraint_distance_start`, scale the frame's offset delta by how far through the start→max leash band the object currently sits** (a ramp/taper multiplier — presumably smoothly reducing how much of the requested frame delta gets applied as the leash tightens). `test ah,0x41` semantically reads as "less-than-or-unordered-or-equal" (`p_1` true when NOT clearly greater), so the ramp only applies while still inside the band; once past `constraint_distance_start` typical port intent should skip the scale (leave `arg2->m_fOrigin` alone) — consistent with the `else` branch two levels up which zeroes `m_fOrigin` outright when `transient_state`'s second flag bit is clear. Mechanically, regardless of the exact flag polarity (worth a live cdb single-step check if the port's leash-taper feel diverges from retail), the function's shape is: 1. No-op if no `physics_obj` or not `is_constrained`. 2. If `transient_state & 1` (some "active"/"in contact" style flag): - If a second transient-state flag byte's bit 0 is set: scale the incoming frame delta `arg2->m_fOrigin` by a lerp fraction based on `(max - pos_offset) / (max - start)`, gated by a comparison of `pos_offset` vs `constraint_distance_start`. - Else: zero the incoming frame delta entirely (fully clamp movement). 3. Unconditionally (after the above), accumulate: `constraint_pos_offset += arg2->m_fOrigin.x` (note: only the `.x` component is added — `arg2->m_fOrigin` is read twice at `0055620e`/`00556211` with no visible effect, likely a debug/no-op dead read from the decompiler, or hints there's a per-component variant BN collapsed; only the final `.x`-add survived as an observable store). ## ConstraintManager::ConstrainTo — `0x00556240` ```c 00556240 void __thiscall ConstraintManager::ConstrainTo(class ConstraintManager* this, class Position const* arg2, float arg3, float arg4) 00556240 { 00556248 this->is_constrained = 1; 00556259 this->constraint_pos.objcell_id = arg2->objcell_id; 0055625c Frame::operator=(&this->constraint_pos.frame, &arg2->frame); 00556271 this->constraint_distance_start = arg3; 00556274 this->constraint_distance_max = arg4; 0055627c this->constraint_pos_offset = ((float)Position::distance(arg2, &this->physics_obj->m_position)); 00556240 } ``` Straightforward: pins the leash anchor (`constraint_pos` = copy of `arg2`'s cell+frame), sets `is_constrained = true`, sets the start/max leash-band radii from `arg3`/`arg4`, and initializes `constraint_pos_offset` to the CURRENT distance from the anchor to the physics object's live position (`Position::distance(arg2, &physics_obj->m_position)`) — i.e. the leash starts already "extended" to wherever the object presently is relative to the constraint anchor, not to zero. --- ## PositionManager-level seams (the actual public API — ConstraintManager is ## lazily-created and private underneath) `ConstraintManager` is never touched directly from outside `PositionManager`. `PositionManager` lazily creates it on first `ConstrainTo` call and forwards through it. ```c 00555190 void __thiscall PositionManager::adjust_offset(class PositionManager* this, class Frame* arg2, double arg3) 00555190 { 00555191 int32_t ebx = arg3; 0055519d class InterpolationManager* interpolation_manager = this->interpolation_manager; 005551a2 int32_t edi = *(uint32_t*)((char*)arg3)[4]; 005551a2 005551a6 if (interpolation_manager != 0) 005551a6 { 005551a8 int32_t var_14_1 = edi; 005551ab InterpolationManager::adjust_offset(interpolation_manager, arg2, ebx); 005551a6 } 005551a6 005551b0 class StickyManager* sticky_manager = this->sticky_manager; 005551b0 005551b5 if (sticky_manager != 0) 005551b5 { 005551b7 int32_t var_14_2 = edi; 005551ba StickyManager::adjust_offset(sticky_manager, arg2, ebx); 005551b5 } 005551b5 005551bf class ConstraintManager* constraint_manager = this->constraint_manager; 005551bf 005551c4 if (constraint_manager != 0) 005551c4 { 005551c6 int32_t var_14_3 = edi; 005551c9 ConstraintManager::adjust_offset(constraint_manager, arg2, ebx); 005551c4 } 00555190 } ``` Chains ALL THREE sub-managers' `adjust_offset` in a fixed order: `InterpolationManager` → `StickyManager` → `ConstraintManager`, each optional (only called if that sub-manager exists). This is the per-frame(?) offset-adjustment dispatcher `PositionManager` uses to let interpolation/sticky/constraint all have a say in shaping a `Frame` delta before it's applied. ```c 00555280 void __thiscall PositionManager::ConstrainTo(class PositionManager* this, class Position const* arg2, float arg3, float arg4) 00555280 { 00555288 if (this->constraint_manager == 0) 00555296 this->constraint_manager = ConstraintManager::Create(this->physics_obj); 00555296 00555299 class ConstraintManager* constraint_manager = this->constraint_manager; 00555299 0055529f if (constraint_manager == 0) 005552a6 return; 005552a6 005552a1 /* tailcall */ 005552a1 return ConstraintManager::ConstrainTo(constraint_manager, arg2, arg3, arg4); 00555280 } 005552b0 void __fastcall PositionManager::UnConstrain(class PositionManager* this) 005552b0 { 005552b0 class ConstraintManager* constraint_manager = this->constraint_manager; 005552b0 005552b5 if (constraint_manager == 0) 005552bc return; 005552bc 005552b7 /* tailcall */ 005552b7 return ConstraintManager::UnConstrain(constraint_manager); 005552b0 } 005552c0 int32_t __fastcall PositionManager::IsFullyConstrained(class PositionManager const* this) 005552c0 { 005552c0 class ConstraintManager* constraint_manager = this->constraint_manager; 005552c0 005552c5 if (constraint_manager == 0) 005552ce return 0; 005552ce 005552c7 /* tailcall */ 005552c7 return ConstraintManager::IsFullyConstrained(constraint_manager); 005552c0 } ``` `PositionManager::Create` (`0x005552d0`) wires a freshly-allocated `PositionManager`'s `physics_obj` back-pointer into any already-non-null sub-managers (only relevant for copy/re-init paths since a fresh `PositionManager` starts with all-null sub-managers): ```c 005552d0 class PositionManager* PositionManager::Create(class CPhysicsObj* arg1) 005552d0 { 005552d3 void* result = operator new(0x10); ... 0055531d class ConstraintManager* ecx_2 = *(uint32_t*)((char*)result + 8); 0055531d 00555322 if (ecx_2 != 0) 00555325 ConstraintManager::SetPhysicsObject(ecx_2, arg1); 00555325 0055532e return result; 005552d0 } ``` `PositionManager::Destroy` (`0x00555340`) tears down and `delete`s all three sub-managers, `ConstraintManager` last: ```c 00555340 void __fastcall PositionManager::Destroy(class PositionManager* this) 00555340 { ... 00555377 class ConstraintManager* constraint_manager = this->constraint_manager; 0055537c this->sticky_manager = nullptr; 0055537c 00555383 if (constraint_manager != 0) 00555383 { 00555387 ConstraintManager::~ConstraintManager(constraint_manager); 0055538d operator delete(constraint_manager); 00555383 } 00555383 00555396 this->constraint_manager = nullptr; 00555340 } ``` --- ## CPhysicsObj-level seams (public API callers actually use) ```c 0050ec10 void __fastcall CPhysicsObj::GetMaxConstraintDistance(class CPhysicsObj const* this) 0050ec10 { 0050ec16 if (this == CPhysicsObj::player_object) 0050ec16 { 0050ec18 this->m_position; 0050ec2d return; 0050ec16 } 0050ec16 0050ec35 this->m_position; 0050ec10 } 0050ebc0 void __fastcall CPhysicsObj::GetStartConstraintDistance(class CPhysicsObj const* this) 0050ebc0 { 0050ebc6 if (this == CPhysicsObj::player_object) 0050ebc6 { 0050ebc8 this->m_position; 0050ebdd return; 0050ebc6 } 0050ebc6 0050ebe5 this->m_position; 0050ebc0 } ``` NOTE (BN decompilation artifact / x87 return-value elision): both functions have `void` signatures per BN's guessed prototype but are clearly meant to RETURN a float (they're called as `ecx_26 = CPhysicsObj::GetMaxConstraintDistance(arg2)` immediately followed by `(float)st0_6` casts at the call sites — an x87 FPU return value living in `st0` that Binary Ninja failed to attach to the declared return type). Body-wise all we get is `this->m_position;` as a bare expression on both the player-branch and fallthrough paths — BN elided the actual field read/constant selection (this is likely `this->m_position.something` or a per-type constant lookup that got collapsed to a dead-looking statement). **This function needs a live cdb read of `st0` after the call, or a Ghidra re-decompile with a corrected float-return signature, to recover the actual values.** Given the call-site pattern (constraining the player and other movers to a "home" position after teleport/movement-timeout in `SmartBox::HandleReceivedPosition`), the two functions almost certainly return small fixed-radius constants (a "start easing" radius and a "max leash" radius), likely DIFFERENT for the player vs. non-player case (hence the `this == CPhysicsObj::player_object` branch in both). Do not guess the literal values — flag as an open research item before porting numeric constants. ```c 00510520 void __thiscall CPhysicsObj::ConstrainTo(class CPhysicsObj* this, class Position const* arg2, float arg3, float arg4) 00510520 { 00510523 CPhysicsObj::MakePositionManager(this); 00510528 class PositionManager* position_manager = this->position_manager; 00510528 00510531 if (position_manager == 0) 00510538 return; 00510538 00510533 /* tailcall */ 00510533 return PositionManager::ConstrainTo(position_manager, arg2, arg3, arg4); 00510520 } ``` `CPhysicsObj::ConstrainTo` lazily ensures a `PositionManager` exists (`MakePositionManager`) then forwards. This is the entry point external code calls. ```c 0050ec60 int32_t __fastcall CPhysicsObj::IsFullyConstrained(class CPhysicsObj const* this) 0050ec60 { 0050ec60 class PositionManager* position_manager = this->position_manager; 0050ec60 0050ec68 if (position_manager == 0) 0050ec71 return 0; 0050ec71 0050ec6a /* tailcall */ 0050ec6a return PositionManager::IsFullyConstrained(position_manager); 0050ec60 } ``` (Address correction noted at top of doc: this is `0x0050ec60`, not the task-supplied `0x0050f730`.) There is no separate `CPhysicsObj::UnConstrain` — callers go straight to `PositionManager::UnConstrain(this->position_manager)` (see caller list below); only `ConstrainTo` and `IsFullyConstrained` got a `CPhysicsObj`-level convenience wrapper. --- ## CALLERS — when does retail actually constrain an object? ### 1. `SmartBox::HandleReceivedPosition` (`0x00453fd0`) — THE constrain call site All three live `CPhysicsObj::ConstrainTo` calls in the entire corpus are inside this one function, at three different branches of its position-update-reconciliation logic: **Branch A — non-player mover, after a successful move/teleport resolve (`0x00454254`):** ```c 0045414d if (arg2 != this->player) 0045414d { 00454254 if (CPhysicsObj::MoveOrTeleport(arg2, &var_48, arg8, arg5, arg6) != 0) 00454254 { 00454258 int32_t ecx_26; 00454258 ecx_26 = CPhysicsObj::GetMaxConstraintDistance(arg2); 0045425d int32_t var_68_14 = ecx_26; 00454263 ecx_28 = CPhysicsObj::GetStartConstraintDistance(arg2); 00454268 int32_t var_6c_9 = ecx_28; 00454272 CPhysicsObj::ConstrainTo(arg2, &arg2->m_position, ((float)st0_7), ((float)st0_6)); 00454254 } 00454254 00454254 return; 0045414d } ``` For a non-player object (`arg2`), once `MoveOrTeleport` succeeds, it is constrained **to its own current position** (`&arg2->m_position` as the anchor) with start/max radii from `GetStartConstraintDistance`/`GetMaxConstraintDistance`. **Branch B — player, on a fresh TELEPORT timestamp event (`0x0045415f`):** ```c 0045415f if (CPhysicsObj::newer_event(arg2, TELEPORT_TS, arg8) != 0) 0045415f { 00454168 SmartBox::TeleportPlayer(this, &var_48); 0045416f ecx_14 = CPhysicsObj::GetMaxConstraintDistance(arg2); 0045417a ecx_16 = CPhysicsObj::GetStartConstraintDistance(arg2); 0045418a CPhysicsObj::ConstrainTo(arg2, &var_48, ((float)st0_2), ((float)st0_1)); 0045418f class CPhysicsObj* player_2 = this->player; 0045419c int32_t var_54 = 0; // zero vector 004541b4 CPhysicsObj::set_velocity(player_2, &var_54, 1); 004541c0 return; 0045415f } ``` On a server teleport of the local player, `SmartBox::TeleportPlayer` snaps position, then the player is constrained **to the newly-received server position** (`&var_48`, the decoded incoming `Position`), and velocity is zeroed. **Branch C — player, fallthrough / non-teleport received-position path (`0x004541c9`):** ```c 004541c9 ecx_19 = CPhysicsObj::GetMaxConstraintDistance(this->player); 004541d8 ecx_21 = CPhysicsObj::GetStartConstraintDistance(this->player); 004541ec CPhysicsObj::ConstrainTo(this->player, &var_48, ((float)st0_5), ((float)st0_4)); 004541f1 class CommandInterpreter* cmdinterp_1 = this->cmdinterp; 0045420a if ((cmdinterp_1->vtable->UsePositionFromServer(cmdinterp_1) != 0 && arg5 != 0)) 0045420a { ... CPhysicsObj::InterpolateTo(arg2, &var_48, ...); ``` Every OTHER received server position update for the local player (not a teleport-flagged event) ALSO constrains the player to the received position (`&var_48`), and then — depending on `UsePositionFromServer`/autonomy settings — may additionally kick off `InterpolateTo`. So the leash gets re-anchored on essentially every server position correction, whether or not interpolation is used to visually smooth toward it. **Summary for Branch A/B/C:** retail constrains an object to a `Position` (self or server-received) with a start/max leash-band pair **every time `SmartBox` processes an inbound position update for that object** — this is the "rubber-band" leash mechanism that keeps the client's locally-simulated position from drifting too far from the server-authoritative position between updates. It's re-armed (re-`ConstrainTo`'d) on every inbound position packet, not set once. ### 2. `CPhysicsObj::teleport_hook` (`0x00514ed0`) — THE unconstrain call site ```c 00514ed0 void __fastcall CPhysicsObj::teleport_hook(class CPhysicsObj* this, int32_t arg2) 00514ed0 { 00514ed3 class MovementManager* movement_manager = this->movement_manager; 00514edb if (movement_manager != 0) 00514edb MovementManager::CancelMoveTo(movement_manager, edx); 00514ee4 class PositionManager* position_manager = this->position_manager; 00514eec if (position_manager != 0) 00514eee PositionManager::UnStick(position_manager); 00514ef3 class PositionManager* position_manager_1 = this->position_manager; 00514efb if (position_manager_1 != 0) 00514efd PositionManager::StopInterpolating(position_manager_1); 00514f02 class PositionManager* position_manager_2 = this->position_manager; 00514f0a if (position_manager_2 != 0) 00514f0c PositionManager::UnConstrain(position_manager_2); 00514f11 class TargetManager* target_manager = this->target_manager; 00514f19 if (target_manager != 0) 00514f19 { 00514f1b TargetManager::ClearTarget(target_manager); 00514f28 TargetManager::NotifyVoyeurOfEvent(this->target_manager, Teleported_TargetStatus); 00514f19 } 00514f31 CPhysicsObj::report_collision_end(this, 1); 00514ed0 } ``` The ONLY `UnConstrain` call in the corpus. `teleport_hook` is a general "this object just got relocated in a way that invalidates all continuity state" cleanup: it cancels any active `MoveTo`, un-sticks (StickyManager), stops interpolation, **un-constrains**, clears target tracking, and ends collision reporting. So the leash is torn down whenever an object teleports (any teleport, not just the player's) — makes sense, since a teleport by definition means "the position just legitimately jumped," so the anti-drift leash from the PREVIOUS anchor must be dropped rather than fight the teleport. ### 3. `CMotionInterp::jump_is_allowed` (`0x005282b0`) — THE read call site ```c 005282b0 uint32_t __thiscall CMotionInterp::jump_is_allowed(class CMotionInterp* this, float arg2, int32_t* arg3) 005282b0 { 005282b8 if (this->physics_obj != 0) 005282b8 { ... 005282fd if (CPhysicsObj::IsFullyConstrained(this->physics_obj) != 0) 00528305 return 0x47; 00528305 00528308 class LListData* head_ = this->pending_motions.head_; ... ``` `jump_is_allowed` reads `IsFullyConstrained` and, if true, immediately returns error code `0x47` (rejecting the jump attempt) before even checking pending motions / jump-charge state. This is the ONLY read-site of `IsFullyConstrained` in the corpus. Ties back to the mechanical read of `ConstraintManager::IsFullyConstrained` above: while the object is still within 90% of its max leash distance from the constraint anchor, it counts as "fully constrained" and jumping is blocked outright — i.e. **you cannot jump while the client's simulated position is being actively rubber-banded back toward a server-received position inside the tight leash band.** Only once you've drifted past 90% of the leash (or the leash has been dropped via `UnConstrain`/teleport) does the jump-blocking gate open. --- ## CPhysicsObj-level "constrain" seam grep (exhaustive) Full result of `grep "::ConstrainTo(\|::UnConstrain(\|::IsFullyConstrained("` across the corpus — every call site, no filtering: ``` 93007 CPhysicsObj::ConstrainTo(arg2, &arg2->m_position, ...) [SmartBox::HandleReceivedPosition, Branch A] 93024 CPhysicsObj::ConstrainTo(arg2, &var_48, ...) [SmartBox::HandleReceivedPosition, Branch B] 93041 CPhysicsObj::ConstrainTo(this->player, &var_48, ...) [SmartBox::HandleReceivedPosition, Branch C] 276520 CPhysicsObj::IsFullyConstrained (definition) 276529 -> PositionManager::IsFullyConstrained (tailcall) 278353 CPhysicsObj::ConstrainTo (definition) 278363 -> PositionManager::ConstrainTo (tailcall) 283140 PositionManager::UnConstrain(position_manager_2) [CPhysicsObj::teleport_hook] 305524 CPhysicsObj::IsFullyConstrained(this->physics_obj) != 0 [CMotionInterp::jump_is_allowed] 352186 PositionManager::ConstrainTo (definition) 352198 -> ConstraintManager::ConstrainTo (tailcall) 352203 PositionManager::UnConstrain (definition) 352212 -> ConstraintManager::UnConstrain (tailcall) 352217 PositionManager::IsFullyConstrained (definition) 352226 -> ConstraintManager::IsFullyConstrained (tailcall) 353405 ConstraintManager::UnConstrain (definition) 353413 ConstraintManager::IsFullyConstrained (definition) 353528 ConstraintManager::ConstrainTo (definition) ``` No other call sites exist anywhere in the 1.4M-line corpus. The entire constrain mechanism is used EXCLUSIVELY by: - `SmartBox::HandleReceivedPosition` to arm/re-arm the leash on inbound position updates (3 branches: self-anchor for remote movers, server-anchor for player teleport, server- anchor for player non-teleport updates), and - `CPhysicsObj::teleport_hook` to disarm it on any teleport, and - `CMotionInterp::jump_is_allowed` to read it as a jump-blocking gate. This is a narrow, special-purpose "server position rubber-band leash," NOT a general physics constraint/joint system.