#148: restore the ENGINE's replicant instance-branch in the Mech subsystem tick
Correct on its own merits as a fidelity fix; it is NOT the cause of #148, and I am not claiming it is. Entity::Perform (ENTITY.cpp:733-793, real engine source [T0]) picks the executable predicate BY INSTANCE: if (GetInstance() != ReplicantInstance) IsNonReplicantExecutable() else IsReplicantExecutable() and the two differ exactly on the replicant case (SIMULATE.h:195-206): NonReplicant : (flags & DontExecuteFlag) == 0 Replicant : (flags & DontExecuteFlag) == 0 || lastUpdate >= lastPerformance `ExecuteOnUpdate()` SETS DontExecuteFlag -- it means "do not tick me every frame, tick me when an UPDATE ARRIVES". Mech's reconstructed tick loop used the NonReplicant predicate for EVERY mech, dropping the branch, so on a replicant any ExecuteOnUpdate subsystem could never run however many records arrived. Restored. Measured: it does NOT move #148 (first TorsoCopySimulation call 1014 -> 1006, noise). So the torso's own flag was not the gate. Keeping it because the engine source is unambiguous about what the loop is supposed to do. WHAT #148 ACTUALLY IS, now much better characterised: * The record CADENCE is authentic -- my original "only 13 records" framing was wrong. Payloads are the sweep EXTREMES with `rate` flipping sign each time (atUpd 0.0437, 2.3558, -2.3928, 2.3854, ...): the master sends on RATE CHANGE and the peer dead-reckons `atUpd + rate * elapsed` between them. 12 records for 12 direction reversals is correct, not starved. * The real defect is that the peer's copy torso PERFORMANCE does not run at all until log line ~1006, while its first record arrived at line 205 -- ~800 lines of correctly-replicated twist integrated by nobody. The first tick coincides with the replicant's MODEL bring-up, not with record arrival: [loadclips] end: fScale=0.8 ... hasGimpClips=1 [clipfix] mech 05769358 -> EXTERIOR (lean) [torso] PushTwist COPY node=057A3C68 type=1 twist=-1.52319 so the gate is above the subsystem level, in replicant model/clip init. Not yet found; #148 stays OPEN. Also: [torso-copy] logs on call #0 (s_cl++ % 120), so its first line IS the first Performance call -- that is what makes the 205-vs-1006 gap readable, and it is why the earlier "first copy currentTwist != 0 at 1016" reading was a SAMPLING artifact, not a measurement of when the twist started. Probe additions kept: [torso-copy] now prints limL/limR/enab (which ruled out the ComputeTargetTwist clamp -- limits load correctly at +/-2.44346 on the copy), and [launchframe] now prints the shooter's live torso twist so twistDelta and its driver sit on the SAME line. That pairing is what proved #141 is fully fixed: every zero-twistDelta peer launch reads liveTwist=0, and the first launch with liveTwist=-1.84061 reads twistDelta=-1.83813. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SgmXGNMXavXiafKXf9MDC
This commit is contained in:
co-authored by
Claude Opus 5
parent
eebe7e61a4
commit
f36f0136c8
@@ -1607,9 +1607,19 @@ void
|
||||
const void *parent = (pseg != 0) ? (const void *)pseg->GetParent() : 0;
|
||||
int parentIdx = (pseg != 0) ? pseg->GetParentIndex() : -99;
|
||||
JointSubsystem *js = sm->GetJointSubsystem();
|
||||
// #148: the SHOOTER's live torso twist AT THIS INSTANT. The
|
||||
// [torso-copy] probe samples every 120th call, so its "first
|
||||
// non-zero" tells you when it first SAMPLED, not when the twist
|
||||
// started -- that is exactly the artifact that made the earlier
|
||||
// "the peer had no twist to carry" reading look right. Read the
|
||||
// cell directly instead, so twistDelta and the twist that should
|
||||
// be driving it are on the SAME line.
|
||||
extern Scalar *BTGetTorsoTwistAddr(Subsystem *torso);
|
||||
Scalar *twp = BTGetTorsoTwistAddr(sm->GetTorsoSubsystem());
|
||||
DEBUG_STREAM << "[launchframe] "
|
||||
<< (sm->GetInstance() == Entity::ReplicantInstance
|
||||
? "REPLICANT" : "master ")
|
||||
<< " liveTwist=" << (twp != 0 ? (float)*twp : -99.0f)
|
||||
<< " seg=" << muzzle_seg << " segResolved=" << haveFrame
|
||||
<< " segYaw=" << segYaw << " bodyYaw=" << bodyYaw
|
||||
<< " twistDelta=" << dYaw
|
||||
@@ -7940,7 +7950,33 @@ void
|
||||
continue;
|
||||
if (i != 0) // slot 0 = the mapper (task #7)
|
||||
++subsystemsPresent;
|
||||
if (!subsystem->IsNonReplicantExecutable())
|
||||
// #148 -- THE INSTANCE BRANCH. Entity::Perform (ENTITY.cpp:733-793,
|
||||
// real engine source [T0]) picks the predicate by instance:
|
||||
// if (GetInstance() != ReplicantInstance) IsNonReplicantExecutable()
|
||||
// else IsReplicantExecutable()
|
||||
// and they differ exactly on the replicant case (SIMULATE.h:195-206):
|
||||
// NonReplicant: (flags & DontExecuteFlag) == 0
|
||||
// Replicant : (flags & DontExecuteFlag) == 0
|
||||
// || lastUpdate >= lastPerformance
|
||||
// `ExecuteOnUpdate()` SETS DontExecuteFlag -- it means "do not tick me
|
||||
// every frame, tick me when an UPDATE ARRIVES". This loop used the
|
||||
// NonReplicant predicate for every mech, so on a REPLICANT any
|
||||
// ExecuteOnUpdate subsystem never ran at all, no matter how many
|
||||
// records arrived for it.
|
||||
//
|
||||
// Measured (scratchpad/night13/missileframe2.sh): the peer's copy
|
||||
// TORSO received its first record at log line 205 but its Performance
|
||||
// did not run until line 1014 -- ~800 lines of arriving twist data
|
||||
// integrated by nobody, so the peer's torso sat at 0 and its missiles
|
||||
// launched along the body facing (the tail of #141). The records
|
||||
// themselves were fine: they are sent on RATE CHANGE (the sweep's
|
||||
// direction flips -- atUpd +/-2.39 with rate flipping sign), and the
|
||||
// peer dead-reckons `atUpd + rate * elapsed` between them.
|
||||
const Logical execOK =
|
||||
(GetInstance() != Entity::ReplicantInstance)
|
||||
? subsystem->IsNonReplicantExecutable()
|
||||
: subsystem->IsReplicantExecutable();
|
||||
if (!execOK)
|
||||
continue;
|
||||
|
||||
// The controls-mapping subsystem (roster slot 0 via Mech::SetMapping
|
||||
|
||||
@@ -823,6 +823,13 @@ void
|
||||
<< " vel=" << twistVelocity
|
||||
<< " lastUpd=" << lastUpdateTime
|
||||
<< " now=" << GetCurrentTime()
|
||||
// #148: ComputeTargetTwist ends in Min(limitLeft)/Max(limitRight).
|
||||
// If the COPY's limits never loaded they are 0/0, which pins
|
||||
// targetTwist to EXACTLY 0 no matter what the record carried --
|
||||
// which is what a peer stuck at zero twist would look like.
|
||||
<< " limL=" << horizontalLimitLeft
|
||||
<< " limR=" << horizontalLimitRight
|
||||
<< " enab=" << (int)horizontalEnabled
|
||||
<< " copy=" << (int)isDamagedCopy << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user