Merge origin/master: the D1 relay/operator line + input remap meet the glass layer

33 master commits in (relay TCP/UDP + PySide6 operator console, CONTROLS.MAP
+XInput binding engine, camera seats, torso pitch aim, sign fixes, the 1995
manual, version stamping, 18-mech certification).  Conflicts: .gitignore +
CLAUDE.md router rows (combined).

SEMANTIC RECONCILIATION (the one real overlap): masters btinput binding
engine (ungated, CONTROLS.MAP) and the glass PadRIO (gated, bindings.txt)
would both read the keyboard/pad in a glass+PAD session.  btinput now joins
the stand-down convention: BTInputPoll yields (and BTInputSuppressKey claims
NOTHING, so authentic hotkeys flow) when an operational cockpit device owns
the input path -- BTRIODevicePresent, BT_KEY_BRIDGE force-override honored,
forced harness exempt.  One input system per mode: btinput on pod/dev
desktops, PadRIO on glass.  The mechmppr/mech4 bridge merges composed clean
(masters negate-once sign fix inside our device-gated bridge).  The D1 relay
keeps its own raw sockets by design (an alternative LAN wire; Steam and
relay are separate modes).

Verified post-merge: all 3 configs build; glass boots with [input] binding
engine standing down + PadRIO owning input (30 ticks); pod forced-walk
speedDemand=61.501 with btinput ACTIVE; 2-node loopback MP full 31/31
mission, 76/76 ticks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-18 17:46:41 -05:00
co-authored by Claude Fable 5
48 changed files with 7234 additions and 141 deletions
+48 -4
View File
@@ -641,7 +641,13 @@ void
{
float key_throttle = gBTDrive.forced
? gBTDrive.forcedThrottle : gBTDrive.throttle;
float key_turn = gBTDrive.forced ? 0.0f : gBTDrive.turn;
// PORT SIGN (user live-verified 2026-07-18: left arrow turned the
// mech RIGHT): the sim's positive turnDemand is CCW (LEFT) -- math
// convention, same as the torso twist. On the pod the RIO Ranger
// owned the hardware sign; the desktop bridge must negate so that
// input-right (positive) = turn RIGHT. Forced/BT_GOTO harness
// demands are sim-frame already and stay un-negated.
float key_turn = gBTDrive.forced ? 0.0f : -gBTDrive.turn;
// Headless harness (forced mode only): BT_FORCE_TURN holds a steering
// demand; BT_FORCE_SECONDS releases the forced throttle after n
// sim-seconds while KEEPING the turn (the turn-in-place repro).
@@ -760,6 +766,14 @@ void
gBTModeCycle = 0;
CycleControlModeNow();
}
// TORSO ELEVATION (pitch): the pod stick's Y axis -- every
// control mode routes stickPosition.y into
// Torso::SetAnalogElevationAxis below, so the bridge feeds
// it in BOTH branches (keys R/F or a pad stick via btinput).
{
extern float gBTElevAxis;
stickPosition.y = gBTElevAxis;
}
if (controlMode == BasicMode)
{
stickPosition.x = key_turn;
@@ -767,7 +781,14 @@ void
}
else
{
stickPosition.x = gBTTwistAxis; // the torso axis
// PORT SIGN: positive twist is CCW (left) like the turn;
// negate so stick/E-key right = torso right (manual p8:
// "pulling your joystick to the right torso twists your
// 'Mech to the right"). The earlier "MadCat twist was
// correct" call was based on the same misread screenshot
// forensics as the turn -- the user's live left-arrow
// test invalidated both.
stickPosition.x = -gBTTwistAxis; // the torso axis
pedalsPosition = key_turn; // A/D = the pedals
// 'X' recenter pulse (2026-07-13): one-frame centerCommand
// -> the sim arms recenterActive and Recenter (@004b6918)
@@ -792,7 +813,8 @@ void
}
}
}
stickPosition.y = 0.0f;
// (stickPosition.y no longer zeroed here -- the bridge above
// feeds the elevation axis every bridged frame, 2026-07-18)
}
}
@@ -863,6 +885,18 @@ void
{
torso->SetAnalogElevationAxis(stick_y); // raw torso+0x1f4 (500)
torso->SetAnalogTwistAxis(0.0f); // raw torso+0x1f0
if (getenv("BT_INPUT_LOG"))
{
static float s_eacc2 = 0.0f; s_eacc2 += time_slice;
if (s_eacc2 >= 0.5f) { s_eacc2 = 0.0f;
YawPitchRoll ypr;
ypr = mech->localOrigin.angularPosition;
DEBUG_STREAM << "[input] mppr elev in=" << stick_y
<< " torsoElev=" << torso->CurrentElevation()
<< " vel=" << torso->ElevationVelocity()
<< " mechYaw=" << (Scalar)ypr.yaw
<< "\n" << std::flush; }
}
}
if (cockpit)
{
@@ -887,6 +921,8 @@ void
//
if (torso == 0 || !torso->GetHorizontalEnabled()) // raw torso+0x250 == 0
{
// (sign handled ONCE at the key bridge: positive sim twist/slew
// = CCW/left; the bridge negates so input-right = right)
if (cockpit) cockpit->SetFreeAimSlew(stick_x); // raw cockpit+0x28c
}
else
@@ -894,6 +930,14 @@ void
torso->SetAnalogTwistAxis(stick_x); // raw torso+0x1f0
}
if (torso) torso->SetAnalogElevationAxis(stick_y); // raw torso+0x1f4
if (torso && getenv("BT_INPUT_LOG"))
{
static float s_tacc = 0.0f; s_tacc += time_slice;
if (s_tacc >= 0.5f) { s_tacc = 0.0f;
DEBUG_STREAM << "[input] twist in=" << stick_x
<< " currentTwist=" << torso->CurrentTwist()
<< "\n" << std::flush; }
}
turnDemand = (pedal_3 == 0.0f) ? 0.0f : pedal_3;
@@ -914,7 +958,7 @@ void
//
if (torso == 0 || !torso->GetHorizontalEnabled())
{
if (cockpit) cockpit->SetFreeAimSlew(stick_x);
if (cockpit) cockpit->SetFreeAimSlew(stick_x); // (sign at the bridge)
}
else
{