Process animation completion at the retail process_hooks boundary, then run targeting, movement, PartArray completion, and PositionManager in the named UpdateObjectInternal order for local, remote, hidden, and position-less animated objects. Retire TS-42 with deterministic conformance coverage.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
6.7 KiB
R6 retail object-update order
Date: 2026-07-19 Retail oracle: Asheron's Call v11.4186 (September 2013 EoR)
Scope
This note pins the control-flow order needed by R6. It deliberately separates two kinds of animation-hook work:
- control hooks such as
AnimDoneHook, whose result changes the motion queues observed later in the same object update; and - pose-dependent presentation hooks such as particles and sounds, whose App-layer delivery may wait until the final root/part pose has been published without changing motion state.
That split is an acdream presentation boundary. It does not change retail's logical hook order.
Named-retail sources
CPhysicsObj::process_hooks0x00511550CPhysicsObj::UpdatePositionInternal0x00512C30CPhysicsObj::UpdateObjectInternal0x005156B0CPhysicsObj::update_object0x00515D10CPartArray::AnimationDone0x00517D30CPartArray::HandleMovement0x00517D60AnimDoneHook::Execute0x00526C20CPhysicsObj::Hook_AnimDone0x0050FDA0
Primary text:
docs/research/named-retail/acclient_2013_pseudo_c.txt.
Addresses and names are also pinned in
docs/research/named-retail/symbols.json.
Corrected top-level order
An older roadmap diagram put process_hooks after the manager tail. The named
retail function body disproves that. UpdatePositionInternal processes hooks
before it returns to UpdateObjectInternal; the transition/commit and every
manager therefore observe completed animation state in the same object tick.
CPhysicsObj::update_object
-> quantum gate/subdivision
-> CPhysicsObj::UpdateObjectInternal
-> CPhysicsObj::UpdatePositionInternal
-> CPartArray::Update
-> PositionManager::adjust_offset
-> Frame::combine
-> CPhysicsObj::UpdatePhysicsInternal
-> CPhysicsObj::process_hooks <-- completion drain
-> transition / SetPositionInternal
-> DetectionManager::CheckDetection
-> TargetManager::HandleTargetting
-> MovementManager::UseTime
-> CPartArray::HandleMovement
-> PositionManager::UseTime
-> ParticleManager::UpdateParticles
-> ScriptManager::UpdateScripts
Pseudocode
CPhysicsObj::update_object
if parent != null or cell == null or state contains Frozen:
return
refresh active/view-distance state
elapsed = current_physics_time - last_update_time
if elapsed > HugeQuantum:
last_update_time = current_physics_time
return
while elapsed > MaxQuantum:
last_update_time += MaxQuantum
UpdateObjectInternal(MaxQuantum)
elapsed -= MaxQuantum
if elapsed > MinQuantum:
last_update_time = current_physics_time
UpdateObjectInternal(elapsed)
The comparisons are strict (>), not >=. A sub-minimum remainder stays in
the clock for a later call.
CPhysicsObj::UpdatePositionInternal(dt, candidate)
delta = identity Frame
if state does not contain Hidden:
if part_array != null:
part_array.Update(dt, delta)
if transient state contains OnWalkable:
delta.origin *= object_scale
else:
delta.origin = zero
if position_manager != null:
position_manager.adjust_offset(delta, dt)
candidate = combine(current_frame, delta)
if state does not contain Hidden:
UpdatePhysicsInternal(dt, candidate)
process_hooks()
Hidden suppresses PartArray time advance and physics integration. It does not
suppress PositionManager::adjust_offset, hook processing, or the later
manager tail.
CPhysicsObj::process_hooks
execute each queued physics hook in FIFO order
clear physics-hook queue
execute each queued animation hook in FIFO order
clear animation-hook queue
For an AnimDoneHook:
AnimDoneHook::Execute(owner)
-> owner.Hook_AnimDone()
-> owner.part_array.AnimationDone(success = true)
-> MotionTableManager.AnimationDone(true)
-> possibly MotionDone on the matching pending motion
Consequently, targeting and movement managers in the same object tick must see the post-completion queue state.
CPhysicsObj::UpdateObjectInternal(dt) manager tail
candidate = current frame
UpdatePositionInternal(dt, candidate)
if candidate moved:
transition/sweep from current frame to candidate
commit accepted position and collision response
if detection_manager != null:
detection_manager.CheckDetection()
if target_manager != null:
target_manager.HandleTargetting()
if movement_manager != null:
movement_manager.UseTime()
if part_array != null:
part_array.HandleMovement()
// tailcalls MotionTableManager.UseTime /
// CheckForCompletedMotions
if position_manager != null:
position_manager.UseTime()
if particle_manager != null:
particle_manager.UpdateParticles(dt)
if script_manager != null:
script_manager.UpdateScripts(dt)
The tail is reached for both visible and Hidden objects when the object quantum runs. A missing manager is simply skipped. No manager is reordered around another manager based on local/remote/player/NPC classification.
acdream conformance boundary
The first R6 cutover uses one explicit coordinator for the ordered manager
tail. Local and remote owners provide callbacks for the managers they possess;
unsupported DetectionManager is represented by an absent callback rather
than an invented behavior.
Animation-hook capture performs AnimationDone control effects immediately,
at the process_hooks boundary. Pose-dependent routing remains deferred until
the current frame's root/part poses are published. Deferred routing must never
invoke AnimationDone a second time and must never own
MotionTableManager.UseTime; that belongs only to the PartArray manager-tail
slot.
Conformance cases
- Animation completion is visible to TargetManager and MovementManager in the same object tick.
- Target runs before Movement; Movement runs before PartArray; PartArray runs before PositionManager.
- A missing manager does not shift or duplicate another callback.
- Hidden objects skip sequence advance but retain the same manager tail.
- A visual hook sees a pose published after capture, while an
AnimDoneHookchanges motion state at capture time. - Deferred visual-hook drain never completes an animation twice and never performs a zero-tick motion sweep.
- A sub-MinQuantum frame performs no object-tail call; accumulated time is consumed by a later quantum.
Cross-reference notes
ACE's physics/motion ports remain interpretation aids for manager ownership
and state naming; the retail binary above is authoritative for order. The
existing RemoteChaseEndToEndHarnessTests hand-codes acdream's pre-R6 order
and is therefore a migration target, not an oracle.