#82 FINAL LAYER: the peer body-channel trn-lock -- a circling limper skated because the peer's turn-arm had no walk-demand yield and the trn speed exit read a dead replicant mapper cell
The master limped clean (218x gimp-cycle records, zero trn) -- a _ReturnAddress trap on SetAnimationState(4) proved ALL peer trn arming came from the port's own mech4 turn-step block, not the type-3 reader. Two dead ends, one root: - mech4 peer arm: gated on !wantsWalk (standSpeed < bodyTargetSpeed), exits trn when walking demand appears -- the leg twin's authentic precedence (part_012.c:12013, the Standing speed test outranks the turn test). - mech2 body case 4: bspd reads bodyTargetSpeed on ReplicantInstance; the local mapper's speedDemand is a dead cell on a peer (reads 0 forever -> no exit). Verified 2-node timeline: arena circle replicant 218x state-24 / 0x state-4 (was 116x trn churn), grass circle 235-clean, knockdowns bounded, 0 deaths. Diagnostics kept: BT_TRNTRAP ra-trap, BT_ANIMIND_CAP, [gimpfeed], [replgimp] extension. KB: locomotion.md trn-lock entry + the dead-mapper-cell lesson. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QRjrTQpJd6u9XyfnUbTB3v
This commit is contained in:
co-authored by
Claude Fable 5
parent
14ff3519ac
commit
e91d447405
@@ -90,6 +90,7 @@
|
||||
//
|
||||
|
||||
#include <bt.hpp>
|
||||
#include <intrin.h> // _ReturnAddress -- the #82 trn-armer trap
|
||||
#include <AUDCMP.hpp> // AudioComponent -- the foot-plant step-intensity broadcast
|
||||
#include <AUDSRC.hpp> // AudioSource -- footstep-source identification
|
||||
#include <AUDLVL.hpp> // AudioResource::GetAudioLevelOfDetail
|
||||
@@ -250,11 +251,33 @@ void
|
||||
// StateIndicator::SetState's Verify(state<stateCount). [T2]
|
||||
if (state >= 0 && state < 0x21)
|
||||
{
|
||||
// DIAG (BT_TRNTRAP): who arms state 4 on a REPLICANT? Module-relative
|
||||
// ra (symbolize: tools/symcrash.py) -- the #82 trn-lock armer hunt.
|
||||
if (state == 4 && getenv("BT_TRNTRAP")
|
||||
&& GetInstance() == ReplicantInstance)
|
||||
{
|
||||
static int s_tt = 0;
|
||||
if (s_tt++ < 60)
|
||||
{
|
||||
char ttbuf[96];
|
||||
sprintf(ttbuf, "[trntrap] mech=%d ra=btl4+0x%lx",
|
||||
(int)GetEntityID(),
|
||||
(unsigned long)_ReturnAddress()
|
||||
- (unsigned long)GetModuleHandleA(0));
|
||||
DEBUG_STREAM << ttbuf << std::endl << std::flush;
|
||||
}
|
||||
}
|
||||
// #78 audio-flake diag: print the LIVE indicator address once per mech
|
||||
// so a session's [attrbind] ptr can be checked against it (stale-bind
|
||||
// hypothesis: watchers bound to a recreated mech's dead indicator).
|
||||
if (getenv("BT_AUDIO_SPATIAL")) { static int s_ai=0;
|
||||
if (s_ai++ < 6 || (state >= 22 && state <= 27 && s_ai < 200))
|
||||
// BT_ANIMIND_CAP: the 200-print budget hid every post-1-minute state
|
||||
// (two #82 investigations mis-read the silence as "never entered") --
|
||||
// raise it for state-timeline diagnosis.
|
||||
static int s_aiCap = -1;
|
||||
if (s_aiCap < 0) { const char *c = getenv("BT_ANIMIND_CAP"); s_aiCap = (c && *c) ? atoi(c) : 200; }
|
||||
if (s_ai++ < 6 || (state >= 22 && state <= 27 && s_ai < s_aiCap)
|
||||
|| (s_aiCap > 200 && s_ai < s_aiCap))
|
||||
DEBUG_STREAM << "[animind] mech=" << (int)GetEntityID()
|
||||
<< " &animationState=" << (void*)&animationState
|
||||
<< " inst=" << (int)(GetInstance() == ReplicantInstance)
|
||||
@@ -833,8 +856,35 @@ Scalar
|
||||
// alias (an AV); controlsMapper is the typed mirror of roster slot 0. A mech
|
||||
// with no mapper reads demand 0 (idles) -- matching a zeroed binary roster.
|
||||
MechControlsMapper *mppr = MappingMapper(); // roster slot 0 (task #7)
|
||||
// REPLICANT DEMAND FEED (#82 final root, 2026-07-30): a replicant's LOCAL
|
||||
// mapper cell is a dead 0 (input never drives it), so every demand
|
||||
// threshold in this machine failed on peers -- from STAND the walk-begin
|
||||
// could never fire and the trn state churned on the turn signal forever
|
||||
// ("lifting legs in an alternating turning fashion"). It never mattered
|
||||
// while a peer was mid-cycle (clip-end chains keep cycles going, which is
|
||||
// why straight-line/grass limpers replicated fine) -- it bit the moment a
|
||||
// KNOCKDOWN recovery dropped the replicant to STAND. The binary replicant
|
||||
// reads a LIVE demand here (its mapper cell replicates with the subsystem
|
||||
// records); the port's replicated equivalent is bodyTargetSpeed (@0x6b4 --
|
||||
// every update record's speedDemand writes it on RX). [gimpfeed]-probe
|
||||
// proof: AdvanceLegAnimationGimp never even runs on replicants, so THIS
|
||||
// driver is the peer's one and only leg machine.
|
||||
Scalar commandedSpeed =
|
||||
(mppr != 0) ? mppr->speedDemand : 0.0f;
|
||||
(GetInstance() == ReplicantInstance) ? bodyTargetSpeed
|
||||
: (mppr != 0) ? mppr->speedDemand : 0.0f;
|
||||
// DIAG (BT_GIMPFEED): 1 Hz -- the NORMAL driver is the only leg machine a
|
||||
// replicant runs; print every threshold operand it sees.
|
||||
if (getenv("BT_GIMPFEED") && GetInstance() == ReplicantInstance)
|
||||
{
|
||||
static Scalar s_gfAcc = 0.0f; s_gfAcc += time_slice;
|
||||
if (s_gfAcc >= 1.0f) { s_gfAcc = 0.0f;
|
||||
DEBUG_STREAM << "[gimpfeed] cmd=" << commandedSpeed
|
||||
<< " bts=" << bodyTargetSpeed
|
||||
<< " standSpeed=" << standSpeed
|
||||
<< " state=" << (int)legStateAlarm.GetLevel()
|
||||
<< " gl=" << ([](Mech *m){ extern int BTMechGimpLevel(void*); return BTMechGimpLevel(m); })(this)
|
||||
<< std::endl << std::flush; }
|
||||
}
|
||||
Scalar distance = 0.0f;
|
||||
|
||||
// binary: legAnimationState@0x3b0 IS legStateAlarm's level (one field; the
|
||||
@@ -1251,7 +1301,15 @@ Scalar
|
||||
// re-enter walk on the same frame.
|
||||
{
|
||||
MechControlsMapper *bm = MappingMapper();
|
||||
const Scalar bspd = (bm != 0) ? bm->speedDemand : 0.0f;
|
||||
// REPLICANT: the local mapper's speedDemand is a dead cell (nothing
|
||||
// writes it on a peer -- reads 0 forever, so a peer that entered trn
|
||||
// could NEVER take the speed exit = the #82 trn-lock). The peer's
|
||||
// commanded speed is the REPLICATED demand (bodyTargetSpeed@0x6b4,
|
||||
// stamped by every record RX) -- same source the peer Standing case
|
||||
// walks on, so entry and exit judge the same number.
|
||||
const Scalar bspd = (GetInstance() == ReplicantInstance)
|
||||
? bodyTargetSpeed
|
||||
: (bm != 0) ? bm->speedDemand : 0.0f;
|
||||
if (standSpeed < bspd || bspd < ZeroSpeed) // walk / reverse (leg-symmetric)
|
||||
{
|
||||
bodyStateAlarm.SetLevel(0);
|
||||
@@ -1613,12 +1671,38 @@ Scalar
|
||||
// straight at the speedDemand cell.
|
||||
MechControlsMapper *gimpMppr = MappingMapper();
|
||||
static Scalar s_nullDemand = 0.0f; // no mapper -> demand 0, writes inert
|
||||
ReconMotionSource *motionSource = gimpMppr
|
||||
? (ReconMotionSource *)&gimpMppr->speedDemand
|
||||
: (ReconMotionSource *)&s_nullDemand;
|
||||
// REPLICANT DEMAND FEED (#82 final root, 2026-07-30): a replicant's LOCAL
|
||||
// mapper demand is a dead 0 (no input ever drives it), so every speed
|
||||
// threshold in this machine read 0 on peers -- in particular the trn exit
|
||||
// (standSpeed < commandedSpeed) could NEVER fire, and a gimped replicant
|
||||
// that staggered through stand->trn was LOCKED in turn-in-place forever
|
||||
// (the observed "lifting legs in an alternating turning fashion" skate;
|
||||
// unblinded [animind] timeline: SIX transitions in 3 minutes, ending at
|
||||
// state 4). The binary's replicant reads a LIVE demand here because the
|
||||
// mapper's speedDemand cell replicates with the subsystem records; the
|
||||
// port's replicated equivalent of that same value is bodyTargetSpeed
|
||||
// (@0x6b4 -- every update record's speedDemand writes it on RX). Feed
|
||||
// the machine from it on replicants; the gimp caps that write back
|
||||
// through the source are transient there (the next record rewrites it),
|
||||
// and masters keep the authentic live mapper cell.
|
||||
ReconMotionSource *motionSource =
|
||||
(GetInstance() == ReplicantInstance)
|
||||
? (ReconMotionSource *)&bodyTargetSpeed
|
||||
: gimpMppr
|
||||
? (ReconMotionSource *)&gimpMppr->speedDemand
|
||||
: (ReconMotionSource *)&s_nullDemand;
|
||||
Scalar distance = 0.0f;
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (gotcha #23)
|
||||
int mode = BTMechGimpLevel(this); // binary this+0x40 = gimp level
|
||||
// DIAG (BT_GIMPFEED): 1 Hz -- what demand does this machine actually see?
|
||||
if (getenv("BT_GIMPFEED") && GetInstance() == ReplicantInstance)
|
||||
{
|
||||
static Scalar s_gfAcc = 0.0f; s_gfAcc += time_slice;
|
||||
if (s_gfAcc >= 1.0f) { s_gfAcc = 0.0f;
|
||||
DEBUG_STREAM << "[gimpfeed] repl cmd=" << motionSource->commandedSpeed
|
||||
<< " bts=" << bodyTargetSpeed << " state=" << legAnimationState
|
||||
<< " standSpeed=" << standSpeed << " mode=" << mode << std::endl << std::flush; }
|
||||
}
|
||||
// RE-SYNC alarm -> state member (see AdvanceBodyAnimationGimp note).
|
||||
legAnimationState = (int)legStateAlarm.GetLevel();
|
||||
int state = legAnimationState; // this+0x3b0
|
||||
|
||||
@@ -2436,10 +2436,19 @@ void
|
||||
const int bs = (int)bodyStateAlarm.GetLevel();
|
||||
const bool turning = (replMppr->turnDemand > 0.05f
|
||||
|| replMppr->turnDemand < -0.05f);
|
||||
if (turning && bs == 0) // standing + turning -> enter turn
|
||||
// YIELD TO WALK DEMAND (#82 trn-lock): the master only sits in
|
||||
// trn when its commanded speed is at stand (the leg Standing
|
||||
// case's speed test wins first, part_012.c:12013). A circling
|
||||
// LIMPER keeps full demand (bts~24) while its yaw rate reads as
|
||||
// turning here -- ungated, this block pinned the peer in body
|
||||
// case 4 (advance-only, no exit) forever = the skating. Mirror
|
||||
// the leg twin's precedence: walking demand outranks the pivot.
|
||||
const bool wantsWalk = (standSpeed < bodyTargetSpeed
|
||||
|| bodyTargetSpeed < 0.0f /*reverse*/);
|
||||
if (turning && bs == 0 && !wantsWalk) // standing pivot only
|
||||
SetBodyAnimation(4);
|
||||
else if (!turning && bs == 4) // turn stopped -> back to stand
|
||||
SetBodyAnimation(0);
|
||||
else if (bs == 4 && (!turning || wantsWalk))
|
||||
SetBodyAnimation(0); // stand; case 0 walk-begins next tick
|
||||
}
|
||||
// GIMP routing (#78): a limping PEER runs the gimp machines too
|
||||
// (the graphicAlarm level replicates with the damage state).
|
||||
@@ -2456,6 +2465,9 @@ void
|
||||
s_ga = 0.0f;
|
||||
DEBUG_STREAM << "[replgimp] ent=" << (int)GetEntityID()
|
||||
<< " gl=" << replGl
|
||||
<< " bts=" << bodyTargetSpeed
|
||||
<< " standSpeed=" << standSpeed
|
||||
<< " bodyState=" << (int)bodyStateAlarm.GetLevel()
|
||||
<< " sim=" << (int)GetSimulationState()
|
||||
<< " hasClips=" << hasGimpClips << " zones:";
|
||||
for (int zi = 0; zi < damageZoneCount; ++zi)
|
||||
|
||||
Reference in New Issue
Block a user