WIP checkpoint: TCP_NODELAY + dense-send + BT_JIT/BT_ANIM probes + FOGDAY test egg (task #50 investigation)

Session-in-progress peer-motion work, checkpointed before bisecting the
07-13/07-14 gait regression. Contains: TCP_NODELAY on game sockets (L4NET),
every-frame dense position send while moving + came-to-rest/REST send +
per-frame BT_JIT/BT_ANIM smoothness probes (mech4). bodyTargetSpeed feed
reverted to derived-velocity default. MOVER.cpp spline experiment already
reverted. NOT a fix -- a checkpoint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-14 13:43:35 -05:00
co-authored by Claude Opus 4.8
parent 94647dd378
commit d5d512e087
3 changed files with 374 additions and 27 deletions
+112 -27
View File
@@ -1930,6 +1930,44 @@ void
DeadReckon(dt); // engine: reckoner + lerp
localToWorld = localOrigin;
// Lightweight dead-reckon smoothness probe (one flush/sec, cheap).
// PATH length (sum of |per-frame step|) vs NET displacement reveals
// jitter: a smooth walk has path==net; oscillation inflates path while
// net stays small. avgHorizon is the lerp target distance in time.
if (getenv("BT_JIT"))
{
static bool s_jinit = false;
static Point3D s_jprev, s_jnetA;
static float s_jacc = 0.0f, s_jpath = 0.0f, s_jmax = 0.0f, s_jhzn = 0.0f;
static int s_jfr = 0;
if (s_jinit)
{
const float dx = (float)(localOrigin.linearPosition.x - s_jprev.x);
const float dz = (float)(localOrigin.linearPosition.z - s_jprev.z);
const float step = sqrtf(dx * dx + dz * dz);
s_jpath += step;
if (step > s_jmax) s_jmax = step;
}
else { s_jnetA = localOrigin.linearPosition; s_jinit = true; }
s_jprev = localOrigin.linearPosition;
s_jhzn += (float)(nextUpdate - lastPerformance);
s_jfr++; s_jacc += dt;
if (s_jacc >= 1.0f)
{
const float ndx = (float)(localOrigin.linearPosition.x - s_jnetA.x);
const float ndz = (float)(localOrigin.linearPosition.z - s_jnetA.z);
const float net = sqrtf(ndx * ndx + ndz * ndz);
DEBUG_STREAM << "[repljit] frames=" << s_jfr
<< " path=" << s_jpath << " net=" << net
<< " ratio=" << (net > 0.001f ? s_jpath / net : 0.0f)
<< " maxStep=" << s_jmax << " avgStep=" << (s_jpath / s_jfr)
<< " avgHorizon=" << (s_jhzn / s_jfr)
<< "\n" << std::flush;
s_jacc = 0.0f; s_jpath = 0.0f; s_jmax = 0.0f; s_jhzn = 0.0f;
s_jfr = 0; s_jnetA = localOrigin.linearPosition;
}
}
// REPLICANT GAIT (task #50): animate the peer mech's legs at the
// REPLICATED speed -- without this the replicant slides around as a
// frozen statue. The master publishes worldLinearVelocity in every
@@ -1946,33 +1984,37 @@ void
MechControlsMapper *replMppr = MappingMapper(); // roster slot 0 (task #7)
if (!IsMechDestroyed() && replMppr != 0)
{
const Vector3D &wv = updateVelocity.linearMotion;
float spd = sqrtf((float)(wv.x * wv.x + wv.z * wv.z));
UnitVector zAxR;
localToWorld.GetFromAxis(Z_Axis, &zAxR);
const float fdot = -((float)wv.x * (float)zAxR.x
+ (float)wv.z * (float)zAxR.z); // mech faces -Z
Scalar sd = (fdot < 0.0f) ? -spd : spd;
// WALK-ENTRY FLOOR (user-reported "slides forward before it steps",
// root-caused 2026-07-14 from a per-frame [replmov] trace): the leg
// SM enters walk only when `standSpeed < commandedSpeed`, but a mech's
// forward WALK velocity == walkStrideLength which sits BELOW standSpeed
// (bhk1: walk 6.13 < stand ~6.8). The master clears the gate because
// it feeds the leg SM its COMMANDED throttle speed (>> standSpeed); the
// replicant feeds the DERIVED actual velocity, which never crosses the
// gate -- so a steadily-walking peer dead-reckons forward frozen in the
// standing pose (legState 0, legFrm pinned) until the master happens to
// accelerate past standSpeed. When the peer is clearly moving forward,
// floor the demand just past standSpeed so it enters the walk cycle at
// once; the walk case (6/7) clamps legCycleSpeed back to walkStrideLength,
// so the CADENCE still matches the actual travel (no foot-slip). Reverse
// needs no floor -- its entry gate is `commandedSpeed < ZeroSpeed`, which
// any negative demand already trips.
// (CORRECTED threshold: walkStrideLength is a STRIDE metric ~22, NOT
// the walk velocity ~6.1 -- gate on standSpeed, the actual gate value.)
if (sd > standSpeed * 0.5f && sd < standSpeed * 1.05f)
sd = standSpeed * 1.05f;
replMppr->speedDemand = sd;
// Feed the leg SM the REPLICATED COMMANDED speed (bodyTargetSpeed):
// the master's OWN mppr->speedDemand, carried in every pose (type-0)
// and speed (type-2) record (mech.cpp:1833/1841). This is the
// IDENTICAL input the master's own leg SM consumes, so the replicant
// selects the same gait state and RAMPS its cadence through accel /
// decel exactly as the master does -- the legs stay in sync with the
// motion. The earlier feed derived the ACTUAL velocity from
// updateVelocity, but a mech's forward walk velocity (~walkStrideLength)
// sits BELOW standSpeed, so it never cleared the stand->walk gate; the
// floor hack that fixed THAT then pinned the cadence flat during
// accel/decel -- the reported stutter/skip glitch. bodyTargetSpeed is
// signed (negative == reverse), so reverse needs no special case.
// (BT_REPL_VEL forces the old derived-velocity feed for A/B compare.)
static const int s_useCmd = getenv("BT_REPL_CMD") ? 1 : 0;
if (!s_useCmd) // default: derive from replicated velocity (bodyTargetSpeed is 0 -- master walks thr=0)
{
const Vector3D &wv = updateVelocity.linearMotion;
float spd = sqrtf((float)(wv.x * wv.x + wv.z * wv.z));
UnitVector zAxR;
localToWorld.GetFromAxis(Z_Axis, &zAxR);
const float fdot = -((float)wv.x * (float)zAxR.x
+ (float)wv.z * (float)zAxR.z); // mech faces -Z
Scalar sd = (fdot < 0.0f) ? -spd : spd;
if (sd > standSpeed * 0.5f && sd < standSpeed * 1.05f)
sd = standSpeed * 1.05f;
replMppr->speedDemand = sd;
}
else
{
replMppr->speedDemand = bodyTargetSpeed;
}
// REPLICANT TURN-STEP (user-reported: a turning peer
// statue-rotates while the local mech steps): the leg SM's
// Standing case arms the turn-in-place "trn" clip (state 4,
@@ -2008,6 +2050,35 @@ void
legCycleSpeed = 0.0f;
(void)AdvanceLegAnimation(dt); // joints only; travel = DeadReckon
// Cadence-vs-travel sync probe (0.25s, ~4 flush/s -- cheap). Compares
// the leg CADENCE input (bodyTargetSpeed / legCycleSpeed) against the
// ACTUAL dead-reckoned ground speed; if the legs are in sync the two
// track together and legFrm advances monotonically.
if (getenv("BT_ANIM"))
{
static bool s_ainit = false;
static Point3D s_aprev;
static float s_aacc = 0.0f, s_apath = 0.0f;
if (s_ainit)
{
const float dx = (float)(localOrigin.linearPosition.x - s_aprev.x);
const float dz = (float)(localOrigin.linearPosition.z - s_aprev.z);
s_apath += sqrtf(dx * dx + dz * dz);
}
s_aprev = localOrigin.linearPosition; s_ainit = true;
s_aacc += dt;
if (s_aacc >= 0.25f)
{
DEBUG_STREAM << "[anim] cmd=" << bodyTargetSpeed
<< " drSpeed=" << (s_apath / s_aacc)
<< " legCyc=" << legCycleSpeed
<< " legState=" << (int)legStateAlarm.GetLevel()
<< " legFrm=" << legAnimation.currentFrame
<< "\n" << std::flush;
s_aacc = 0.0f; s_apath = 0.0f;
}
}
if (getenv("BT_REPL_TRN"))
{
static float s_rt = 0.0f; s_rt += dt;
@@ -3432,10 +3503,24 @@ void
(Scalar)(updateVelocity.linearMotion.x * updateVelocity.linearMotion.x
+ updateVelocity.linearMotion.z * updateVelocity.linearMotion.z);
const Logical cameToRest = (liveSpdSq < 0.01f && sentSpdSq > 0.01f);
// DENSE MOTION SEND (replicant-smoothness): the position deadband
// fires IRREGULARLY (only after ~0.55u of drift), but the replicant's
// dead-reckoner predicts the next record one PAST-interval ahead and
// lerps its localOrigin toward that fixed horizon target -- so when the
// deadband cadence != the predicted cadence, the peer decelerates
// toward a stale target then lurches when a record finally lands
// (measured: dead-reckoned ground speed surging 5<->27 while the master
// walks a steady 6.13). Sending a pose record EVERY frame while moving
// makes the cadence regular + dense (records ~1 frame apart), so the
// horizon prediction matches and the lerp tracks smoothly. On a LAN
// this is a handful of small packets/frame -- negligible.
static const int s_denseTx = getenv("BT_NO_DENSE_TX") ? 0 : 1;
const Logical moving = (liveSpdSq > 0.25f);
if (
error.LengthSquared() > posDb
|| lastPerformance - lastUpdate > 2.0f
|| cameToRest
|| (s_denseTx && moving)
)
{
ForceUpdate(); // type 0: pose record