182 lines
8 KiB
Markdown
182 lines
8 KiB
Markdown
# 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.
|