Net: host->member mission marshal -- a Steam/LAN mission plays end to end

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>
This commit is contained in:
Cyd
2026-07-17 07:30:17 -05:00
co-authored by Claude Opus 4.8
parent a92e3a5780
commit 77f019ed4f
11 changed files with 630 additions and 73 deletions
+35 -7
View File
@@ -548,6 +548,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
extern int BTFrontEnd_LastLaunchMode();
extern void BTFrontEnd_ShowResults();
extern void BTLocalConsole_Install(int mission_seconds);
extern int BTLocalConsole_InstallNetworkMission(int mission_seconds,
const char *egg_path, const char *remote_pod_list);
extern int BTLocalConsole_MissionCompleted();
extern void BTLobby_PushMissionResults();
extern void BTLobby_PullMissionResults();
@@ -580,16 +582,42 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
std::cout << "[frontend] lobby MEMBER -> network pod on :1501"
<< std::endl << std::flush;
}
else if (getenv("BT412HOSTPODS") != 0 && getenv("BT412HOSTPODS")[0] != '\0')
{
// HOST (lobby OR classic LAN): mesh on the session port AND play the
// console for the member pods over the wire. BT412HOSTPODS is their
// console channels (ip[:port]); the lobby primes it, or a LAN host
// sets it in environ.ini. BT412HOSTPORT overrides this pod's port
// (default 1501). The marshal connects, feeds each member its egg,
// launches the mesh together, and stops at expiry.
const char *host_pods = getenv("BT412HOSTPODS");
int host_port = 1501;
const char *port_override = getenv("BT412HOSTPORT");
if (port_override != 0 && atoi(port_override) > 0)
host_port = atoi(port_override);
L4Application::SetNetworkCommonFlatAddress(host_port);
if (BTLocalConsole_InstallNetworkMission(secs, "frontend.egg", host_pods))
{
std::cout << "[frontend] HOST -> network mission (" << secs
<< "s) on :" << host_port << ", pods=" << host_pods
<< std::endl << std::flush;
}
else
{
// no reachable members: fall back to a solo mission on this pod
L4Application::SetNetworkCommonFlatAddress(0);
BTLocalConsole_Install(secs);
std::cout << "[frontend] HOST setup failed -> solo ("
<< secs << "s)" << std::endl << std::flush;
}
}
else
{
// SOLO or LOBBY HOST: the egg was built locally and the in-process
// marshal owns the mission clock (the host is also the console). A
// host additionally joins the mesh on the session port.
if (launch_mode == 1)
L4Application::SetNetworkCommonFlatAddress(1501);
// SOLO: the egg was built locally; the in-process marshal owns the
// mission clock and stops it at the chosen length.
BTLocalConsole_Install(secs);
std::cout << "[frontend] menu LAUNCH (" << (launch_mode == 1 ? "host" : "solo")
<< ") -> marshal armed (" << secs << "s)" << std::endl << std::flush;
std::cout << "[frontend] menu LAUNCH (solo) -> marshal armed ("
<< secs << "s)" << std::endl << std::flush;
}
}