diff --git a/game/reconstructed/mech.cpp b/game/reconstructed/mech.cpp index b1e3036..3b664f8 100644 --- a/game/reconstructed/mech.cpp +++ b/game/reconstructed/mech.cpp @@ -1848,7 +1848,23 @@ void case 2: // commanded-speed update { Mech__SpeedUpdateRecord *record = (Mech__SpeedUpdateRecord *)message; - Simulation::ReadUpdateRecord(message); // FUN_0041bd34 + { + // CLOCK GUARD (keyboard-skip fix): the base reader stamps lastUpdate=Now() + // on EVERY record ('HACK' per SIMULATE.cpp:276). Non-pose records carry no + // pose, but that stamp shrinks the dead-reckoner's projection span + // (nextUpdate - lastUpdate) WITHOUT refreshing updateOrigin -- so the + // peer's position target jumps backward toward the stale origin, then the + // next pose record yanks it forward: target oscillation every frame while + // records churn. Keyboard driving churns them CONSTANTLY (the throttle + // lever sweeps every frame of a key-hold -> a type-2 speed record per + // frame), which autodrive's pinned throttle never does -- the 'skips when + // I drive, smooth when you drive' report. Preserve the clock; keep the + // real payload (simulationState). BT_T2_CLOCK restores the old stamp (A/B). + static const int s_t2clock = getenv("BT_T2_CLOCK") ? 1 : 0; + const Time savedLastUpdate = lastUpdate; + Simulation::ReadUpdateRecord(message); + if (!s_t2clock) lastUpdate = savedLastUpdate; + } // FUN_0041bd34 bodyTargetSpeed = record->speedDemand; // @0x6b4 <- rec+0x10 } break; @@ -1856,7 +1872,23 @@ void case 3: // leg/body state + stability { Mech__StateUpdateRecord *record = (Mech__StateUpdateRecord *)message; - Simulation::ReadUpdateRecord(message); + { + // CLOCK GUARD (keyboard-skip fix): the base reader stamps lastUpdate=Now() + // on EVERY record ('HACK' per SIMULATE.cpp:276). Non-pose records carry no + // pose, but that stamp shrinks the dead-reckoner's projection span + // (nextUpdate - lastUpdate) WITHOUT refreshing updateOrigin -- so the + // peer's position target jumps backward toward the stale origin, then the + // next pose record yanks it forward: target oscillation every frame while + // records churn. Keyboard driving churns them CONSTANTLY (the throttle + // lever sweeps every frame of a key-hold -> a type-2 speed record per + // frame), which autodrive's pinned throttle never does -- the 'skips when + // I drive, smooth when you drive' report. Preserve the clock; keep the + // real payload (simulationState). BT_T2_CLOCK restores the old stamp (A/B). + static const int s_t2clock = getenv("BT_T2_CLOCK") ? 1 : 0; + const Time savedLastUpdate = lastUpdate; + Simulation::ReadUpdateRecord(message); + if (!s_t2clock) lastUpdate = savedLastUpdate; + } bodyResetLatch = record->legResetLatch; // @0x658 <- rec+0x10 stabilityAlarm.SetLevel(record->stability); // @0x4c4 <- rec+0x18 if (record->legState == 0) @@ -1904,7 +1936,23 @@ void case 5: // knockdown { Mech__FallUpdateRecord *record = (Mech__FallUpdateRecord *)message; - Simulation::ReadUpdateRecord(message); + { + // CLOCK GUARD (keyboard-skip fix): the base reader stamps lastUpdate=Now() + // on EVERY record ('HACK' per SIMULATE.cpp:276). Non-pose records carry no + // pose, but that stamp shrinks the dead-reckoner's projection span + // (nextUpdate - lastUpdate) WITHOUT refreshing updateOrigin -- so the + // peer's position target jumps backward toward the stale origin, then the + // next pose record yanks it forward: target oscillation every frame while + // records churn. Keyboard driving churns them CONSTANTLY (the throttle + // lever sweeps every frame of a key-hold -> a type-2 speed record per + // frame), which autodrive's pinned throttle never does -- the 'skips when + // I drive, smooth when you drive' report. Preserve the clock; keep the + // real payload (simulationState). BT_T2_CLOCK restores the old stamp (A/B). + static const int s_t2clock = getenv("BT_T2_CLOCK") ? 1 : 0; + const Time savedLastUpdate = lastUpdate; + Simulation::ReadUpdateRecord(message); + if (!s_t2clock) lastUpdate = savedLastUpdate; + } heatAlarm.SetLevel(record->heatLevel); // @0x450 <- rec+0x10 throttleState = record->throttleState; // @0x4a4 <- rec+0x14 fallDirection = record->fallDirection; // @0x4a8 <- rec+0x18 @@ -1919,7 +1967,23 @@ void case 6: // death { Mech__FallUpdateRecord *record = (Mech__FallUpdateRecord *)message; - Simulation::ReadUpdateRecord(message); + { + // CLOCK GUARD (keyboard-skip fix): the base reader stamps lastUpdate=Now() + // on EVERY record ('HACK' per SIMULATE.cpp:276). Non-pose records carry no + // pose, but that stamp shrinks the dead-reckoner's projection span + // (nextUpdate - lastUpdate) WITHOUT refreshing updateOrigin -- so the + // peer's position target jumps backward toward the stale origin, then the + // next pose record yanks it forward: target oscillation every frame while + // records churn. Keyboard driving churns them CONSTANTLY (the throttle + // lever sweeps every frame of a key-hold -> a type-2 speed record per + // frame), which autodrive's pinned throttle never does -- the 'skips when + // I drive, smooth when you drive' report. Preserve the clock; keep the + // real payload (simulationState). BT_T2_CLOCK restores the old stamp (A/B). + static const int s_t2clock = getenv("BT_T2_CLOCK") ? 1 : 0; + const Time savedLastUpdate = lastUpdate; + Simulation::ReadUpdateRecord(message); + if (!s_t2clock) lastUpdate = savedLastUpdate; + } heatAlarm.SetLevel(record->heatLevel); throttleState = record->throttleState; fallDirection = record->fallDirection; @@ -1936,7 +2000,23 @@ void case 7: // impact (fields only, no side effects) { Mech__FallUpdateRecord *record = (Mech__FallUpdateRecord *)message; - Simulation::ReadUpdateRecord(message); + { + // CLOCK GUARD (keyboard-skip fix): the base reader stamps lastUpdate=Now() + // on EVERY record ('HACK' per SIMULATE.cpp:276). Non-pose records carry no + // pose, but that stamp shrinks the dead-reckoner's projection span + // (nextUpdate - lastUpdate) WITHOUT refreshing updateOrigin -- so the + // peer's position target jumps backward toward the stale origin, then the + // next pose record yanks it forward: target oscillation every frame while + // records churn. Keyboard driving churns them CONSTANTLY (the throttle + // lever sweeps every frame of a key-hold -> a type-2 speed record per + // frame), which autodrive's pinned throttle never does -- the 'skips when + // I drive, smooth when you drive' report. Preserve the clock; keep the + // real payload (simulationState). BT_T2_CLOCK restores the old stamp (A/B). + static const int s_t2clock = getenv("BT_T2_CLOCK") ? 1 : 0; + const Time savedLastUpdate = lastUpdate; + Simulation::ReadUpdateRecord(message); + if (!s_t2clock) lastUpdate = savedLastUpdate; + } heatAlarm.SetLevel(record->heatLevel); throttleState = record->throttleState; fallDirection = record->fallDirection; @@ -1948,7 +2028,23 @@ void case 8: // airborne/reverse cycle-rate selector { Mech__AirborneUpdateRecord *record = (Mech__AirborneUpdateRecord *)message; - Simulation::ReadUpdateRecord(message); + { + // CLOCK GUARD (keyboard-skip fix): the base reader stamps lastUpdate=Now() + // on EVERY record ('HACK' per SIMULATE.cpp:276). Non-pose records carry no + // pose, but that stamp shrinks the dead-reckoner's projection span + // (nextUpdate - lastUpdate) WITHOUT refreshing updateOrigin -- so the + // peer's position target jumps backward toward the stale origin, then the + // next pose record yanks it forward: target oscillation every frame while + // records churn. Keyboard driving churns them CONSTANTLY (the throttle + // lever sweeps every frame of a key-hold -> a type-2 speed record per + // frame), which autodrive's pinned throttle never does -- the 'skips when + // I drive, smooth when you drive' report. Preserve the clock; keep the + // real payload (simulationState). BT_T2_CLOCK restores the old stamp (A/B). + static const int s_t2clock = getenv("BT_T2_CLOCK") ? 1 : 0; + const Time savedLastUpdate = lastUpdate; + Simulation::ReadUpdateRecord(message); + if (!s_t2clock) lastUpdate = savedLastUpdate; + } airborneSelect = record->airborne; // @0x3f4 <- rec+0x10 bodyTargetSpeed = record->speedDemand; // rec+0x14 } diff --git a/game/reconstructed/mechmppr.cpp b/game/reconstructed/mechmppr.cpp index ad0f3a6..91199c1 100644 --- a/game/reconstructed/mechmppr.cpp +++ b/game/reconstructed/mechmppr.cpp @@ -649,6 +649,24 @@ void key_throttle = 0.08f; // near-idle half-cycle } } + // BT_FORCE_SWEEP=: triangle-sweep the forced throttle 0.2..0.9 + // continuously -- mimics a keyboard key-hold sweeping the lever every + // frame (a type-2 speed record per frame), the keyboard-skip repro. + { + static float s_swpP = -1.0f, s_swpClock = 0.0f; + if (s_swpP < 0.0f) + { + const char *fw = getenv("BT_FORCE_SWEEP"); + s_swpP = fw ? (float)atof(fw) : 0.0f; + } + if (s_swpP > 0.0f) + { + s_swpClock += time_slice; + float ph = fmodf(s_swpClock, 2.0f * s_swpP) / s_swpP; // 0..2 + float tri = (ph < 1.0f) ? ph : (2.0f - ph); // 0..1..0 + key_throttle = 0.2f + 0.7f * tri; + } + } // BT_FORCE_FLIP=: invert the forced throttle after t sim-seconds // (the mid-stride direction-change repro). static float s_hFlip = -1.0f;