one log per day, and every log says who it sent it -- rotation was eating the crash stacks

Conn Man crashed twice in an Owens on 2026-07-27, kept playing, and by the time
we asked for his logs they no longer existed.  Not a mystery: every launcher did

    if exist content\X.old.log del content\X.old.log
    if exist content\X.log     ren content\X.log X.old.log

which keeps TWO generations.  He relaunched more than twice, so his own bat
deleted both crash sessions.  The player who crashes is exactly the player who
relaunches immediately, so the retention window was aimed at the wrong case.

THE LOG.  BT_LOG set still means "use this path verbatim" -- that contract is
load-bearing (mp_a.log/mp_b.log for 2-node runs from one cwd, btoperator's
operator_N.log, every scratchpad/mp_*.sh that greps the file it named) and is
untouched.  Only when BT_LOG is UNSET does the exe now name the file itself:
content\<stem>_YYYYMMDD.log, appended, one per day, no rotation window to fall
out of.  The stem (solo/join/steam/joyconfig) comes from the gates the bat
already sets, so solo still cannot overwrite the MULTIPLAYER log.

Self-named files append UNCONDITIONALLY.  The default open mode is ios::out --
TRUNCATE -- and the operator GUI's exported bats never set BT_LOG_APPEND, so
they were truncating already; leaving append implicit would have let the second
launch of the day erase the morning.

IDENTITY, because these arrive from many players at once and Discord renames
half of them to message.txt (most of the night-5 evidence had to be
re-identified by hand):

    ===== BT411 SESSION  build=4.11.603 (d64d75f+)  machine=...  user=...
          callsign=Conn Man  mode=solo  pid=13192  local=...  log=... =====

Also the session separator inside a per-day file, and it puts build+hash ABOVE
any [crash] block so btl4+0xNNNN offsets stay matchable to a PDB across builds.

SIZE.  Never delete, but stay sendable: past 8 MB the next launch rolls to
<stem>_YYYYMMDD.1.log.  Checked only at open, so a live session is never split.

CONSOLIDATION.  launch_report.txt is gone, folded into lastrun_<stem>.txt (one
launch record: the exe appends its block at first breath, the bat appends the
exit line).  Per-stem because one shared lastrun.txt let a second launcher's
`del` destroy the first launch's block.  Its ABSENCE after a run is now the
#41 "never reached WinMain" probe -- the old "if not exist X.log" test cannot
work against a per-day file, which survives earlier launches, and would have
reported every healthy run as blocked by antivirus.  marshal.log folded into
the day log too; it keeps its own handle and gained a CRITICAL_SECTION because
it runs on a worker thread while the engine log stream is single-threaded.
play_steam.bat gained a launch bracket it never had -- which is why a Steam
player killed before WinMain previously left no evidence at all.

_putenv_s, not SetEnvironmentVariableA, to pin the resolved name: MSVC's CRT
keeps its own environment copy, so getenv() in-process does NOT see a
SetEnvironmentVariableA write (measured).  btl4console/btl4lobby resolve the
day log via getenv, so with the Win32-only call they silently fell back to
marshal.log and the fold never happened.

logfile.open() is now checked -- a failed open used to rebind cout to a dead
buffer, silently dropping the header, [boot] and any crash stack while
lastrun still claimed log=<name>.

Verified: append across sessions with distinct pids, crash block landing under
its own header, BT_LOG verbatim unmangled, an inherited BT_LOG cleared by the
bats, roll-over at 8 MB, and both forensic verdicts (exe ran / exe blocked).
Console auto-retrieval is unaffected -- it uploads the matchlog, a different
file, and a crashed round never reaches the upload call anyway.

