acdream/docs/research/2026-07-31-canonical-set-position.md

18 KiB
Raw Blame History

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

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:

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 (0x005153E50x005154FE). 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.