8 KiB
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_end0x00510A90CPhysicsObj::report_environment_collision0x00512FC0CPhysicsObj::report_object_collision0x00513060CPhysicsObj::track_object_collision0x00513F10CPhysicsObj::report_collision_start0x00513FD0CPhysicsObj::report_collision_end0x00514620CPhysicsObj::handle_all_collisions0x00514780CPhysicsObj::SetPositionInternal(CTransition const*)0x00515330CPhysicsObj::leave_world0x005155A0- placement failure path in
CPhysicsObj::SetPositionInternal0x00515BD0 CPhysicsObj::CollisionRecord,EnvCollisionProfile,ObjCollisionProfile, andAtkCollisionProfileindocs/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
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
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
IgnoreCollisionsand the mover hasReportCollisionsplus a weenie; - a mover which had Missile set before the source callback unconditionally
masks its current
Missile | AlignPath | PathClippedbits 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 notIgnoreCollisions, 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.
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
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
RuntimeEntityKeyvalues, 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
ShadowObjectRegistryregistration which produced the collision; every dynamic Static/Ethereal/Ignore/ReportAsEnvironment decision then reads the current canonicalPhysicsBody.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.