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:
co-authored by
Claude Fable 5
parent
e6ad29d648
commit
a3b735d5da
@@ -2000,6 +2000,24 @@ void
|
||||
<< " spd=" << replMppr->speedDemand << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
// PER-FRAME heading trace (BT_REPL_HDG, choppy-spin diagnosis): the
|
||||
// replicant's rendered yaw each frame + dt. A smooth dead-reckoned
|
||||
// spin advances ~yawRate*dt every frame; choppiness shows as heading
|
||||
// STALLS followed by SNAPS (records arriving without integration
|
||||
// between them).
|
||||
if (getenv("BT_REPL_HDG") && yawRate != 0.0f)
|
||||
{
|
||||
YawPitchRoll rh, ph, uh;
|
||||
rh = localOrigin.angularPosition;
|
||||
ph = projectedOrigin.angularPosition;
|
||||
uh = updateOrigin.angularPosition;
|
||||
DEBUG_STREAM << "[replhdg] yaw=" << (Scalar)rh.yaw
|
||||
<< " proj=" << (Scalar)ph.yaw
|
||||
<< " upd=" << (Scalar)uh.yaw
|
||||
<< " lp-lu=" << (lastPerformance.ticks - lastUpdate.ticks)
|
||||
<< " nu-lp=" << (nextUpdate.ticks - lastPerformance.ticks)
|
||||
<< " yawRate=" << yawRate << " dt=" << dt << "\n" << std::flush;
|
||||
}
|
||||
|
||||
// REPLICANT BEAMS (task #51): the emitters carry live replicated
|
||||
// discharge state (Emitter::ReadUpdateRecord); draw them with the
|
||||
@@ -3356,6 +3374,20 @@ void
|
||||
// UpdateTurnVelocityDiffrence. [T1 expressions; zero-deadband
|
||||
// guard falls back to the old quat-w stand-in]
|
||||
{
|
||||
// PEER-ESTIMATE MIRROR (2026-07-14, replicant-chop fix): advance the
|
||||
// projection by the last-SENT angular velocity each frame -- this is
|
||||
// what the replicant's dead-reckoner is doing with the last record.
|
||||
// The gate below then measures the peer's TRUE drift, so a steady
|
||||
// turn (matching velocity) sends almost nothing and the replicant
|
||||
// extrapolates smoothly, instead of the stale mirror firing a
|
||||
// re-base record EVERY frame (the stall/snap chop). The type-4
|
||||
// writer re-bases this mirror on each send (mech.cpp case 4).
|
||||
{
|
||||
Vector3D angStepM;
|
||||
angStepM.Multiply(updateVelocity.angularMotion, dt);
|
||||
projectedOrigin.angularPosition.Add(
|
||||
projectedOrigin.angularPosition, angStepM);
|
||||
}
|
||||
Scalar angDb = (updateTurnAngleDeadband > 0.0f) ? updateTurnAngleDeadband : -1.0f;
|
||||
Scalar velDb = (updateTurnVelocityDeadband > 0.0f) ? updateTurnVelocityDeadband : -1.0f;
|
||||
Logical resync = False;
|
||||
@@ -4333,8 +4365,18 @@ void
|
||||
}
|
||||
|
||||
// Keep the simulation/networking bookkeeping consistent (this is exactly
|
||||
// what the base "no time / stasis" early-out does).
|
||||
WriteSimulationUpdate(update_stream);
|
||||
// what the base "no time / stasis" early-out does). MASTER ONLY (2026-07-14):
|
||||
// replication is master-authoritative -- a replicant must never serialize.
|
||||
// The port's replicant runs the leg SM for JOINTS (task #50 accommodation),
|
||||
// whose transitions call ForceUpdate() and mark updateModel; serializing those
|
||||
// emitted derived/uninitialized state back into the stream (and the type-3
|
||||
// writer re-dispatches SetBodyAnimation on the WRITER -- the real-clock crash
|
||||
// rode this path with legAnimationState still 0xCDCDCDCD). Discard replicant
|
||||
// marks instead.
|
||||
if (GetInstance() != ReplicantInstance)
|
||||
WriteSimulationUpdate(update_stream);
|
||||
else
|
||||
updateModel = 0; // drop accommodation-path marks
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user