#52 SKATE detector (ungated field forensic) -- negative-verified; local repro eludes
The night-12 field logs eliminated record starvation (zero [ghost] during three observed skating windows), so the bug lives in gait APPLICATION on peers. This adds the [skate] detector to the death-handler tick: a replicant moving >0.08 u/frame for 90+ frames with BOTH animation channels idle (legCycleSpeed + bodyCycleSpeed ~ 0) logs one line per episode + a SKATE matchlog record carrying the discriminating inputs (legCyc/bodyCyc/cmdSpd/destroyed/mode). Honest history: the first build keyed on legCycleSpeed alone and false-fired on every healthy movement phase -- the current peer architecture poses joints from the BODY channel (s_peerLegCh=0, AdvanceBodyAnimation mj=1), so legCycleSpeed==0 is NORMAL there. Caught same-session by the [gimpfeed] silence (AdvanceLegAnimation never runs on peers); corrected to channel-agnostic before anything shipped. Bench (skatebench2.sh, 2-node, autodrive walker + kill every ~40s): 7 death/respawn cycles, ZERO skate hits either side -- no false fires, and light local conditions do NOT reproduce the field skating. Next provocations: leg-GIMPED walker (the #82 family transition) and 6-player load; otherwise the detector rides the next cut and the field names the failing case for us. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6d7946a264
commit
9604f4c492
@@ -1206,7 +1206,8 @@ void
|
||||
}
|
||||
|
||||
MechDeathHandler::MechDeathHandler(Mech *mech) // @0042a984
|
||||
: owner(mech), prevMode(0), ghostFrames(0), ghostLogged(0)
|
||||
: owner(mech), prevMode(0), ghostFrames(0), ghostLogged(0),
|
||||
skatePrevX(0.0f), skatePrevZ(0.0f), skateFrames(0), skateLogged(0)
|
||||
{
|
||||
// per-zone last-damage cache, zeroed (binary this[0x10], size mech+0x11c).
|
||||
int count = (mech != 0 && mech->damageZoneCount > 0) ? mech->damageZoneCount : 0;
|
||||
@@ -1229,6 +1230,64 @@ void
|
||||
if (owner == 0)
|
||||
return;
|
||||
|
||||
// #52 SKATE DETECTOR (PORT, ungated -- the night-12 forensic): a REPLICANT
|
||||
// whose position advances while its LEG CHANNEL is not cycling is the
|
||||
// field "skating" signature. One line per episode, carrying the three
|
||||
// inputs that split the failure: bodyTargetSpeed (the replicated commanded
|
||||
// speed the leg SM feeds from -- ~0 means the RECORD side starved the
|
||||
// gait), legCycleSpeed (~0 with a live demand means the SM is STUCK), and
|
||||
// IsMechDestroyed (1 means the gait feed block was SKIPPED -- the stale
|
||||
// post-respawn flag suspect). ~90 frames of sustained skate to trip; the
|
||||
// threshold 0.08 u/frame ~ 2.4 u/s at 30 fps, well above reckoner jitter.
|
||||
if (owner->GetInstance() == Entity::ReplicantInstance)
|
||||
{
|
||||
const float px = (float)owner->localOrigin.linearPosition.x;
|
||||
const float pz = (float)owner->localOrigin.linearPosition.z;
|
||||
const float dx = px - skatePrevX, dz = pz - skatePrevZ;
|
||||
const float step = sqrtf(dx * dx + dz * dz);
|
||||
skatePrevX = px; skatePrevZ = pz;
|
||||
// CHANNEL-AGNOSTIC (corrected same-day): the peer's joints are posed by
|
||||
// the BODY channel in the current authentic architecture (mech4
|
||||
// s_peerLegCh=0 -> AdvanceBodyAnimation mj=1), so legCycleSpeed==0 is
|
||||
// NORMAL there -- the first detector build false-fired on every healthy
|
||||
// movement phase. Skating = moving with BOTH channels idle.
|
||||
const float cyc = fabsf((float)owner->legCycleSpeed)
|
||||
+ fabsf((float)owner->bodyCycleSpeed);
|
||||
const int movingNoLegs = (step > 0.08f && step < 5.0f // 5+: teleport/warp
|
||||
&& cyc < 0.05f) ? 1 : 0;
|
||||
if (movingNoLegs)
|
||||
{
|
||||
if (++skateFrames > 90 && !skateLogged)
|
||||
{
|
||||
skateLogged = 1;
|
||||
DEBUG_STREAM << "[skate] replicant " << owner->GetEntityID()
|
||||
<< " SKATING: " << skateFrames << " frames moving ("
|
||||
<< step << " u/frame) with legCyc="
|
||||
<< (float)owner->legCycleSpeed
|
||||
<< " bodyCyc=" << (float)owner->bodyCycleSpeed
|
||||
<< " bodyTargetSpeed=" << (float)owner->bodyTargetSpeed
|
||||
<< " destroyed=" << (int)owner->IsMechDestroyed()
|
||||
<< " mode=" << (int)owner->MovementMode()
|
||||
<< " at (" << px << "," << pz << ")\n" << std::flush;
|
||||
if (BTMatchLogActive())
|
||||
BTMatchLog("SKATE", "mech=%d:%d frames=%d step=%.3f cyc=%.3f "
|
||||
"cmdSpd=%.2f destroyed=%d mode=%d x=%.1f z=%.1f",
|
||||
BTMatchHostOf(owner->GetEntityID()), (int)owner->GetEntityID(),
|
||||
skateFrames, step, cyc,
|
||||
(float)owner->bodyTargetSpeed,
|
||||
(int)owner->IsMechDestroyed(), (int)owner->MovementMode(),
|
||||
px, pz);
|
||||
}
|
||||
}
|
||||
else if (skateFrames > 0)
|
||||
{
|
||||
if (skateLogged)
|
||||
DEBUG_STREAM << "[skate] replicant " << owner->GetEntityID()
|
||||
<< " recovered after " << skateFrames << " frames\n" << std::flush;
|
||||
skateFrames = 0; skateLogged = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// #108 GHOST DETECTOR (PORT, ungated -- the night-9 forensic): a visible
|
||||
// REPLICANT that stops receiving update records is exactly what "everyone
|
||||
// shoots thin air" looks like from the other side. ReadUpdateRecord
|
||||
|
||||
Reference in New Issue
Block a user