The lobby could stage a room but the launched mission never fed the members.
BTLocalConsole_InstallNetworkMission (btl4console.cpp) makes the host play the
arcade console in-process over the NetTransport seam (Winsock TCP or Steam SDR):
- connect to each member's console channel (ip[:port] from BT412HOSTPODS);
- feed each the chunked egg (NetworkManager::ReceiveEggFileMessage, \n->NUL
wire image like tools/btconsole.py), resending until it ACKs;
- poll member state (StateQueryMessage); when the whole mesh is staged at
WaitingForLaunch, dispatch RunMission to every member + locally at once;
- StopMission at expiry (members first, then self after a short grace);
- scores are BT's kills/deaths snapshotted from the *meshed* roster at the
stop -- no EndMission wire intake (that flow is a BT stub; the mesh already
put every pilot in the host's roster). The owner's own pod is fed locally
via L4NetworkManager::FeedLocalEgg.
Engine change (ported 1:1 from RP412): gConsoleMarshalsLaunch (APPMGR.h/.cpp) +
the `!gConsoleMarshalsLaunch &&` guard in APP.cpp's WaitingForLaunch self-launch.
The owner has no console connection to itself, so without this it would
self-launch before the mesh staged and never send the coordinated RunMission.
Default False = stock behavior; solo boot un-regressed.
WinMain host path (btl4main.cpp) now honors BT412HOSTPODS (+BT412HOSTPORT) for
the lobby host AND a classic-LAN host: SetNetworkCommonFlatAddress +
InstallNetworkMission, falling back to a solo marshal if no member is reachable.
Verified (loopback): two `btl4.exe -net` pods fed by tools/btconsole.py reach
"All connections completed!" and run after the engine change -- the exact
protocol + launch handshake the marshal uses. Both gates build+link; the
Release dist boots and the Steam transport comes up. The live multi-machine
Steam mission (FakeIP mesh + pilot-slot matching) is untestable here -- see the
new docs/STEAM-3-MACHINE-TEST.md. Dist README updated: multiplayer is now
"newly implemented, please test" rather than deferred.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.2 KiB
Steam multiplayer — multi-machine test procedure (BattleTech 4.12)
The full end-to-end: two or three machines, that many Steam accounts, one lobby, one marshaled mission over Steam Datagram Relay (SDR). Adapted from RP412's procedure for BattleTech's kills/deaths model.
Status: the host→member mission feed (
BTLocalConsole_InstallNetworkMission) is implemented. The console protocol + launch handshake it uses are loopback-verified (two-netpods fed bytools/btconsole.pymesh and run after the engine'sgConsoleMarshalsLaunchlaunch-hold change). The full multi-machine Steam mesh has not been lab-tested — this procedure IS that test. If a step diverges, collect the logs (bottom of this doc).
Per machine (all of them)
- Steam client installed, logged in (a different account per machine), and RUNNING.
- Copy the
dist\folder onto the machine. (It already containssteam_appid.txt=480— Valve's Spacewar public dev AppID — and anenviron.iniwithBT412STEAM=1.) - Run
start.bat.
Sanity check per machine: btl4.log (beside the exe) should show
SteamNetTransport: up as 169.254.x.y (fake ports N console, M game) and
[steam] transport up -- lobby available. If it says "staying on TCP", Steam
isn't running / not logged in / steam_appid.txt is missing.
The test
- Machine A (host): on the setup menu pick a map, mech, color, time,
weather, length, and pilot name, then click HOST STEAM MISSION. The lobby
room appears with A listed as
[HOST]. - Machines B (and C): pick loadouts on the setup menu FIRST (the lobby publishes them), then click JOIN STEAM MISSION. It joins the first open BT412 lobby it finds.
- When every member's name shows bright in A's room (bright = FakeIP + loadout published), A clicks L A U N C H M I S S I O N.
- All pods leave the room and boot the mission. Expect in the logs:
- host A:
[marshal] network mission, N remote pod(s) connected - host A:
[marshal] connecting to pod 169.254...then[marshal] egg sent to 169.254... (K chunks)and[marshal] <addr> EGG ACK (mesh complete)per member - host A:
[marshal] all pods staged -- RUN - every pod:
All connections completed!then the mission runs
- host A:
- The mission runs for the length A picked; at expiry A's marshal snapshots the
roster, stops every member, then stops itself
(
[marshal] time expired -- stopping remote pods). A gets the MISSION COMPLETE results (kills/deaths); the sheet is published to the lobby so B/C show the same board. - Everyone lands back on a fresh setup menu (the single-binary relaunch — the lobby ROOM does not persist across the relaunch in this build; re-HOST/JOIN for the next mission).
Addressing (how the mesh is wired)
- Every pod uses engine ports 1501 console / 1502 game by convention under Steam; the transport maps them to the Steam-assigned fake ports per peer.
- The lobby exchanges each member's FakeIP + fake console/game ports live
through lobby member data (
ip/cp/gp); nothing is configured by hand. - On LAUNCH the host writes the nonced
goroster into lobby data; every pod callsSteamNetTransport_RegisterPeerfor all peers before dialing out. - The host bakes every member into the mission egg's
[pilots]list (<fakeip>:1502), so each pod finds its own slot in the shared mission.
Controllers: disable Steam Input (once per machine)
Steam Input intercepts Xbox controllers for AppID 480 the moment the game initializes SteamAPI, hiding them from XInput — the pad works everywhere else and is invisible only in-game. Fix, either scope:
- Per game: Steam Library → Spacewar → Properties → Controller → Override: Disable Steam Input.
- Global: Steam → Settings → Controller → uncheck Enable Steam Input for Xbox controllers.
PadRIO re-probes every ~3 s, so the pad hot-connects the moment Steam releases it — no restart needed. (BT412's own AppID will set this server-side; test machines need the client setting.)
Notes and knowns
- Two instances under ONE Steam account cannot mesh (one identity per account) — hence separate machines/accounts.
- If a member joins after A already launched once, it answers the NEXT launch only (launch signals are nonced).
- Same-LAN machines may get a direct SDR route; different networks relay. Both are fine.
- Known MP fidelity gaps carried from single-player (peer warp spheres, missile
clusters, PNAME/PLACE billboards) do not block the connectivity test — log
observations in
context/multiplayer.md.
No-Steam fallback (proven, for isolating a Steam-specific problem)
Set BT412STEAM=0 and drive the pods with the external console emulator over
TCP (needs Python 3):
Machine A: btl4.exe -egg MP.EGG -net 1501
Machine B: btl4.exe -egg MP.EGG -net 1601
Feeder: python tools\btconsole.py MP.EGG <A_ip>:1501 <B_ip>:1601
If this works but the Steam path does not, the problem is in the Steam transport / lobby layer, not the game mesh.
What to collect if something breaks
btl4.log from each machine (each dist folder) — grep [steam], [frontend],
[marshal], net-tx / net-rx — plus which step above diverged.