Known gaps, deliberately not in this commit: the setlocal hoist for gate
leakage when two bats share one console (dev-only), and btoperator's exported
bats still lack the lastrun bracket -- do not press Export on a shipped zip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-07-28 10:33:07 -05:00
co-authored by Claude Opus 5
parent d64d75fcf7
commit 1777d5a62e
13 changed files with 384 additions and 86 deletions
+5
View File
@@ -11,6 +11,11 @@
# logs / debugger dumps # logs / debugger dumps
*.log *.log
cdb_*.txt cdb_*.txt
# per-launch forensic breadcrumb (btl4main.cpp) + the launch_report.txt it
# replaces -- runtime artifacts, one per machine, never committed
content/lastrun_*.txt
content/launch_report.txt
content/volume.cfg
# editor / OS # editor / OS
.vs/ .vs/
+2 -1
View File
@@ -381,7 +381,8 @@ operator control link over a private tunnel (mesh VPN).
|---|---| |---|---|
| `content/operator_relay.log` | the relay's own stdout, teed by the GUI. **Truncated at each session start** — so a Start Session destroys the evidence of the wedge it is being used to clear. Copy it first when diagnosing. | | `content/operator_relay.log` | the relay's own stdout, teed by the GUI. **Truncated at each session start** — so a Start Session destroys the evidence of the wedge it is being used to clear. Copy it first when diagnosing. |
| `matchlogs/<peer>_<name>` | per-pod match reports, auto-uploaded on route -9 at each clean round end (relay modes only; a mid-round crash loses it) | | `matchlogs/<peer>_<name>` | per-pod match reports, auto-uploaded on route -9 at each clean round end (relay modes only; a mid-round crash loses it) |
| `content/join.log` | the pod's own log; **never** uploaded, only on the player's machine | | `content/join_YYYYMMDD.log` | the pod's own log — ONE file per day, appended, every session headed by `===== BT411 SESSION … =====`; **never** uploaded, only on the player's machine. Rotation was removed 2026-07-28: two generations used to be deleted by the player's own relaunches, which is how the 2026-07-27 Owens crash stacks were lost. |
| `content/lastrun_<mode>.txt` (mode = solo/join/steam/joyconfig) | the launch record (exe's first-breath block + the bat's exit line). Its ABSENCE after a run means the exe never reached WinMain — loader failure or antivirus (Gitea #41). Replaced `launch_report.txt`. |
Every relay log line is wall-clock stamped (`_StampedOut`) so post-mortems can measure delays. Every relay log line is wall-clock stamped (`_StampedOut`) so post-mortems can measure delays.
The GUI's monitor regexes all use `.search()`, so the prefix is transparent to them. The GUI's monitor regexes all use `.search()`, so the prefix is transparent to them.
+2 -1
View File
@@ -241,7 +241,8 @@ not need to ask players to send anything.
Exceptions worth knowing: a client that **crashes mid-round** loses its report, and players on Exceptions worth knowing: a client that **crashes mid-round** loses its report, and players on
**solo** or **Steam** have no relay to upload through. In those cases ask them for **solo** or **Steam** have no relay to upload through. In those cases ask them for
`content\matchlog_*.txt` directly. Their crash log, `content\join.log`, is **never** uploaded — ask `content\matchlog_*.txt` directly. Their crash log `content\join_YYYYMMDD.log`, one file per day
holding every session (plus the tiny `content\lastrun_<mode>.txt`) — is **never** uploaded — ask
for it explicitly when something went wrong on their end. for it explicitly when something went wrong on their end.
--- ---
+199 -9
View File
@@ -562,17 +562,159 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
} }
// Redirect the engine's std::cout/DEBUG_STREAM to a log file. BT_LOG names the // Redirect the engine's std::cout/DEBUG_STREAM to a log file. TWO rules,
// file (default btl4.log) -- REQUIRED for multi-instance runs from one cwd // in this order (2026-07-28):
// (instance 2 would truncate instance 1's log). //
// BT_LOG SET -> used VERBATIM, append per BT_LOG_APPEND. This is the
// documented multi-instance contract and MUST NOT change:
// mp_a.log/mp_b.log for 2-node runs from one cwd, the
// operator GUI's rotated operator_N.log, and every
// scratchpad/mp_*.sh harness that greps the file it named.
// BT_LOG UNSET -> the exe names a PER-DAY file itself and ALWAYS appends:
// <stem>_YYYYMMDD.log.
//
// WHY the per-day file: the launcher used to rotate (del *.old.log, then
// ren *.log *.old.log), keeping only TWO generations. On 2026-07-27 a
// player crashed twice in an Owens, kept playing, and his own relaunches
// deleted both crash sessions before anyone could ask for them -- the
// stacks are gone permanently. A per-day file has no rotation window to
// fall out of. The stem preserves the launcher identity the .bat used to
// supply; every gate below is set before the log opens and is inherited
// across the menu->mission relaunch.
// The STEM identifies which launcher started us. Hoisted out of the naming
// block because the launch record (lastrun_<stem>.txt) uses it too: one
// shared lastrun.txt let a second bat's `del` destroy the first launch's
// block, and file the first's exit code under the wrong pid.
char logStem[32];
lstrcpynA(logStem, "btl4", sizeof(logStem));
if (getenv("BT_JOYCONFIG")) lstrcpynA(logStem, "joyconfig", sizeof(logStem));
else if (getenv("BT_STEAM_NET")) lstrcpynA(logStem, "steam", sizeof(logStem));
else if (getenv("BT_FE_SOLO")) lstrcpynA(logStem, "solo", sizeof(logStem));
else if (getenv("BT_FE_JOIN") || getenv("BT_RELAY")) lstrcpynA(logStem, "join", sizeof(logStem));
char resolvedLog[MAX_PATH];
int logSelfNamed = 0;
{
const char *env_log = getenv("BT_LOG");
if (env_log != NULL && *env_log != '\0')
{
lstrcpynA(resolvedLog, env_log, MAX_PATH);
}
else
{
SYSTEMTIME lt;
GetLocalTime(&lt); // NOT cmd's %DATE%: locale-ordered, and
// on en-US it contains '/' (path chars).
// SIZE ROLL-OVER -- never deletes anything, but keeps each FILE small
// enough to actually send. A playtest evening concatenates into one
// file (4.3 MB from a single session in scratchpad/night5/), and the
// channel this evidence travels through is a chat attachment. Part 0
// is <stem>_YYYYMMDD.log; once it passes the cap the NEXT launch opens
// <stem>_YYYYMMDD.1.log, and so on. Checked only at open, so one very
// long session can still overrun -- that is deliberate: never split a
// live session across files.
const DWORD part_max = 8UL * 1024UL * 1024UL;
for (int part = 0; part < 1000; ++part)
{
if (part == 0)
wsprintfA(resolvedLog, "%s_%04d%02d%02d.log",
logStem, (int)lt.wYear, (int)lt.wMonth, (int)lt.wDay);
else
wsprintfA(resolvedLog, "%s_%04d%02d%02d.%d.log",
logStem, (int)lt.wYear, (int)lt.wMonth, (int)lt.wDay, part);
WIN32_FILE_ATTRIBUTE_DATA fad;
if (!GetFileAttributesExA(resolvedLog, GetFileExInfoStandard, &fad))
break; // does not exist yet -- use it
if (fad.nFileSizeHigh == 0 && fad.nFileSizeLow < part_max)
break; // still room to append
}
logSelfNamed = 1;
}
}
std::ofstream logfile; std::ofstream logfile;
// BT_LOG_APPEND=1: keep prior generations' lines (the lobby-loop // A self-named day file appends UNCONDITIONALLY -- the default open mode is
// relaunch truncates the shared BT_LOG otherwise -- multi-generation // std::ios::out (TRUNCATE), so relying on the caller to pass BT_LOG_APPEND
// forensics need the whole trail). // would let the second launch of the day erase the morning. (The operator
logfile.open(getenv("BT_LOG") ? getenv("BT_LOG") : "btl4.log", // GUI's exported bats do not set it and truncate today.)
(getenv("BT_LOG_APPEND") != NULL && *getenv("BT_LOG_APPEND") == '1') logfile.open(resolvedLog,
(logSelfNamed
|| (getenv("BT_LOG_APPEND") != NULL && *getenv("BT_LOG_APPEND") == '1'))
? (std::ios::out | std::ios::app) : std::ios::out); ? (std::ios::out | std::ios::app) : std::ios::out);
std::cout.rdbuf(logfile.rdbuf()); // If the log cannot be opened, DO NOT silently rebind cout to a dead buffer:
// the header, [boot] and any crash stack would vanish while lastrun still
// claimed a log=<name>. Fall back to a per-pid name, and if even that fails
// leave cout alone so the lines still go somewhere.
if (!logfile.is_open())
{
char fallback[MAX_PATH];
wsprintfA(fallback, "%s_pid%u.log", logStem, (unsigned)GetCurrentProcessId());
logfile.open(fallback, std::ios::out | std::ios::app);
if (logfile.is_open())
lstrcpynA(resolvedLog, fallback, MAX_PATH);
}
if (logfile.is_open())
std::cout.rdbuf(logfile.rdbuf());
// Pin the RESOLVED name (and append mode) into the environment so the
// menu->mission relaunch inherits it verbatim instead of re-deriving it.
// Without this a session started at 23:50 would re-resolve after midnight
// and split across two files -- the fragmentation this change exists to fix.
//
// _putenv_s, NOT SetEnvironmentVariableA. MSVC's CRT keeps its OWN copy of
// the environment; SetEnvironmentVariableA updates only the Win32 block, so
// a later getenv() IN THIS PROCESS still returns the old value (measured:
// getenv reads NULL immediately after a successful SetEnvironmentVariableA).
// That matters because btl4console.cpp/btl4lobby.cpp resolve the day log via
// getenv("BT_LOG") -- with SetEnvironmentVariableA they silently fell back to
// marshal.log and the fold never happened. _putenv_s updates BOTH the CRT
// copy and the Win32 block, so in-process getenv AND the inherited
// environment of the menu->mission relaunch both see it.
//
if (logSelfNamed)
{
_putenv_s("BT_LOG", resolvedLog);
_putenv_s("BT_LOG_APPEND", "1");
}
// SESSION HEADER -- the log must identify ITSELF, because attachments lose
// their filename in transit (Discord renames pasted logs to message.txt;
// most of the 2026-07-27 evidence arrived that way and had to be
// re-identified by hand). It doubles as the session separator inside a
// per-day file, and it puts build+pid ABOVE any [crash] block so the
// btl4+0xNNNN offsets stay matchable to the right PDB.
{
SYSTEMTIME lt;
GetLocalTime(&lt);
char machine[MAX_COMPUTERNAME_LENGTH + 1] = "?";
DWORD machine_len = sizeof(machine);
GetComputerNameA(machine, &machine_len);
std::cout << "\n===== BT411 SESSION"
<< " build=" << BT_VERSION_FULL // FULL = version + commit hash:
// a never-rotated day file spans
// builds, and btl4+0xNNNN offsets
// are meaningless without the exact
// build to match a PDB against.
// WHO and WHICH BOX -- the operator receives many of these from many
// players at once, often renamed in transit, so a file must be
// attributable from its CONTENTS alone. callsign is the name the
// operator actually knows a player by; user/machine survive even when
// no callsign has been chosen yet (menu, solo, joyconfig).
<< " machine=" << machine
<< " user=" << (getenv("USERNAME") ? getenv("USERNAME") : "?")
<< " callsign=" << (getenv("BT_CALLSIGN") && *getenv("BT_CALLSIGN")
? getenv("BT_CALLSIGN") : "-")
<< " mode=" << logStem
<< " pid=" << (unsigned)GetCurrentProcessId()
<< " local=" << lt.wYear << "-"
<< (lt.wMonth < 10 ? "0" : "") << lt.wMonth << "-"
<< (lt.wDay < 10 ? "0" : "") << lt.wDay << " "
<< (lt.wHour < 10 ? "0" : "") << lt.wHour << ":"
<< (lt.wMinute< 10 ? "0" : "") << lt.wMinute << ":"
<< (lt.wSecond< 10 ? "0" : "") << lt.wSecond
<< " log=" << resolvedLog
<< " =====" << std::endl << std::flush;
}
if (getenv("BT_CRASHTEST") != NULL && *getenv("BT_CRASHTEST") == '1') if (getenv("BT_CRASHTEST") != NULL && *getenv("BT_CRASHTEST") == '1')
{ {
@@ -590,6 +732,54 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
<< (getenv("BT_FE_LOOP") ? " (relaunched generation)" : " (first process)") << (getenv("BT_FE_LOOP") ? " (relaunched generation)" : " (first process)")
<< std::endl << std::flush; << std::endl << std::flush;
// LAUNCH BREADCRUMB -- replaces launch_report.txt (Gitea #41 forensics).
// The .bat deletes this file before launching and tests for it afterwards:
// PRESENT means the exe reached WinMain, ABSENT means it was blocked before
// any of our code ran (loader failure / antivirus). This REPLACES the old
// "if not exist <log>" probe, which a per-day log silently breaks -- the
// file survives from earlier launches, so the probe could never fire again
// and every healthy run would be reported as blocked. It also records
// which day-log this launch actually wrote to, which the .bat cannot derive
// itself (cmd has no locale-safe date).
// APPEND, never truncate: the .bat deletes this file before launching and
// writes its own "launching" line into it FIRST, so truncating here would
// erase the outside half of the bracket. One file carries the whole launch
// record (bat -> exe -> bat), which is why launch_report.txt is gone.
// PER-STEM name (lastrun_solo.txt, lastrun_join.txt, ...): one shared
// lastrun.txt meant a second launcher's `del` wiped the first launch's block
// and filed its exit code under the wrong pid. Each bat now deletes and
// probes only its OWN record. Self-identifying for the same reason the log
// header is: these arrive from many players, often renamed.
{
SYSTEMTIME lt;
GetLocalTime(&lt);
char machine[MAX_COMPUTERNAME_LENGTH + 1] = "?";
DWORD machine_len = sizeof(machine);
GetComputerNameA(machine, &machine_len);
char lastrun_name[MAX_PATH];
wsprintfA(lastrun_name, "lastrun_%s.txt", logStem);
std::ofstream breadcrumb(lastrun_name, std::ios::out | std::ios::app);
if (breadcrumb.is_open())
{
breadcrumb << "exe reached WinMain"
<< " build=" << BT_VERSION_FULL
<< " machine=" << machine
<< " user=" << (getenv("USERNAME") ? getenv("USERNAME") : "?")
<< " callsign=" << (getenv("BT_CALLSIGN") && *getenv("BT_CALLSIGN")
? getenv("BT_CALLSIGN") : "-")
<< " mode=" << logStem
<< " pid=" << (unsigned)GetCurrentProcessId()
<< " local=" << lt.wYear << "-"
<< (lt.wMonth < 10 ? "0" : "") << lt.wMonth << "-"
<< (lt.wDay < 10 ? "0" : "") << lt.wDay << " "
<< (lt.wHour < 10 ? "0" : "") << lt.wHour << ":"
<< (lt.wMinute < 10 ? "0" : "") << lt.wMinute << ":"
<< (lt.wSecond < 10 ? "0" : "") << lt.wSecond
<< "\nlog=" << resolvedLog
<< "\nargs=" << (lpCmdLine ? lpCmdLine : "") << "\n";
}
}
// Say so when we had to go looking for content\ -- a player who launched // Say so when we had to go looking for content\ -- a player who launched
// the exe directly should see WHY their settings appeared where they did. // the exe directly should see WHY their settings appeared where they did.
if (gBTCwdFixNote != NULL) if (gBTCwdFixNote != NULL)
+23 -4
View File
@@ -16,19 +16,37 @@
#include <stdarg.h> #include <stdarg.h>
// //
// The marshal logs to its own file (marshal.log in the cwd = content\): // The marshal runs on a WORKER thread and the engine's DEBUG log stream is
// the engine's DEBUG log stream is single-threaded and this is a worker // single-threaded, so it cannot share std::cout -- it keeps its own FILE*.
// thread. Ordinary printf-file logging, flushed per line. // It does, however, target the SAME per-day log file (2026-07-28): btl4main
// pins the resolved name into BT_LOG before any of this runs, so one getenv
// puts the marshal's lines in the file players are asked for, instead of a
// separate marshal.log nobody knows to send. Own handle + own lock + line
// flush; the fallback keeps the old filename if BT_LOG is somehow unset.
// //
static FILE *marshalLogFile = NULL; static FILE *marshalLogFile = NULL;
static CRITICAL_SECTION marshalLogLock;
static LONG marshalLogLockReady = 0;
static void static void
MarshalLog(const char *format, ...) MarshalLog(const char *format, ...)
{ {
if (InterlockedCompareExchange(&marshalLogLockReady, 1, 0) == 0)
{
InitializeCriticalSection(&marshalLogLock);
InterlockedExchange(&marshalLogLockReady, 2);
}
while (InterlockedCompareExchange(&marshalLogLockReady, 2, 2) != 2)
{
Sleep(0); // another thread is initialising
}
EnterCriticalSection(&marshalLogLock);
if (marshalLogFile == NULL) if (marshalLogFile == NULL)
{ {
marshalLogFile = fopen("marshal.log", "at"); const char *day_log = getenv("BT_LOG");
marshalLogFile = fopen((day_log && *day_log) ? day_log : "marshal.log", "at");
if (marshalLogFile == NULL) if (marshalLogFile == NULL)
{ {
LeaveCriticalSection(&marshalLogLock);
return; return;
} }
} }
@@ -39,6 +57,7 @@ static void
fprintf(marshalLogFile, "\n"); fprintf(marshalLogFile, "\n");
fflush(marshalLogFile); fflush(marshalLogFile);
va_end(arguments); va_end(arguments);
LeaveCriticalSection(&marshalLogLock);
} }
// //
+5 -1
View File
@@ -28,7 +28,11 @@ static void
{ {
if (lobbyLogFile == NULL) if (lobbyLogFile == NULL)
{ {
lobbyLogFile = fopen("marshal.log", "at"); // shares the marshal's file // Same target as MarshalLog (btl4console.cpp): the per-day log whose
// resolved name btl4main pinned into BT_LOG, so lobby lines land in the
// one file players are asked for. Old filename kept as the fallback.
const char *day_log = getenv("BT_LOG");
lobbyLogFile = fopen((day_log && *day_log) ? day_log : "marshal.log", "at");
if (lobbyLogFile == NULL) if (lobbyLogFile == NULL)
{ {
return; return;
+19 -3
View File
@@ -29,7 +29,8 @@ in the join menu (the operator can override it).
When a round ends your game AUTOMATICALLY rejoins for the next one When a round ends your game AUTOMATICALLY rejoins for the next one
(same seat, same mech) -- just wait for the operator's next launch. (same seat, same mech) -- just wait for the operator's next launch.
Close the window to leave for real. Close the window to leave for real.
If the game closes unexpectedly, send the operator content\join.log. If the game closes unexpectedly, send the operator TODAY'S log --
content\join_YYYYMMDD.log (the NEWEST one) -- plus content\lastrun_join.txt.
AFTER A MULTIPLAYER SESSION: the game writes a small match report AFTER A MULTIPLAYER SESSION: the game writes a small match report
(content\matchlog_<date>_<time>_<number>.txt) and, on join.bat / (content\matchlog_<date>_<time>_<number>.txt) and, on join.bat /
@@ -38,8 +39,23 @@ round -- you do not need to send it. Please send one MANUALLY only if
the game CRASHED mid-round (the upload happens on a clean round end, so the game CRASHED mid-round (the upload happens on a clean round end, so
a crash loses it) or if you were playing SOLO or over STEAM (no relay a crash loses it) or if you were playing SOLO or over STEAM (no relay
to upload through). to upload through).
IF ANYTHING CRASHED OR MISBEHAVED, send content\join.log -- that one is IF ANYTHING CRASHED OR MISBEHAVED, send BOTH of these -- neither is ever
never uploaded and only exists on your machine. uploaded, they only exist on your machine:
content\join_YYYYMMDD.log today's date; the NEWEST one. There is now
ONE log per day holding every session, so a
crash is no longer erased when you relaunch.
Each session inside it starts with a line
"===== BT411 SESSION ... =====" naming your
build and PC, so it still identifies itself
if the filename gets changed when you send it.
content\lastrun_join.txt tiny; send it EVEN IF there is no log dated
today -- that combination is exactly how we
tell "the game was blocked before it started"
(antivirus, or a missing system file) apart
from a normal crash.
If you UPGRADED over an older copy you may still have an old content\join.log
lying around. Nothing writes to it any more -- ignore it, or delete it, so
you don't send a stale one by mistake.
You start INSIDE the cockpit with the pod's MFD panels composited You start INSIDE the cockpit with the pod's MFD panels composited
around the view (one window). The red MFD buttons, radar rails and around the view (one window). The red MFD buttons, radar rails and
+16 -9
View File
@@ -13,12 +13,20 @@ rem one fresh log per bat run; the menu->game relaunch chain APPENDS
rem so every generation's lines survive (0-byte logs = killed pre-start) rem so every generation's lines survive (0-byte logs = killed pre-start)
REM keep the PREVIOUS session's log: a crash report died 2026-07-26 REM keep the PREVIOUS session's log: a crash report died 2026-07-26
REM because re-running this bat deleted the only evidence. REM because re-running this bat deleted the only evidence.
if exist content\join.old.log del content\join.old.log
if exist content\join.log ren content\join.log join.old.log
set BT_LOG=join.log
set BT_LOG_APPEND=1
set BT_START_INSIDE=1 set BT_START_INSIDE=1
set BT_DEV_GAUGES=1 set BT_DEV_GAUGES=1
rem LOGGING (2026-07-28): the bat no longer names or rotates the log. With
rem BT_LOG unset the exe names a PER-DAY file itself -- content\join_YYYYMMDD.log
rem -- and always appends, so every session of the day lands in ONE file, each
rem starting with a "===== BT411 SESSION ... =====" header naming the build,
rem machine and pid (so it identifies itself even if the filename is changed
rem in transit). Rotation used to keep only TWO generations: on 2026-07-27 a
rem player crashed twice, kept playing, and his own relaunches deleted both
rem crash sessions before anyone could ask for them.
rem Clear any INHERITED BT_LOG: with it set the exe uses that path verbatim
rem and, with no BT_LOG_APPEND, TRUNCATES it -- the exact evidence loss this
rem change removes. Empty forces the per-day self-named file + append.
set BT_LOG=
cd content cd content
rem JOIN-GAME menu (2026-07-22): pick your CALLSIGN + MECH, click JOIN -- rem JOIN-GAME menu (2026-07-22): pick your CALLSIGN + MECH, click JOIN --
rem your choice is sent to the operator and lands in the mission roster. rem your choice is sent to the operator and lands in the mission roster.
@@ -26,7 +34,7 @@ set BT_FE_JOIN=1
rem LAUNCH FORENSICS (#41): bracket the exe from outside -- if it dies rem LAUNCH FORENSICS (#41): bracket the exe from outside -- if it dies
rem before it can write its own log, this report still records when it rem before it can write its own log, this report still records when it
rem ran, how it exited, and whether it ever created its log at all. rem ran, how it exited, and whether it ever created its log at all.
echo [%DATE% %TIME%] launching btl4.exe > launch_report.txt if exist lastrun_join.txt del lastrun_join.txt
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold
rem this window open for ANY btl4.exe on the machine -- a second client, rem this window open for ANY btl4.exe on the machine -- a second client,
@@ -42,9 +50,8 @@ for /f "tokens=1,2" %%P in ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME e
) )
..\build\Release\btl4.exe ..\build\Release\btl4.exe
set BT_EXITCODE=%ERRORLEVEL% set BT_EXITCODE=%ERRORLEVEL%
echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> launch_report.txt if not exist lastrun_join.txt echo THE EXE NEVER REACHED WinMain: blocked before it could run (loader failure or antivirus) > lastrun_join.txt
if not exist join.log echo join.log WAS NEVER CREATED: the exe was blocked before it could run (loader failure or antivirus) >> launch_report.txt echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> lastrun_join.txt
if exist join.log for %%A in (join.log) do echo join.log size: %%~zA bytes >> launch_report.txt
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it
@@ -81,7 +88,7 @@ if defined BT_STILL_RUNNING (
echo. echo.
echo The game has exited. If you did NOT quit on purpose echo The game has exited. If you did NOT quit on purpose
echo (crash, or it never connected), send the operator BOTH files: echo (crash, or it never connected), send the operator BOTH files:
echo content\join.log and content\launch_report.txt echo content\join_YYYYMMDD.log (today's, newest) and content\lastrun_join.txt
echo Otherwise the session may not be up yet -- try again. echo Otherwise the session may not be up yet -- try again.
pause pause
exit /b exit /b
+16 -9
View File
@@ -16,12 +16,20 @@ rem one fresh log per bat run; the menu->game relaunch chain APPENDS
rem so every generation's lines survive (0-byte logs = killed pre-start) rem so every generation's lines survive (0-byte logs = killed pre-start)
REM keep the PREVIOUS session's log: a crash report died 2026-07-26 REM keep the PREVIOUS session's log: a crash report died 2026-07-26
REM because re-running this bat deleted the only evidence. REM because re-running this bat deleted the only evidence.
if exist content\join.old.log del content\join.old.log
if exist content\join.log ren content\join.log join.old.log
set BT_LOG=join.log
set BT_LOG_APPEND=1
set BT_START_INSIDE=1 set BT_START_INSIDE=1
set BT_DEV_GAUGES=1 set BT_DEV_GAUGES=1
rem LOGGING (2026-07-28): the bat no longer names or rotates the log. With
rem BT_LOG unset the exe names a PER-DAY file itself -- content\join_YYYYMMDD.log
rem -- and always appends, so every session of the day lands in ONE file, each
rem starting with a "===== BT411 SESSION ... =====" header naming the build,
rem machine and pid (so it identifies itself even if the filename is changed
rem in transit). Rotation used to keep only TWO generations: on 2026-07-27 a
rem player crashed twice, kept playing, and his own relaunches deleted both
rem crash sessions before anyone could ask for them.
rem Clear any INHERITED BT_LOG: with it set the exe uses that path verbatim
rem and, with no BT_LOG_APPEND, TRUNCATES it -- the exact evidence loss this
rem change removes. Empty forces the per-day self-named file + append.
set BT_LOG=
cd content cd content
rem JOIN-GAME menu (2026-07-22): pick your CALLSIGN + MECH, click JOIN -- rem JOIN-GAME menu (2026-07-22): pick your CALLSIGN + MECH, click JOIN --
rem your choice is sent to the operator and lands in the mission roster. rem your choice is sent to the operator and lands in the mission roster.
@@ -29,7 +37,7 @@ set BT_FE_JOIN=1
rem LAUNCH FORENSICS (#41): bracket the exe from outside -- if it dies rem LAUNCH FORENSICS (#41): bracket the exe from outside -- if it dies
rem before it can write its own log, this report still records when it rem before it can write its own log, this report still records when it
rem ran, how it exited, and whether it ever created its log at all. rem ran, how it exited, and whether it ever created its log at all.
echo [%DATE% %TIME%] launching btl4.exe > launch_report.txt if exist lastrun_join.txt del lastrun_join.txt
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold
rem this window open for ANY btl4.exe on the machine -- a second client, rem this window open for ANY btl4.exe on the machine -- a second client,
@@ -45,9 +53,8 @@ for /f "tokens=1,2" %%P in ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME e
) )
..\build\Release\btl4.exe ..\build\Release\btl4.exe
set BT_EXITCODE=%ERRORLEVEL% set BT_EXITCODE=%ERRORLEVEL%
echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> launch_report.txt if not exist lastrun_join.txt echo THE EXE NEVER REACHED WinMain: blocked before it could run (loader failure or antivirus) > lastrun_join.txt
if not exist join.log echo join.log WAS NEVER CREATED: the exe was blocked before it could run (loader failure or antivirus) >> launch_report.txt echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> lastrun_join.txt
if exist join.log for %%A in (join.log) do echo join.log size: %%~zA bytes >> launch_report.txt
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it
@@ -84,7 +91,7 @@ if defined BT_STILL_RUNNING (
echo. echo.
echo The game has exited. If you did NOT quit on purpose echo The game has exited. If you did NOT quit on purpose
echo (crash, or it never connected), send the operator BOTH files: echo (crash, or it never connected), send the operator BOTH files:
echo content\join.log and content\launch_report.txt echo content\join_YYYYMMDD.log (today's, newest) and content\lastrun_join.txt
echo Otherwise the session may not be up yet -- try again. echo Otherwise the session may not be up yet -- try again.
pause pause
exit /b exit /b
+18 -14
View File
@@ -13,27 +13,31 @@ if not exist "content\OPERATOR.EGG" goto badpath
set BT_JOYCONFIG=1 set BT_JOYCONFIG=1
set BT_START_INSIDE=1 set BT_START_INSIDE=1
set BT_DEV_GAUGES=1 set BT_DEV_GAUGES=1
rem one fresh log per bat run; the menu->game relaunch chain APPENDS rem The wizard's own stem keeps it clear of join_*.log (the MULTIPLAYER log):
rem so every generation's lines survive (0-byte logs = killed pre-start) rem it used to be able to delete a player's MP crash evidence. It now writes
rem KEEP the previous run's log (a crash report died 2026-07-26 because a rem content\joyconfig_YYYYMMDD.log.
rem re-run deleted the only evidence). Rotate, never delete.
rem ...and do NOT touch join.log (the MULTIPLAYER log) -- the wizard used to
rem delete a player's MP crash evidence. The wizard writes joyconfig.log.
if exist content\joyconfig.old.log del content\joyconfig.old.log
if exist content\joyconfig.log ren content\joyconfig.log joyconfig.old.log
set BT_LOG=joyconfig.log
set BT_LOG_APPEND=1
set BT_FE_SOLO=1 set BT_FE_SOLO=1
rem LOGGING (2026-07-28): the bat no longer names or rotates the log. With
rem BT_LOG unset the exe names a PER-DAY file itself -- content\joyconfig_YYYYMMDD.log
rem -- and always appends, so every session of the day lands in ONE file, each
rem starting with a "===== BT411 SESSION ... =====" header naming the build,
rem machine and pid (so it identifies itself even if the filename is changed
rem in transit). Rotation used to keep only TWO generations: on 2026-07-27 a
rem player crashed twice, kept playing, and his own relaunches deleted both
rem crash sessions before anyone could ask for them.
rem Clear any INHERITED BT_LOG: with it set the exe uses that path verbatim
rem and, with no BT_LOG_APPEND, TRUNCATES it -- the exact evidence loss this
rem change removes. Empty forces the per-day self-named file + append.
set BT_LOG=
cd content cd content
rem LAUNCH FORENSICS (#41): bracket the exe from outside -- if it dies rem LAUNCH FORENSICS (#41): bracket the exe from outside -- if it dies
rem before it can write its own log, this report still records when it rem before it can write its own log, this report still records when it
rem ran, how it exited, and whether it ever created its log at all. rem ran, how it exited, and whether it ever created its log at all.
echo [%DATE% %TIME%] launching btl4.exe > launch_report.txt if exist lastrun_joyconfig.txt del lastrun_joyconfig.txt
..\build\Release\btl4.exe ..\build\Release\btl4.exe
set BT_EXITCODE=%ERRORLEVEL% set BT_EXITCODE=%ERRORLEVEL%
echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> launch_report.txt if not exist lastrun_joyconfig.txt echo THE EXE NEVER REACHED WinMain: blocked before it could run (loader failure or antivirus) > lastrun_joyconfig.txt
if not exist joyconfig.log echo joyconfig.log WAS NEVER CREATED: the exe was blocked before it could run (loader failure or antivirus) >> launch_report.txt echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> lastrun_joyconfig.txt
if exist joyconfig.log for %%A in (joyconfig.log) do echo joyconfig.log size: %%~zA bytes >> launch_report.txt
exit /b exit /b
:badpath :badpath
echo. echo.
+39 -20
View File
@@ -12,23 +12,36 @@ if not exist "build\Release\btl4.exe" goto badpath
if not exist "content\OPERATOR.EGG" goto badpath if not exist "content\OPERATOR.EGG" goto badpath
set BT_START_INSIDE=1 set BT_START_INSIDE=1
set BT_DEV_GAUGES=1 set BT_DEV_GAUGES=1
rem one fresh log per bat run; the menu->game relaunch chain APPENDS rem LOGGING (2026-07-28): the bat no longer names or rotates the log. With
rem so every generation's lines survive (0-byte logs = killed pre-start) rem BT_LOG unset the exe names a PER-DAY file itself -- content\solo_YYYYMMDD.log
rem KEEP the previous run's log (a crash report died 2026-07-26 because a rem -- and always appends, so every session of the day lands in one file with a
rem re-run deleted the only evidence). Rotate, never delete. rem "===== BT411 SESSION ... =====" header between them.
rem ...and do NOT touch join.log: that is the MULTIPLAYER log. A player who rem
rem crashed in MP and then ran solo to investigate used to destroy their own rem WHY: rotation kept only TWO generations (del *.old.log, then ren *.log
rem crash evidence. Solo writes solo.log. rem *.old.log). On 2026-07-27 a player crashed twice, kept playing, and his own
if exist content\solo.old.log del content\solo.old.log rem relaunches deleted both crash sessions before anyone could ask for them --
if exist content\solo.log ren content\solo.log solo.old.log rem the stacks were lost permanently. A per-day file has no rotation window to
set BT_LOG=solo.log rem fall out of. The stem still separates solo from the MULTIPLAYER log, so a
set BT_LOG_APPEND=1 rem player who crashes in MP and then runs solo still cannot destroy their own
rem crash evidence.
set BT_FE_SOLO=1 set BT_FE_SOLO=1
rem Clear any INHERITED BT_LOG: with it set the exe uses that path verbatim
rem and, with no BT_LOG_APPEND, TRUNCATES it -- the exact evidence loss this
rem change removes. Empty forces the per-day self-named file + append.
set BT_LOG=
cd content cd content
rem LAUNCH FORENSICS (#41): bracket the exe from outside -- if it dies rem LAUNCH FORENSICS (#41): bracket the exe from outside, so a launch that dies
rem before it can write its own log, this report still records when it rem before it can log anything still leaves a record. The exe APPENDS its own
rem ran, how it exited, and whether it ever created its log at all. rem block to lastrun_solo.txt at first breath (build, pid, local time, and WHICH
echo [%DATE% %TIME%] launching btl4.exe > launch_report.txt rem day-log it chose -- cmd cannot derive that name itself, %DATE% being
rem locale-ordered and full of '/'). So: delete the file here and do NOT
rem pre-write it -- its ABSENCE afterwards is what means "never reached
rem WinMain". Writing a line here first would make that test always pass.
rem This REPLACES the old "if not exist solo.log" probe, which a per-day log
rem silently breaks: that file survives earlier launches, so the probe could
rem never fire and every healthy run would be reported as blocked.
rem No find/findstr probe -- MSYS shadows both (see the note above :btwait).
if exist lastrun_solo.txt del lastrun_solo.txt
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold
rem this window open for ANY btl4.exe on the machine -- a second client, rem this window open for ANY btl4.exe on the machine -- a second client,
@@ -44,9 +57,8 @@ for /f "tokens=1,2" %%P in ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME e
) )
..\build\Release\btl4.exe ..\build\Release\btl4.exe
set BT_EXITCODE=%ERRORLEVEL% set BT_EXITCODE=%ERRORLEVEL%
echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> launch_report.txt if not exist lastrun_solo.txt echo THE EXE NEVER REACHED WinMain: blocked before it could run (loader failure or antivirus) > lastrun_solo.txt
if not exist solo.log echo solo.log WAS NEVER CREATED: the exe was blocked before it could run (loader failure or antivirus) >> launch_report.txt echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> lastrun_solo.txt
if exist solo.log for %%A in (solo.log) do echo solo.log size: %%~zA bytes >> launch_report.txt
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it
@@ -81,8 +93,15 @@ if defined BT_STILL_RUNNING (
goto btwait goto btwait
) )
echo. echo.
echo The game has exited. If it closed unexpectedly, echo The game has exited. If it closed unexpectedly, send the operator
echo send the operator content\solo.log. echo BOTH of these from the content folder:
echo content\lastrun_solo.txt (tiny -- send it EVEN IF there is no
echo log dated today: that combination is
echo how we spot a blocked launch)
echo content\solo_YYYYMMDD.log
echo (the newest solo_*.log -- it holds every session from today, and each
echo one starts with a "===== BT411 SESSION" line naming your build and PC,
echo so it identifies itself even if the filename gets changed in transit.)
pause pause
exit /b exit /b
:badpath :badpath
+27 -9
View File
@@ -9,12 +9,6 @@ if not exist "build\Release\btl4.exe" goto badpath
if not exist "content\BTL4.RES" goto badpath if not exist "content\BTL4.RES" goto badpath
set BT_PLATFORM=glass set BT_PLATFORM=glass
set BT_STEAM_NET=1 set BT_STEAM_NET=1
rem KEEP the previous run's log (a crash report died 2026-07-26 because a
rem re-run deleted the only evidence). Rotate, never delete.
if exist content\steam.old.log del content\steam.old.log
if exist content\steam.log ren content\steam.log steam.old.log
set BT_LOG=steam.log
set BT_LOG_APPEND=1
rem Cockpit view + MFD gauges -- the same client flags the join bats set rem Cockpit view + MFD gauges -- the same client flags the join bats set
rem (field report: players booted 3rd-person without them). Inherited by rem (field report: players booted 3rd-person without them). Inherited by
rem the menu's mission relaunch. rem the menu's mission relaunch.
@@ -23,6 +17,18 @@ set BT_DEV_GAUGES=1
rem full menu here (all modes + steam buttons); clear the solo trim in rem full menu here (all modes + steam buttons); clear the solo trim in
rem case this shell ran play_solo.bat first rem case this shell ran play_solo.bat first
set BT_FE_SOLO= set BT_FE_SOLO=
rem LOGGING (2026-07-28): the bat no longer names or rotates the log. With
rem BT_LOG unset the exe names a PER-DAY file itself -- content\steam_YYYYMMDD.log
rem -- and always appends, so every session of the day lands in ONE file, each
rem starting with a "===== BT411 SESSION ... =====" header naming the build,
rem machine and pid (so it identifies itself even if the filename is changed
rem in transit). Rotation used to keep only TWO generations: on 2026-07-27 a
rem player crashed twice, kept playing, and his own relaunches deleted both
rem crash sessions before anyone could ask for them.
rem Clear any INHERITED BT_LOG: with it set the exe uses that path verbatim
rem and, with no BT_LOG_APPEND, TRUNCATES it -- the exact evidence loss this
rem change removes. Empty forces the per-day self-named file + append.
set BT_LOG=
cd content cd content
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold
@@ -37,7 +43,16 @@ set "BT_PRE=,"
for /f "tokens=1,2" %%P in ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME eq btl4.exe" /NH 2^>nul') do ( for /f "tokens=1,2" %%P in ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME eq btl4.exe" /NH 2^>nul') do (
if /I "%%P"=="btl4.exe" set "BT_PRE=!BT_PRE!%%Q," if /I "%%P"=="btl4.exe" set "BT_PRE=!BT_PRE!%%Q,"
) )
rem LAUNCH RECORD (#41 forensics -- NEW on the Steam path, 2026-07-28). This
rem bat never had the outside bracket the join/solo bats carry, which is why a
rem Steam player whose exe was killed before WinMain left no evidence at all.
rem The exe appends its own block to lastrun_steam.txt at first breath; delete it here
rem so its ABSENCE afterwards means the exe never started.
if exist lastrun_steam.txt del lastrun_steam.txt
..\build\Release\btl4.exe ..\build\Release\btl4.exe
set BT_EXITCODE=%ERRORLEVEL%
if not exist lastrun_steam.txt echo THE EXE NEVER REACHED WinMain: blocked before it could run (loader failure or antivirus) > lastrun_steam.txt
echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> lastrun_steam.txt
rem ------------------------------------------------------------------ rem ------------------------------------------------------------------
rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it
@@ -72,9 +87,12 @@ if defined BT_STILL_RUNNING (
goto btwait goto btwait
) )
echo. echo.
echo The game has exited. If it closed unexpectedly, send the operator echo The game has exited. If it closed unexpectedly, send the operator BOTH
echo content\steam.log. Steam not running = the menu still works but echo content\lastrun_steam.txt (tiny -- send it EVEN IF there is no log
echo lobbies will not appear. echo dated today: that is how we spot a
echo launch that was blocked before it ran)
echo content\steam_YYYYMMDD.log (today's, newest)
echo Steam not running = the menu still works but lobbies will not appear.
pause pause
exit /b exit /b
:badpath :badpath
+13 -6
View File
@@ -1386,8 +1386,10 @@ class Operator(QMainWindow):
net_tail = ("echo.\n" net_tail = ("echo.\n"
"echo The game has exited. If you did NOT quit on purpose\n" "echo The game has exited. If you did NOT quit on purpose\n"
"echo (crash, or it never connected), send the operator the\n" "echo (crash, or it never connected), send the operator the\n"
"echo file content\\join.log so they can see what " "echo newest file content\\join_YYYYMMDD.log so they can "
"happened.\n" "see what happened.\n"
"echo (one file per day, every session in it, each headed by "
"a BT411 SESSION line.)\n"
"echo Otherwise the session may not be up yet -- try " "echo Otherwise the session may not be up yet -- try "
"again.\n" "again.\n"
"pause\n") "pause\n")
@@ -1400,7 +1402,7 @@ class Operator(QMainWindow):
# cockpit with MFD gauges -- match it (field report: remote # cockpit with MFD gauges -- match it (field report: remote
# players booted 3rd-person + no gauges because join.bat set # players booted 3rd-person + no gauges because join.bat set
# neither; view/gauges are CLIENT flags the relay can't reach). # neither; view/gauges are CLIENT flags the relay can't reach).
"set BT_RELAY=%s:%d\nset BT_LOG=join.log\n" "set BT_RELAY=%s:%d\n"
"set BT_START_INSIDE=1\nset BT_DEV_GAUGES=1\n" "set BT_START_INSIDE=1\nset BT_DEV_GAUGES=1\n"
% (host, self.f_port.value()), % (host, self.f_port.value()),
"..\\build\\Release\\btl4.exe -egg %s -net 1501\n" % egg_name, "..\\build\\Release\\btl4.exe -egg %s -net 1501\n" % egg_name,
@@ -1418,7 +1420,7 @@ class Operator(QMainWindow):
f.write(guarded( f.write(guarded(
lan_hdr + lan_hdr +
"rem Joining over the INTERNET? Use join.bat instead.\n", "rem Joining over the INTERNET? Use join.bat instead.\n",
"set BT_RELAY=%s\nset BT_LOG=join.log\n" "set BT_RELAY=%s\n"
"set BT_START_INSIDE=1\nset BT_DEV_GAUGES=1\n" % lan_relay, "set BT_START_INSIDE=1\nset BT_DEV_GAUGES=1\n" % lan_relay,
"..\\build\\Release\\btl4.exe -egg %s -net 1501\n" % egg_name, "..\\build\\Release\\btl4.exe -egg %s -net 1501\n" % egg_name,
net_tail)) net_tail))
@@ -1430,10 +1432,15 @@ class Operator(QMainWindow):
"rem experience, weather, length; the menu returns after each " "rem experience, weather, length; the menu returns after each "
"mission).\n", "mission).\n",
"set BT_START_INSIDE=1\nset BT_DEV_GAUGES=1\n" "set BT_START_INSIDE=1\nset BT_DEV_GAUGES=1\n"
"set BT_LOG=join.log\nset BT_FE_SOLO=1\n", "set BT_FE_SOLO=1\n",
"..\\build\\Release\\btl4.exe\n", "..\\build\\Release\\btl4.exe\n",
"echo.\necho The game has exited. If it closed unexpectedly,\n" "echo.\necho The game has exited. If it closed unexpectedly,\n"
"echo send the operator content\\join.log.\npause\n")) # this bat sets BT_FE_SOLO, and btl4main tests BT_FE_SOLO before
# BT_FE_JOIN/BT_RELAY -- so the exe names it solo_, not join_.
"echo send the operator the newest content\\solo_YYYYMMDD.log\n"
"echo and content\\lastrun_solo.txt (send it even if there is no\n"
"echo log dated today -- that means it never started).\n"
"pause\n"))
self.log.appendPlainText( self.log.appendPlainText(
"exported join.bat + join_lan.bat + play_solo.bat to %s " "exported join.bat + join_lan.bat + play_solo.bat to %s "
"(public host: %s) -- join bats are universal; the relay " "(public host: %s) -- join bats are universal; the relay "