333 lines
18 KiB
Markdown
333 lines
18 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.
|
||
|
||
## Slice 4B1 — Runtime residence owner
|
||
|
||
Slice 4B1 adds the presentation-independent half of the cutover without
|
||
changing a production graphical route yet. `RuntimeSetPositionState` accepts
|
||
an exact entity/position token before graphical DAT preparation, consumes the
|
||
immutable Core result, and commits body, contact, full cell, shadows, object
|
||
clock, and Runtime spatial worksets before publishing one ordered placement
|
||
delta. The delta carries the exact `RuntimeEntityKey`, session lifetime,
|
||
position/spatial/placement versions, adjusted cell, collision generation, and
|
||
optional portal-authority shape. A throwing or unavailable host does not roll
|
||
simulation back: Runtime republishes the same projection token until the
|
||
exact FIFO head is acknowledged. A newer operation changes an already-
|
||
published token to `Discard` and increments its projection revision, so an
|
||
acknowledgement of the previously observed Place/Withdraw cannot consume an
|
||
unseen Discard. It is never silently forgotten. An unacknowledged lost-cell
|
||
Withdraw transfers intact to a replacing accepted operation and remains the
|
||
FIFO head before that replacement may publish Place.
|
||
|
||
The successful missing-cell path owns retail's residence shape:
|
||
|
||
```text
|
||
SetPosition -> OK + DeferredCell
|
||
retain adjusted Position and the same PhysicsBody/components
|
||
clear only Active and suspend the object clock
|
||
withdraw Runtime spatial worksets and shadow rows
|
||
retain shadow registration and exact authored mover request
|
||
append parentless root to (exact cell, collision generation)
|
||
arm independent exact-key 25 s deadlines for root + direct children
|
||
publish Withdraw
|
||
|
||
exact cell generation resident + Withdraw acknowledged
|
||
re-run SetPosition with retained authored spheres and CurrentCellId=null
|
||
atomically install complete result
|
||
publish Place
|
||
```
|
||
|
||
Lost-cell membership buckets use retail-shaped append plus swap-remove.
|
||
Destruction deadlines use an exact-key hash plus a bounded indexed min-heap,
|
||
the allocation-bounded modern equivalent of retail's hash +
|
||
`PQueueArray<double>` priority owner. Rearm/removal updates the exact heap node;
|
||
there are no stale tombstones. Only committed, current direct children from
|
||
the parent-incarnation ordered CHILDLIST participate; unresolved or future
|
||
relations cannot inherit a deadline. Parent, pickup, delete, GUID
|
||
replacement, newer Position, reset, and disposal cancel the exact incarnation
|
||
and use leave-world semantics rather than a wakeable lost entry. The dormant
|
||
collision-retirement entry parks non-static parentless indoor roots and, for
|
||
complete withdrawal, affected outdoor roots. It performs a complete preflight
|
||
and rejects overlap with any active accepted/host-ack-pending placement before
|
||
mutating one resident. It then installs every affected canonical lost
|
||
residence and operation before publishing the first synchronous Withdraw, so
|
||
an observer re-entering for a later root inherits that root's exact pending
|
||
Withdraw instead of having its newly accepted placement cancelled by the
|
||
retirement loop. 4B2 must quiesce that placement prefix before invoking the
|
||
entry in the same transaction that installs host acknowledgements.
|
||
|
||
Runtime retains the last accepted prepared mover request, including exact
|
||
off-center/two-sphere payloads, scale, flags, and step values. A cold resident
|
||
with no prepared request still withdraws atomically but remains explicitly in
|
||
`AwaitingPreparation`; it cannot wake through an invented empty-sphere shape.
|
||
Preparation is cached only after Core accepts it as Committed or DeferredCell.
|
||
A rejected/malformed preparation keeps the same accepted token retryable and
|
||
cannot replace the last validated mover used by later collision retirement.
|
||
Runtime's host boundary rejects only non-finite consumed frame/shape values;
|
||
retail-valid oddities such as nonpositive authored scale remain untouched.
|
||
Likewise, a non-deferred wake failure retains the withdrawn body, independent
|
||
25-second lifetime, and exact operation: invalid arguments return to
|
||
AwaitingPreparation and re-index for the next exact generation, while other
|
||
world-placement failures also re-index. The last successful DeferredCell
|
||
result and adjusted frame remain canonical across the failed attempt. No
|
||
failed wake can leave a live entity withdrawn without a Runtime owner. The
|
||
graphical/no-window cutover in 4B2 supplies that exact preparation token.
|
||
|
||
Retail `CPhysicsObj::SetPositionInternal` (`0x00515BD0`) calls
|
||
`prepare_to_enter_world` (`0x00511FA0`) only when `this->cell == 0`.
|
||
Consequently the physics `update_time` (`PhysicsBody.LastUpdateTime`) and
|
||
active bit are reset only on the cellless-to-world edge. Ordinary same-cell or
|
||
cross-cell in-world SetPosition preserves the already-consumed physics clock;
|
||
entering the lost-cell residence also preserves it until the eventual
|
||
cellless wake commit. Runtime pins both sides and does not inherit the older
|
||
graphical teleport helper's unconditional timer reset.
|
||
|
||
The wake timestamp is in the Runtime simulation-time domain, never Unix/UTC:
|
||
`GameRuntime` binds its instance `GameRuntimeClock` through the entity/physics
|
||
owner, and RetryDeferred samples `SimulationTimeSeconds`. Standalone Runtime
|
||
fixtures without a bound game clock retain the accepted command time. This is
|
||
a Runtime dependency only; no App delegate enters the owner.
|
||
|
||
The canonical commit also installs every SetPosition-derived body invariant
|
||
before host publication: Contact/OnWalkable/WaterContact, the current contact
|
||
plane and slope `GroundNormal`, Sliding plus its normal, and the complete
|
||
StationaryFall/Stop/Stuck encoding. Named retail
|
||
`CPhysicsObj::SetPositionInternal(CTransition const*)` (`0x00515330`) copies
|
||
only the transition's current contact plane/water flag, walkability, sliding
|
||
normal/valid flag, and collision state (`0x005153E5–0x005154FE`). It does not
|
||
publish `last_known_contact_plane` or the SpherePath walkable polygon on this
|
||
path, so those ordinary-update-only fields are intentionally absent from the
|
||
immutable SetPosition result and remain unchanged. A zero expected velocity
|
||
version in host preparation preserves the nonzero version captured when the
|
||
operation was accepted; an intervening Vector/Movement therefore suppresses
|
||
only the stale collision-velocity response. A bodyless cancellation terminates
|
||
without fabricating a PhysicsBody or a host Withdraw projection. The two time
|
||
domains remain explicit: `PhysicsBody.LastUpdateTime` consumes the instance
|
||
simulation clock, while `IRuntimeRemotePlacement.LastServerPositionTime`
|
||
remains Unix-UTC receipt time because the remote stale-velocity owner ages it
|
||
against `RuntimePhysicsState.UtcNowSeconds`. A deferred wake therefore cannot
|
||
make fresh authoritative remote velocity appear years old. Runtime preparation
|
||
also applies the existing retail `PositionFrameValidation` before Core or
|
||
prepared-mover caching, and caps synchronous Scatter/RandomScatter work at 64
|
||
attempts; this keeps valid authored retail request shapes while rejecting a
|
||
hostile `uint.MaxValue` loop at the authority boundary.
|
||
|
||
One authority boundary intentionally remains open for 4B2:
|
||
|
||
- `RuntimePortalPlacementAuthority` validates immutable token shape only;
|
||
4B2 must bind it to active `RuntimeWorldTransitState` generation, teleport
|
||
sequence, destination, and host acknowledgement before reveal.
|
||
|
||
The former collision-report boundary is closed by
|
||
`RuntimeCollisionReportingState`. Runtime now owns retail's exact-key object
|
||
contact table, environment latch, strict ordinary/ethereal expiry, force-end,
|
||
static and `ReportAsEnvironment` routing, reciprocal callback eligibility,
|
||
missile-state clearing, ordered reentrant dispatch, and the report-result
|
||
boolean which distinguishes placement `Collided` from `NoValidPosition`.
|
||
Successful SetPosition commits reporting after Contact/OnWalkable and ground
|
||
callbacks but before its single physical response and shadow reflood. See
|
||
`docs/research/2026-07-31-runtime-set-position-collision-reporting.md`.
|
||
|
||
### Slice 4B2 checkpoint 1 — public dormant host seam
|
||
|
||
The first 4B2 checkpoint exposes the dormant receipt owner through
|
||
`RuntimePlacementProjectionChannel`. Graphical and no-window hosts can observe
|
||
the one ordered placement stream, retry the exact immutable pending receipts,
|
||
peek the FIFO head, measure pending debt, and acknowledge only the exact head.
|
||
Mutation and retry calls require the current `RuntimeGenerationToken`; a stale
|
||
generation, stale revision, reordered token, duplicate acknowledgement, or
|
||
reused GUID cannot consume current placement debt. The channel delegates to
|
||
`RuntimeSetPositionState` and `RuntimeEntityObjectEventStream`; it owns no
|
||
second queue, mirror, or rollback path.
|
||
|
||
Shared local-controller body adoption is deliberately deferred. A reviewed
|
||
prototype that prepared directly on the canonical body was rejected: a
|
||
snapshot/rollback lease cannot safely coexist with reentrant SetPosition,
|
||
remote/projectile binding, deletion/GUID reuse, owner replacement, object-clock
|
||
epoch changes, or disposal. Correct adoption requires either an exclusive
|
||
Runtime transaction integrated with every canonical writer, or off-canonical
|
||
preparation followed by one validated atomic body/controller publication.
|
||
Either choice belongs to the all-route ownership cutover, not this narrow
|
||
dormant-seam checkpoint.
|
||
|
||
This remains a deliberately non-activating checkpoint. Production spawn,
|
||
Position, projectile, drop/pickup/parent, and portal routes do not submit to
|
||
the dormant SetPosition owner yet. The cutover remains blocked on exact
|
||
ordered Setup spheres/scale/step heights/flags/cell-local preparation,
|
||
presentation-only rebucketing, and placement-prefix quiescence before
|
||
collision retirement. AP-1 and AD-1 remain open until those prerequisites and
|
||
every production route land together.
|
||
|
||
AD-2 remains the explicit async adaptation: collision readiness can publish in
|
||
a different frame from retail's blocking load. A failed wake is safely re-
|
||
indexed to the next exact generation instead of inheriting retail's
|
||
synchronous assumption. When older unbound survivors meet newer entities
|
||
already indexed into that future generation, Runtime merges them into one
|
||
bucket with the older survivor order first and retains one bucket-order entry.
|
||
AP-1 and AD-1 remain open until 4B2 removes the legacy graphical/headless
|
||
placement paths.
|
||
|
||
`RuntimeSetPositionStateTests` pins accepted-before-preparation ownership,
|
||
portal-shape rejection, canonical-before-projection ordering, retry and
|
||
reentrant discard, cross-landblock adjusted-cell park/wake, same-body
|
||
identity, retained contact/water/sliding/velocity, exact generation gating,
|
||
authored two-sphere retention, cold preparation, bounded priority-deadline
|
||
rearm/cancel, zero-allocation empty ticks, independent ordered direct-child
|
||
deadlines, actual collision-admission supersession/invalidation, missing exact
|
||
indoor-cell generation rebind/wake, collision demotion/withdrawal preflight,
|
||
failed-wake retry, malformed-preparation retry without cache poisoning,
|
||
simulation-clock-domain wake, velocity-version preservation, derived body-bit
|
||
writeback, immediate remote-velocity survival across the simulation/UTC clock
|
||
boundary, invalid cell/frame/quaternion and extreme-scatter rejection before
|
||
Core/caching, bodyless cancellation, newer Position/pickup/parent/delete, GUID
|
||
reuse, reset, and complete index/node terminal convergence. Existing zero-
|
||
allocation collision-generation gates remain unchanged on the no-deferred
|
||
fast path.
|
||
|
||
The warmed immediate commit/ack route currently measures exactly **1,880
|
||
managed bytes per operation** in the Release Runtime test host (1,000
|
||
iterations after 64 warmups); the regression gate caps it at 2,048 bytes.
|
||
This dormant-path result is an explicit 4B2 activation blocker rather than a
|
||
claim of allocation-free production readiness: 4B2 must either pool/remove
|
||
the operation and projection envelopes or record an approved measured budget
|
||
before routing frame-frequency placement through this owner.
|