Miniconsole: the front end + LocalConsole marshal -- self-launched missions (step 3)

NEW gated dir game/glass/: btl4fe (green-on-black GDI catalog menu -- map/
time/weather/length/mech/color/pilot/mode/port/peers; egg writer emitting the
FULL console egg shape, golden-tested vs MP.EGG: identical section sequence
at identical line offsets, the 4 static ordinal pages verbatim, GDI-rendered
128x32/64x16 pilot-name plasma bitmaps in MP.EGG framing) and btl4console
(the marshal: a worker-thread console CLIENT speaking the btconsole.py wire
protocol verbatim -- 1040-byte chunked egg, 20s settle, RunMission x2,
STAY CONNECTED, own the clock, StopMission at expiry; logs marshal.log).
The engine sees a real connected console and runs the 100% stock ladder --
NO engine hooks (less invasive than planned: the L4APP.H setters proved
unnecessary; launches go through real argv on relaunch).

WinMain (one gated block): zero-mission-arg glass launch -> menu -> relaunch
self with the mission argv (per-mission process relaunch; BT_FE_* env arms
the marshal); post-RunMissions BT_FE_LOOP tail returns to the menu.  Menu
relaunches CLEAR the BT_FE_* handoff (found live: the inherited env re-armed
a marshal instead of showing the menu).

Verified end-to-end (synthetic menu drive): menu -> frontend.egg -> relaunch
-> marshal feeds self over loopback -> stock console ladder -> mission RUNS
(58+ ticks) -> 60s clock -> StopMission -> clean menu return, no env leak.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-17 23:33:39 -05:00
co-authored by Claude Fable 5
parent 82b53b7ad7
commit c4ca87b808
6 changed files with 1342 additions and 0 deletions
+81
View File
@@ -110,6 +110,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
#ifdef BT_GLASS
#include "btl4fe.hpp"
#include "btl4console.hpp"
#endif
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
// Heap-corruption hunt (env BT_HEAPCHECK=1; default OFF -- ~100x slower): validate
@@ -328,6 +333,67 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
: "DEV (single window + keyboard)")
<< std::endl << std::flush;
#ifdef BT_GLASS
//
// MINICONSOLE FRONT END (glass step 3): a zero-mission-arg launch (no
// -egg, no -net) runs the menu INSTEAD of the game, then RELAUNCHES
// this exe with the chosen mission's real arguments -- the per-mission
// process-relaunch pattern (reconstructed gBT* globals do not survive
// in-process re-init). BT_FE_* env vars arm the mission process's
// LocalConsole marshal; BT_FE_LOOP returns post-mission exits to the
// menu. The console-wire ladder itself is 100% stock -- the engine
// sees a real connected console (btl4console.cpp).
//
{
int fe_has_egg = lpCmdLine && strstr(lpCmdLine, "-egg") != NULL;
int fe_has_net = lpCmdLine && strstr(lpCmdLine, "-net") != NULL;
if (!fe_has_egg && !fe_has_net && getenv("BT_FE_EGG") == NULL)
{
BTFeLaunchSpec fe_spec;
if (BTFrontEnd_Run(&fe_spec) != 0 || fe_spec.mode == BTFeLaunchNone)
{
return 0; // quit from the menu
}
char fe_arguments[192];
char fe_value[64];
SetEnvironmentVariableA("BT_FE_LOOP", "1");
SetEnvironmentVariableA("BT_PLATFORM", "glass");
switch (fe_spec.mode)
{
case BTFeLaunchRawSolo:
SetEnvironmentVariableA("BT_FE_EGG", NULL);
sprintf(fe_arguments, "-egg %s -platform glass", fe_spec.eggPath);
break;
case BTFeLaunchJoinLan:
SetEnvironmentVariableA("BT_FE_EGG", NULL);
sprintf(fe_arguments, "-net %d -platform glass", fe_spec.consolePort);
break;
default: // Solo / HostLan: the marshal armed via env handoff
SetEnvironmentVariableA("BT_FE_EGG", fe_spec.eggPath);
SetEnvironmentVariableA("BT_FE_PODS", fe_spec.podList);
sprintf(fe_value, "%d", fe_spec.missionSeconds);
SetEnvironmentVariableA("BT_FE_SECS", fe_value);
sprintf(fe_arguments, "-net %d -platform glass", fe_spec.consolePort);
break;
}
BTFE_RelaunchSelfAndExit(fe_arguments); // never returns
}
//
// A marshal-armed mission process: start the LocalConsole worker
// (it retries until our own console listener is up).
//
const char *fe_egg = getenv("BT_FE_EGG");
if (fe_egg != NULL && fe_egg[0])
{
const char *fe_pods = getenv("BT_FE_PODS");
const char *fe_secs = getenv("BT_FE_SECS");
BTLocalConsole_Start(fe_egg,
fe_pods ? fe_pods : "",
fe_secs ? atoi(fe_secs) : 600);
}
}
#endif
// DEBUG (bring-up, default OFF): force the player mech to walk full-ahead with
// no keypress so locomotion + camera-follow can be verified in a headless run.
// BT_FORCE_THROTTLE may carry a VALUE: +1 full run, ~0.3 walk, -1 reverse (default 1.0).
@@ -508,6 +574,21 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
app_manager->RunMissions();
std::cout << "[boot] RunMissions returned (mission loop exited)." << std::endl << std::flush;
#ifdef BT_GLASS
//
// Front-end loop (glass step 3): a menu-launched mission returns
// to the menu on exit. (Marshal missions normally relaunch from
// the marshal thread at clock expiry; this tail covers raw-solo
// and early exits.)
//
if (getenv("BT_FE_LOOP") != NULL)
{
std::cout << "[fe] mission over -- relaunching the menu"
<< std::endl << std::flush;
BTFE_RelaunchSelfAndExit("");
}
#endif
btl4App = NULL;
Unregister_Object(app_manager);
delete app_manager;