fix(motion): restore retail object manager order
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>
This commit is contained in:
parent
0f996db747
commit
c5ab99081c
17 changed files with 493 additions and 98 deletions
|
|
@ -1126,7 +1126,7 @@ public sealed class PlayerMovementController
|
|||
/// live. A composed offset still commits through CTransition so cell
|
||||
/// membership and contact state cannot become stale behind the hidden mesh.
|
||||
/// </summary>
|
||||
public MovementResult TickHidden(float dt)
|
||||
public MovementResult TickHidden(float dt, Action? handleTargeting = null)
|
||||
{
|
||||
_simTimeSeconds += dt;
|
||||
_physicsAccum = 0f;
|
||||
|
|
@ -1180,8 +1180,14 @@ public sealed class PlayerMovementController
|
|||
UpdateCellId(resolved.CellId, "hidden-position-manager");
|
||||
}
|
||||
|
||||
Movement.UseTime();
|
||||
PositionManager?.UseTime();
|
||||
RetailObjectManagerTail.Run(
|
||||
checkDetection: null,
|
||||
handleTargeting,
|
||||
movementUseTime: Movement.UseTime,
|
||||
partArrayHandleMovement: _motion.CheckForCompletedMotions,
|
||||
positionUseTime: PositionManager is { } positionManager
|
||||
? positionManager.UseTime
|
||||
: null);
|
||||
_prevPhysicsPos = _body.Position;
|
||||
_currPhysicsPos = _body.Position;
|
||||
_wasAirborneLastFrame = !_body.OnWalkable;
|
||||
|
|
@ -1201,42 +1207,13 @@ public sealed class PlayerMovementController
|
|||
CurrentStyle: _motion.RawState.CurrentStyle);
|
||||
}
|
||||
|
||||
public MovementResult Update(float dt, MovementInput input)
|
||||
public MovementResult Update(
|
||||
float dt,
|
||||
MovementInput input,
|
||||
Action? handleTargeting = null)
|
||||
{
|
||||
_simTimeSeconds += dt;
|
||||
|
||||
// R4-V5: tick the verbatim MoveToManager at the slot the deleted
|
||||
// DriveServerAutoWalk occupied — after inbound wire routing, before
|
||||
// the input-driven motion sections. UseTime runs the retail per-tick
|
||||
// moveto drivers (HandleMoveToPosition aux-turn steering + arrival +
|
||||
// fail-distance / HandleTurnToHeading); the motions it dispatches
|
||||
// land in InterpretedState through _DoMotion → DoInterpretedMotion,
|
||||
// and sections 1/2 below turn that state into Yaw rotation + body
|
||||
// velocity — the same per-frame apply user input gets. No
|
||||
// synthesized input exists, so the outbound-packet pipeline sees no
|
||||
// user motion and never builds a MoveToState mid-moveto (the #75
|
||||
// invariant, now by construction). Provisional tick placement until
|
||||
// R6 ports retail's UpdateObjectInternal ordering (r4-port-plan.md
|
||||
// §3 placement decision). R5-V5: the facade relay
|
||||
// (MovementManager::UseTime 0x005242f0 — moveto side only).
|
||||
Movement.UseTime();
|
||||
|
||||
// R4-V5 wedge fix (2026-07-03 live door bug), retail's per-tick
|
||||
// completion slot: CPartArray::HandleMovement — a tailcall chain to
|
||||
// MotionTableManager::CheckForCompletedMotions (0x00517d60 →
|
||||
// 0x0051bfd0) — runs every tick right AFTER MovementManager::UseTime
|
||||
// in UpdateObjectInternal (@005159a4). It flushes pending_animations
|
||||
// entries whose animations already ran out so their AnimationDone →
|
||||
// MotionDone pops land each tick even when no op fired that frame.
|
||||
// (apply_current_movement is NOT part of the per-tick order — its
|
||||
// retail callers are all event-driven: hold-key toggles, HitGround/
|
||||
// LeaveGround, ReportExhaustion.) Full tick ordering remains R6
|
||||
// scope; the companion fix is StopCompletely's previously-missing
|
||||
// StopCompletely_Internal animation dispatch (see
|
||||
// MotionInterpreter.StopCompletely), whose completable entry this
|
||||
// slot drains.
|
||||
_motion.CheckForCompletedMotions?.Invoke();
|
||||
|
||||
// Portal-space guard: while teleporting, no input is processed and
|
||||
// no physics is resolved. Return a zero-movement result so the caller
|
||||
// can detect the frozen state (MotionStateChanged = false, no commands).
|
||||
|
|
@ -1710,13 +1687,6 @@ public sealed class PlayerMovementController
|
|||
{
|
||||
_prevPhysicsPos = oldTickEndPos;
|
||||
_currPhysicsPos = _body.Position;
|
||||
|
||||
// R5-V3 (#171): retail UpdateObjectInternal TAIL — PositionManager::
|
||||
// UseTime (@0x005159b3) runs AFTER the quantum's integration
|
||||
// (whose head hosts adjust_offset) and only on frames where a
|
||||
// physics quantum executed (retail's update_object MinQuantum
|
||||
// gate skips the whole UpdateObjectInternal).
|
||||
PositionManager?.UseTime();
|
||||
}
|
||||
|
||||
// SetPositionInternal contact determination (pc:283468-510). acdream's resolver
|
||||
|
|
@ -1778,6 +1748,22 @@ public sealed class PlayerMovementController
|
|||
_wasAirborneLastFrame = !_body.OnWalkable;
|
||||
UpdateCellId(resolveResult.CellId, "resolver");
|
||||
|
||||
// Named retail CPhysicsObj::UpdateObjectInternal (0x005156B0):
|
||||
// process_hooks has already completed inside UpdatePositionInternal,
|
||||
// then the transition commits, and only then does the ordered manager
|
||||
// tail run. DetectionManager is not ported yet, so its slot is absent.
|
||||
// PositionManager's clock stays tied to a consumed physics quantum;
|
||||
// the remaining per-render versus per-quantum unification belongs to
|
||||
// the R6 object scheduler rather than this ordering cutover.
|
||||
RetailObjectManagerTail.Run(
|
||||
checkDetection: null,
|
||||
handleTargeting,
|
||||
movementUseTime: Movement.UseTime,
|
||||
partArrayHandleMovement: _motion.CheckForCompletedMotions,
|
||||
positionUseTime: physicsTickRan && PositionManager is { } positionManager
|
||||
? positionManager.UseTime
|
||||
: null);
|
||||
|
||||
// ── 6. Determine outbound motion commands ─────────────────────────────
|
||||
uint? outForwardCmd = null;
|
||||
float? outForwardSpeed = null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue