142 lines
6.6 KiB
Markdown
142 lines
6.6 KiB
Markdown
# Canonical retail `SetPosition` — placement/streaming Slice 4A
|
|
|
|
## Scope
|
|
|
|
This note pins the pure physics half of the placement/streaming closeout.
|
|
Slice 4A lands the retail placement transaction as a separately testable Core
|
|
mechanism. It deliberately does **not** replace the production snap-only
|
|
resolver yet: Runtime lost-cell ownership and the complete inbound-route
|
|
cutover remain Slice 4B. AP-1 and AD-1 therefore remain active until that
|
|
cutover is complete.
|
|
|
|
Named-retail oracle, Sept 2013 EoR:
|
|
|
|
- `CPhysicsObj::SetPosition` `0x005160C0`
|
|
- `CPhysicsObj::SetPositionInternal` `0x00515BD0`
|
|
- `CPhysicsObj::AdjustPosition` `0x00511D80`
|
|
- `CPhysicsObj::CheckPositionInternal` `0x00511E90`
|
|
- `CTransition::find_valid_position` `0x0050C310`
|
|
- `CTransition::find_placement_position` `0x0050C170`
|
|
- `CTransition::find_placement_pos` `0x0050BA50`
|
|
- `CTransition::validate_placement_transition` `0x0050ADC0`
|
|
- `CTransition::validate_placement` `0x0050B210`
|
|
- `CPhysicsObj::ForceIntoCell` `0x00515660`
|
|
- `CPhysicsObj::handle_all_collisions` `0x00514780`
|
|
|
|
## Retail transaction
|
|
|
|
```text
|
|
SetPosition(request):
|
|
transition = makeTransition() // GENERAL_FAILURE if none
|
|
init_object(transition, object)
|
|
|
|
if the PartArray has no spheres:
|
|
init_sphere(1, dummy center=(0,0,0.1), radius=0.1, scale=1)
|
|
else:
|
|
init_sphere(first min(count,2) authored spheres, exact object scale)
|
|
|
|
if flags & RANDOM_SCATTER (0x200):
|
|
return scatter only
|
|
|
|
result = SetPositionInternal(request)
|
|
if result != OK and flags & SCATTER (0x100):
|
|
return scatter
|
|
return result
|
|
|
|
SetPositionInternal(request):
|
|
AdjustPosition(request frame, first sphere, noCreate=(flags & 0x20))
|
|
if no resident cell:
|
|
store the adjusted authoritative frame and enter lost-cell lifetime
|
|
return OK
|
|
|
|
if the live weenie is Hook, Storage, or Corpse:
|
|
return ForceIntoCell(resident cell, frame)
|
|
|
|
set do_not_load_cells from flag 0x20
|
|
if !CheckPositionInternal(...):
|
|
handled = handle_all_collisions(...)
|
|
return handled ? COLLIDED : NO_VALID_POSITION
|
|
if transition.curr_cell == null:
|
|
return NO_CELL
|
|
commit the complete transition
|
|
return OK
|
|
```
|
|
|
|
`AdjustPosition` branches on the claimed cell shape. A direct outdoor claim
|
|
runs pure `LandDefs::adjust_to_outside` normalization before visible-cell
|
|
lookup. An indoor claim first resolves the visible cell and child; only a
|
|
resident indoor cell marked `seen_outside` falls back to outdoor
|
|
normalization. An absent indoor cell (including the `0xFFFF` sentinel) remains
|
|
the exact claimed cell/frame. A map-edge outdoor normalization failure stores
|
|
cell zero with the otherwise unchanged frame. The old `max(terrainZ, z)` lift
|
|
and nearest-in-Z scan do not occur in this canonical mechanism.
|
|
|
|
`CheckPositionInternal` calls the complete placement transition. Without the
|
|
slide flag, retail accepts the result only when signed
|
|
`resolvedX-requestedX <= 0.0500000007`, the same signed Y condition holds, and
|
|
the cell is unchanged. Z is not part of that predicate. The resolved origin is
|
|
accepted while the requested orientation remains intact.
|
|
|
|
## Two validators, not one
|
|
|
|
The similarly named retail helpers have different contracts and stay separate
|
|
in the port:
|
|
|
|
- `validate_placement_transition` is the inner `find_placement_pos` validator.
|
|
Any non-OK state from `COLLIDED` through `SLID`, when sliding is permitted,
|
|
resets `COLLISIONINFO`; it never retries placement.
|
|
- `validate_placement` is the outer initial/final validator. Only `ADJUSTED`
|
|
or `SLID`, and only while its retry argument is true, performs one
|
|
`placement_insert`; `COLLIDED` neither resets nor retries.
|
|
|
|
Step-down is disabled only for missiles. For fewer than two spheres retail
|
|
first clamps the requested height to half the radius when the sphere diameter
|
|
is less than or equal to that height. It then performs one full probe when the
|
|
diameter is greater than the resulting height, otherwise two half probes.
|
|
Equality belongs to the two-half-probe branch.
|
|
|
|
## Modern seam
|
|
|
|
`PhysicsEngine.SetPosition` returns one immutable
|
|
`PhysicsSetPositionResult`. `SetPositionError` retains the header values
|
|
(`OK=0`, `GENERAL=1`, `NO_VALID=2`, `NO_CELL=3`, `COLLIDED=4`,
|
|
`INVALID_ARGS=0x100`) while `PhysicsResidenceDisposition` separately reports
|
|
`Committed`, `DeferredCell`, or `Unchanged`. Missing content is therefore
|
|
successful-but-deferred, never misreported as a placement failure.
|
|
|
|
The result carries the complete commit packet: root/cell-local frame,
|
|
contact/walkable/water state, sliding and collision normals, stationary-fall
|
|
counter, a complete immutable `COLLISIONINFO` snapshot for the real
|
|
`handle_all_collisions` callback (including contact/last-contact, sliding,
|
|
collision normal, stationary-fall, environment, adjustment, and object
|
|
fields), the callback result, and an explicit shadow action. A PhysicsBSP or changed-cell force commit
|
|
requests canonical shadow recalculation; a non-BSP transition replaces its
|
|
shadows only when the transition produced a nonempty cell array, otherwise it
|
|
preserves the prior list. Unchanged force placement changes only the frame.
|
|
|
|
Core never creates cells synchronously, so retail flag `0x20`
|
|
(`DoNotCreateCells`) has no differential loader branch inside this pure
|
|
mechanism. Both flag states can only observe already-published immutable cell
|
|
content and otherwise return `DeferredCell`. Carrying the flag into exact-cell,
|
|
generation-scoped async admission is part of the still-open Slice 4B
|
|
adaptation tracked with AD-2; this slice does not claim a dead SpherePath field
|
|
as exact behavior.
|
|
|
|
The existing public `Resolve` compatibility entry remains entirely unchanged
|
|
for production movement and zero-delta callers. Its result cannot represent a
|
|
successful-but-deferred residence, so hiding `DeferredCell` inside
|
|
`ResolveResult.Ok` would corrupt the contract. Slice 4B will route every
|
|
authoritative placement family through `SetPosition`, atomically install its
|
|
packet, and own exact lost-cell wakeup/commit.
|
|
|
|
## Automated oracle
|
|
|
|
`PhysicsSetPositionTests` pins error values, absent/invalid outdoor and indoor
|
|
claims, cross-landblock frame normalization, map-edge failure and the `0xFFFF`
|
|
sentinel, dummy/authored sphere setup, nonpositive scale, Ethereal seeding,
|
|
explicit-only PathClipped, missile step-down, exact equality schedules, both validators,
|
|
the late compass sample's float bits, signed no-slide behavior, actual
|
|
collision-handler mapping, force-class policy, explicit shadow actions, null
|
|
current-cell wakeup, ten-record scratch exhaustion, exact scatter ordering,
|
|
failed-probe scratch lifetime, and deferred-scatter stop. The legacy public
|
|
Resolve fixture remains unchanged until Slice 4B.
|