MP: replicant motion chop ROOT-CAUSED + FIXED -- stale peer-mirror spammed re-base

records; also the 2007 call-counter clock stub + two crash fixes

User-reported: peer mechs turn/move choppy ("missing frames"). Measured per-frame
(BT_REPL_HDG): 13% of frames the replicant's heading STALLED, 11% it JUMPED 2-6x.

THREE layered defects found:

1) THE CLOCK STUB (engine substrate): TIMESTUB.cpp's GetRTC/GetHiRes were 2007
   `return time++` call-counters -- the "clock" advanced per CALL, not per ms --
   and TIMESTUB won the /FORCE duplicate-symbol race over the REAL QPC clock in
   L4TIME.cpp (LNK4006). Every Now()-domain consumer (dead-reckon above all) ran
   on call-count pseudo-time. Removed TIMESTUB from the build; L4TIME covers every
   symbol. [The real clock alone did NOT cure the chop -- but it was objectively
   broken and un-gated the two latent bugs below.]

2) TWO CRASH FIXES the real clock exposed:
   - legAnimationState@0x3b0 never ctor-initialized (the task-#56 0xCDCDCDCD
     family): the type-3 writer re-dispatches SetBodyAnimation(legAnimationState)
     on the WRITER; a record emitted before the leg SM's first tick passed raw
     0xCDCDCDCD as a clip index -> AV (cdb-pinned, mech2.cpp:233). Init 0.
   - Replicants were SERIALIZING update records: the tail WriteSimulationUpdate ran
     for every instance, and the port's replicant leg-SM accommodation (task #50)
     calls ForceUpdate -> replicants emitted derived/uninitialized state into the
     stream. Master-gated; replicant marks discarded (master-authoritative).

3) THE CHOP ITSELF: the master's resync send-gate compares localOrigin vs
   projectedOrigin -- the PEER-ESTIMATE mirror -- but the port master never
   maintained projectedOrigin (the bring-up drive replaced Mover::Perform; the
   engine only updates the projection inside replicant-only DeadReckon). Stale
   mirror -> |local-projected| > deadband EVERY frame -> a type-4 re-base record
   EVERY frame -> the replicant hard-copied the master's deadband-quantized
   heading each frame (the engine lerp never engaged; nextUpdate always behind
   till) -> stall/snap beat = the chop. FIX: advance the mirror each frame by the
   last-SENT angular velocity (what the peer is extrapolating) and re-base it in
   the type-4 writer. Records now flow only on TRUE drift (~1 per 5 frames in a
   steady spin), the replicant extrapolates smoothly between them, and the lerp
   horizon finally engages. Measured: STALLS 13%->1%, JUMPS 11%->1% (residue: tiny
   sub-degree backward corrections on record arrival -- inherent dead-reckon
   overshoot the lerp absorbs).

Diag probes: BT_REPL_HDG (per-frame replicant heading + dead-reckon internals),
BT_REPL_TRN (replicant turn/leg state). scratchpad/clockcrash_bp.txt (cdb).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-14 10:08:47 -05:00
co-authored by Claude Fable 5
parent e6ad29d648
commit a3b735d5da
3 changed files with 66 additions and 3 deletions
+16
View File
@@ -870,6 +870,13 @@ Mech::Mech(
// dispatch (task #56) gates on it every frame; garbage
// nonzero silently disabled the writers until death anims ran
legResetLatch = 0; // @0x654 -- same latch family, same fill hazard
legAnimationState = 0; // @0x3b0 -- same hazard (found 2026-07-14, the real-clock
// crash): the type-3 record writer re-dispatches
// SetBodyAnimation(legAnimationState) on itself; a record
// emitted before the leg SM's first tick passed the raw
// 0xCDCDCDCD as a clip index -> AV in SetBodyAnimation.
// (The stub call-counter clock had merely re-ordered record
// emission so the window was never hit.) Standing = 0.
attrPad = 0.0f; // shared read pad for un-populated gauge attributes
linearSpeed = 0.0f; // live forward ground speed (LinearSpeed gauge); set per frame
radarRange = 1000.0f; // radar display scale (SetTargetRange is stubbed;
@@ -2091,6 +2098,15 @@ void
}
updateOrigin.angularPosition = localOrigin.angularPosition; // FUN_00409968(0x138 <- 0x10c)
updateVelocity.angularMotion = localVelocity.angularMotion; // FUN_00408440(0x2d4 <- 0x1d0)
// RE-BASE the master's peer-estimate mirror (2026-07-14, replicant-chop
// fix): projectedOrigin/projectedVelocity model what the PEER will now
// extrapolate from this record. The send gate compares local vs projected
// (the peer's drift); without the re-base the mirror went stale, the gate
// fired EVERY frame, and the per-frame records re-based the replicant into
// the stall/snap chop. (The binary maintains this in the un-decompiled
// master perf; the writer-side re-base is the coherent reconstruction.)
projectedOrigin.angularPosition = localOrigin.angularPosition;
projectedVelocity.angularMotion = localVelocity.angularMotion;
record->speedDemand = speedDemand; // rec+0x28
bodyTargetSpeed = speedDemand;
}