# Runtime SetPosition collision-report ownership **Scope:** placement/streaming Slice 4B2 prerequisite A only. This closes the missing Runtime owner for retail collision tracking and the boolean returned by `CPhysicsObj::handle_all_collisions`. It does **not** activate any graphical or headless production SetPosition route. ## Named-retail oracle Primary sources: - `CPhysicsObj::report_object_collision_end` `0x00510A90` - `CPhysicsObj::report_environment_collision` `0x00512FC0` - `CPhysicsObj::report_object_collision` `0x00513060` - `CPhysicsObj::track_object_collision` `0x00513F10` - `CPhysicsObj::report_collision_start` `0x00513FD0` - `CPhysicsObj::report_collision_end` `0x00514620` - `CPhysicsObj::handle_all_collisions` `0x00514780` - `CPhysicsObj::SetPositionInternal(CTransition const*)` `0x00515330` - `CPhysicsObj::leave_world` `0x005155A0` - placement failure path in `CPhysicsObj::SetPositionInternal` `0x00515BD0` - `CPhysicsObj::CollisionRecord`, `EnvCollisionProfile`, `ObjCollisionProfile`, and `AtkCollisionProfile` in `docs/research/named-retail/acclient.h` The source text is `docs/research/named-retail/acclient_2013_pseudo_c.txt`. The addresses above are the behavioral authority; the older unnamed chunks remain fallback only. ### Environment reporting ```text report_environment_collision(meInContact): reported = false if !colliding_with_environment: if self.ReportCollisions && self.weenie != null: DoCollision(EnvCollisionProfile(self.velocity, meInContact)) reported = true colliding_with_environment = true if self.Missile: self.state &= ~(Missile | AlignPath | PathClipped) return reported ``` The latch is independent of callback eligibility. An object with no collision callback still latches its environment contact, and a repeated environment hit returns false. Retail has no environment-end callback. `leave_world` does not clear this latch; the next `handle_all_collisions` call re-arms it only after a non-environment frame. ### Object reporting and tracking ```text track_object_collision(other, meInContact): if other.Static: return report_environment_collision(meInContact) record = { touched_time = PhysicsTimer.curr_time, ethereal = other.Ethereal } existed = collision_table.clobber(other.id, record) if existed: return false return report_object_collision(other, meInContact) ``` The table insert/refresh precedes callbacks. Duplicate contacts refresh their time but never replay a start callback. DAT/static classification and physics state come from the exact shadow object which produced the collision; object-ID presence is not a valid substitute. `report_object_collision` first maps `ReportAsEnvironment` to the environment path. Otherwise: - the mover reports only when the other object is not `IgnoreCollisions` and the mover has `ReportCollisions` plus a weenie; - a mover which had Missile set before the source callback unconditionally masks its current `Missile | AlignPath | PathClipped` bits after striking a non-ignored object, even when the callback cleared Missile but re-added path bits; when pre-callback Missile was clear, callback-added Missile is retained; - the reciprocal report occurs only when the other has `ReportCollisions`, the mover is not `IgnoreCollisions`, and the other has a weenie; - the return is true when at least one callback is attempted. It is never a collision-presence boolean. ### Expiry and end reporting `report_collision_end(force)` removes records before dispatching callbacks. This ordering is required for safe reentrancy. ```text ordinary record: remove when age > 1.0, or force ethereal record: remove when age > 0.0, or force ``` Equality remains alive. A still-resolvable non-`ReportAsEnvironment` peer may receive reciprocal collision-end callbacks. When the peer no longer resolves, the owner can still receive its self-only end using the stored retail object ID. A later incarnation must never satisfy the old contact record. ### `handle_all_collisions` and SetPosition ordering ```text handle_all_collisions(info, previousContact, previousOnWalkable): reported = false for other in info.collidedObjects, in encounter order: reported |= track_object_collision(other, previousContact) report_collision_end(force = false) if environment latch is already set: latch = info.collided_with_environment else if info.collided_with_environment || (!previousOnWalkable && self.OnWalkable): reported |= report_environment_collision(previousContact) apply retail collision velocity/stationary response return reported ``` Successful `SetPositionInternal(CTransition const*)` commits the resolved cell/frame, Contact/WaterContact/OnWalkable state, and HitGround/LeaveGround edge before `handle_all_collisions`; it ignores the returned boolean and only then replaces/refloods shadows. Collision reports observe the old stationary- fall state; the new counter is installed before physical response, while the StationaryFall/Stop/Stuck transient bits are replaced after response and before shadow reflood. The placement failure path calls `handle_all_collisions(info, false, false)` and maps true to `SetPositionError::Collided` (`4`) and false to `NoValidPosition` (`2`). Consequently acdream must keep report/tracking separate from the physical response: failed placement runs both once, while successful Runtime commit runs reporting between the contact/ground commit and shadow reflood without double-applying velocity response. ## Runtime ownership contract The implementation is presentation-free and belongs to the per-session `RuntimePhysicsState` graph. Its invariants are: - owner and peer identities are exact `RuntimeEntityKey` values, not server GUID or local ID alone; - each tracked record retains the peer server GUID, touch time in the Runtime simulation-clock domain, and ethereal-at-touch bit; - collided IDs and authored/static ownership are admitted through the exact retained `ShadowObjectRegistry` registration which produced the collision; every dynamic Static/Ethereal/Ignore/ReportAsEnvironment decision then reads the current canonical `PhysicsBody.State`, never a stale shadow snapshot; - immutable reports preserve encounter order and dispatch through a retained, reentrancy-safe FIFO; - callback exceptions are isolated, while the retail report-result boolean is determined by callback eligibility and does not depend on subscribers; - every callback boundary revalidates the exact record/body/authority before any later canonical mutation; - force-end mutates the complete expired set before publishing ends; exact-key admission guards prevent callback reentry from recreating a leaving owner, and session teardown blocks the whole owner batch before its first callback; - one source lifetime token covers a complete precollected end batch, so a callback-accepted delete stops every later peer report even while teardown sidecars remain resolvable; - lifetime forget, session reset, and disposal cannot donate state to GUID reuse; - terminal ownership diagnostics include contact/report state and converge to zero; - graphical and no-window hosts borrow the same Runtime owner. No host owns a second collision table or report-result heuristic. The warmed steady-contact refresh path allocates zero managed bytes. Expired contact storage is allocated lazily only after the first actual expiry, and session-batch teardown is linear in owner count. ## Deliberately deferred The canonical SetPosition owner remains dormant in production. The following belong to later 4B2 commits and are not part of this checkpoint: - exact ordered Setup spheres, authored scale and step-height preparation; - the atomic shared local-controller body transaction; - presentation-only rebucketing and placement-prefix quiescence; - graphical/headless spawn, Position, portal, projectile, drop, pickup, parent, and delete route cutover. AP-1 and AD-1 therefore remain open, narrowed only by removal of the collision-report prerequisite.