From 4e89baeff31824233789a2274176232b041bed7d Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Thu, 30 Jul 2026 13:30:30 -0500 Subject: [PATCH] bench parity: local nodes now launch exactly like a player's shortcut Local benches had drifted from the shipped launchers and every divergence cost a debugging session: * no BT_START_INSIDE -> benches opened in the EXTERNAL CHASE camera while every player starts in the COCKPIT. The first first-person look at a bench window was misread as a broken HUD. * novice bench eggs -> every SHIPPED egg is expert; novice gates off the entire heat model, crits and jams, so bench combat was not field combat. (Verified: the aligned node now boots experience=3 simLive=1 heatModelOn=1, where it used to boot 0/0/0.) * 1-LP affinity pins -> starved the gauge executive and produced a false "the comms panel never counts deaths" reading. Two LPs per node keeps the documented single-box jitter fix without the starvation. scratchpad/night6/bench_common.sh now owns the contract (bt_player_env, bt_launch, bt_expert_egg, bt_novice_egg) copied verbatim from play_solo.bat, with bt_assert_player_env warning if the shipped bat ever drifts. All three 4-node benches source it. The limp bench keeps a novice egg -- expert crits the aimed leg and the mech turns-but-never-moves -- and now says so loudly. Also: BT_SHOT_EVERY= headless backbuffer capture (btl4vid.cpp), the tool this was diagnosed with. It must sit above the warp phase-0 early-out since that fn is the per-frame alpha-pass hook. OS screen-capture is off-limits: a foreground-lock failure photographed the user's browser instead of the game. KB: build-and-run.md gains the bench-parity rule + the capture diag; cockpit-view.md gains the measured aspect/FOV finding (the surround's 2.05:1 view collapses vertical FOV to 31.5 deg vs the pod's 46.8, so fixed canopy geometry covers 68.6% of the lower half -- NOT a regression: the shipped 643 binary reports identical geometry). Co-Authored-By: Claude Fable 5 --- context/build-and-run.md | 35 ++++++++++++++++ context/cockpit-view.md | 30 +++++++++++++ game/reconstructed/btl4vid.cpp | 29 +++++++++++++ scratchpad/night6/bench_common.sh | 70 +++++++++++++++++++++++++++++++ scratchpad/night6/mp4_cross.sh | 56 +++++++++++++++++++++++++ scratchpad/night6/mp4_limp.sh | 53 +++++++++++++++++++++++ scratchpad/night6/mp4_panel.sh | 48 +++++++++++++++++++++ scratchpad/night6/mp_ghost.sh | 4 +- 8 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 scratchpad/night6/bench_common.sh create mode 100644 scratchpad/night6/mp4_cross.sh create mode 100644 scratchpad/night6/mp4_limp.sh create mode 100644 scratchpad/night6/mp4_panel.sh diff --git a/context/build-and-run.md b/context/build-and-run.md index a00ba19..81ef08f 100644 --- a/context/build-and-run.md +++ b/context/build-and-run.md @@ -92,6 +92,41 @@ run\run.cmd [EGG] # default DEV.EGG; cd's to content\ and runs btl4.exe -eg mad1/mad2, own1, snd1, thr1, vul1); each boots a solo mission, spawns, animates, no crash. The code path is mech-agnostic. [T2] +## Local benches MUST launch like a player (bench parity, 2026-07-30) + +Every shipped launcher (`play_solo.bat`, `join*.bat`, `play_steam.bat`) sets the SAME three +things, and a local bench that omits them is not testing the game the field plays: + + set BT_PLATFORM=glass set BT_START_INSIDE=1 set BT_DEV_GAUGES=1 + +plus a front-end flag (`BT_FE_SOLO` / `BT_FE_JOIN`), `cd content`, and NO `-res`. +**`BT_START_INSIDE=1` is the big one** — without it a bench opens in the EXTERNAL CHASE camera +while every player starts in the COCKPIT. Benches ran chase-only for weeks; the first +first-person look at one was misread as a broken HUD [T2]. +**Experience:** every SHIPPED egg is `experience=expert`. A `novice` bench egg silences the whole +heat model, crits and jams (see [[experience-levels]]) — bench combat then is not field combat. +Only the aimed-leg gimp bench may use novice (expert crits the aimed leg and the mech +turns-but-never-moves); it must say so out loud. +**Affinity:** pin each node to TWO logical processors (`0x03/0x0C/0x30/0xC0`), not one. Disjoint +core sets keep single-box packet delivery even (the peer-shakiness fix, [[multiplayer]]); ONE LP +per node starves the gauge executive and fakes a "the comms panel never counts" reading [T2]. + +`scratchpad/night6/bench_common.sh` owns this contract (`bt_player_env` / `bt_launch` / +`bt_expert_egg` / `bt_novice_egg`, and `bt_assert_player_env` warns if the shipped bat drifts). +The 4-node benches (`mp4_panel.sh` respawn+scoreboard, `mp4_cross.sh` real cross-fire kills, +`mp4_limp.sh` gait replication) all source it. A `-net` node renders NOTHING until +`tools/btconsole.py` starts the mission — a solo launch is the quick visual check. + +## Headless frame capture — `BT_SHOT_EVERY` (2026-07-30) + +`BT_SHOT_EVERY=` dumps the game's own backbuffer every n frames to +`_NNN.png` (btl4vid.cpp, top of `BTDrawTranslocationSpheres` — the per-frame +alpha-pass hook; it MUST stay above that function's phase-0 early-out). This is how rendering is +diagnosed without foregrounding a window, and it is the ONLY capture method to use: OS +screen-capture grabs whatever is actually on the user's screen (a foreground-lock failure once +photographed the user's browser instead of the game) and is off-limits. +Frames land mid-alpha-pass, so a process killed during a write leaves ONE truncated PNG — skip it. + ## Debug (cdb x86) `"C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe"`. Pattern for a faulting stack (cwd = content\): `-g -c ".lines;sxe av;g;kp 24;q"` with `BT_ASSERT_TO_DEBUGGER=1`. Debug CRT fills fresh diff --git a/context/cockpit-view.md b/context/cockpit-view.md index ce84d47..3f8b8fe 100644 --- a/context/cockpit-view.md +++ b/context/cockpit-view.md @@ -88,6 +88,36 @@ inverse(eyeWorld)`. No LookAt anywhere. DPLEyeRenderable::Execute (order `Orient × R`, `D3DXMatrixInverse` on the live path — note the ctor also holds a copy of this code but its view write is DEAD; patch the Execute). +## ⚠ The canopy looks "obscured" at wide viewports — it is the ASPECT, not the canopy [T2] + +`BTFovYFromHorizontal` (L4VIDEO.cpp:127) pins the HORIZONTAL fov at the authentic 60° and derives +the vertical from the LIVE aspect — correct at the pod's 4:3 (60°×46.8°), but the cockpit-surround +layout hands it a very wide, short world view and the vertical field collapses: + +| viewport | aspect | vertical FOV | +|---|---|---| +| pod 800×600 | 1.33 | **46.8°** | +| surround default 900×500 | 1.80 | 35.6° | +| surround on a 1080p screen 900×440 | 2.05 | **31.5°** | + +The surround height-clamps the view to fit the work area (`btl4main.cpp` ~1404: 900×500 → 900×440 +on 1080p), so the smaller the screen the worse it gets. The canopy is FIXED world geometry, so a +1.5× vertical squeeze makes it eat 1.5× more of the view: measured on bhk1 at 900×440 the frame +covers **39% of the viewport — 9.5% of the upper half but 68.6% of the LOWER half**, i.e. the dash +rides up over the horizon and hides the ground where enemy mechs stand (user-reported as "can't +see to shoot"). At a true 4:3 world view the same build/mech/map renders a correct big-window +cockpit. Left/right skew is 6% (symmetric — nothing is tilted). +**Not a regression:** the shipped 4.11.643 binary reports identical geometry (`view 900x440 +canvas 1452x999`), and the gauge-executive change is measurably innocent (canopy coverage 57–59% +with it on vs 60–61% off — scene variation). +**Verification method:** `BT_COP_PLATES=1` (kit off) gives a 100%-solid viewport vs 39% with the +stencil cut live — that is how to prove the punch kit is running. Canopy coverage = per-pixel diff +of a normal frame against a `BT_HIDE_COCKPIT=1` frame; a raw grey-pixel count also catches sky. +**Open decision (nothing changed yet):** cap the aspect used for fovY (wide windows would show +MORE horizontally instead of cropping vertical — identical to the pod at 4:3), or make the +surround's view taller (clamp width, costing surround width), or bench first-person work in the +dock layout (`BT_DEV_GAUGES_DOCK=1 -res 640 480`). + ## Status per mech + the gyro (task #56 — gyro now LIVE) - **Madcat (MAX_COP): verified good on pure defaults** — dark domed frame (top arch + diff --git a/game/reconstructed/btl4vid.cpp b/game/reconstructed/btl4vid.cpp index 9cddc24..bbbb34f 100644 --- a/game/reconstructed/btl4vid.cpp +++ b/game/reconstructed/btl4vid.cpp @@ -2921,6 +2921,35 @@ void BTDrawTranslocationSpheres(LPDIRECT3DDEVICE9 device, const D3DXMATRIX *view, float dt, Time frame_time) { + // DIAG (off by default; MUST sit before the phase-0 early-out -- this + // fn is the per-frame alpha-pass hook, L4VIDEO.cpp:8831): BT_SHOT_EVERY= dumps the backbuffer every n + // frames as _NNN.png -- the GENERAL headless-eyes + // harness (view/HUD/layout debugging without foregrounding the window). + { + static int s_shotEvery = -2; + if (s_shotEvery == -2) + { + const char *se = getenv("BT_SHOT_EVERY"); + s_shotEvery = (se && *se) ? atoi(se) : 0; + if (s_shotEvery < 0) s_shotEvery = 0; + } + if (s_shotEvery > 0) + { + static int s_frameN = 0, s_dumpN = 0; + if ((++s_frameN % s_shotEvery) == 0 && s_dumpN < 400) + { + const char *pfx = getenv("BT_SHOT_PREFIX"); + char fn[600]; + _snprintf(fn, sizeof fn, "%s_%03d.png", (pfx && *pfx) ? pfx : "shot", s_dumpN++); + IDirect3DSurface9 *bb = 0; + if (SUCCEEDED(device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &bb)) && bb) + { + D3DXSaveSurfaceToFileA(fn, D3DXIFF_PNG, bb, 0, 0); + bb->Release(); + } + } + } + } // DIAG (off by default): BT_WARP_SELFTEST=1 forces a steady POV warp // (held in WaitForReincarnate, world masked) so the swirl can be frame-captured and // compared against the original (capture.png) in a solo game -- no death/respawn diff --git a/scratchpad/night6/bench_common.sh b/scratchpad/night6/bench_common.sh new file mode 100644 index 0000000..338aaea --- /dev/null +++ b/scratchpad/night6/bench_common.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# =========================================================================== +# BENCH COMMON -- launch a local test node EXACTLY as a field player's +# shortcut does, so what we see locally is what they see. +# +# WHY THIS FILE EXISTS (2026-07-30): local benches had drifted from the +# shipped launchers, and every divergence cost a debugging session: +# * no BT_START_INSIDE -> benches opened in the EXTERNAL CHASE camera while +# every player starts in the COCKPIT. Hours were +# spent on a "broken HUD" that was simply the first +# first-person look at a view the benches never showed. +# * novice bench eggs -> every SHIPPED egg is `expert`; novice silences the +# whole heat model, crits and jams, so bench combat +# was not the combat players get. +# * 1-logical-processor affinity pins -> starved the gauge executive and +# produced a false "the comms panel never counts" +# reading. Two LPs per node keeps MP jitter pinned +# (the documented single-box fix) without starving. +# +# THE PLAYER ENV IS THE CONTRACT. It is copied verbatim from the shipped +# play_solo.bat / join.bat (dist zip). bt_assert_player_env compares this +# list against a shipped .bat when one is available, so drift is caught. +# =========================================================================== + +BT_ROOT=/c/git/bt411 +BT_EXE=$BT_ROOT/build/Release/btl4.exe + +# --- the exact env every shipped launcher sets ----------------------------- +bt_player_env () { + export BT_PLATFORM=glass # unified build, desktop cockpit layer + export BT_START_INSIDE=1 # COCKPIT eyepoint -- what a player sees + export BT_DEV_GAUGES=1 # MFD/gauge surfaces + cockpit surround +} + +# --- warn if the shipped launcher has gained/lost a setting ---------------- +bt_assert_player_env () { + local bat="$BT_ROOT/dist/BT411_4.11.643/play_solo.bat" + [ -f "$bat" ] || return 0 + for v in BT_PLATFORM BT_START_INSIDE BT_DEV_GAUGES; do + grep -qi "set $v=" "$bat" || echo "[bench] WARNING: shipped bat no longer sets $v" + done +} + +# --- launch one node ------------------------------------------------------- +# bt_launch [extra args...] (env: extra BT_* vars) +# Two logical processors per node: disjoint core sets keep single-box packet +# delivery even (peer-shakiness fix) without starving the gauge executive. +bt_launch () { + local log="$1" egg="$2" aff="$3"; shift 3 + ( bt_player_env + export BT_LOG="$log" BT_AFFINITY="$aff" + "$BT_EXE" -egg "$egg" "$@" & ) +} + +# --- an EXPERT copy of an egg (what players actually fly) ------------------ +# Shipped eggs are all experience=expert. Bench eggs must match unless the +# harness itself needs otherwise (see bt_novice_egg). +bt_expert_egg () { # bt_expert_egg + sed 's/^experience=.*/experience=expert/' "$1" > "$2" +} + +# --- a NOVICE copy, for the harnesses that genuinely require it ------------ +# ONLY the aimed-leg gimp bench: on expert the self-damage bursts roll CRITS on +# the aimed leg, and one myomers crit leaves the mech turning-but-not-moving -- +# which destroys the limp demo. Novice gates crits; the leg gimp still fires. +# Any bench using this is NOT showing player-authentic damage behaviour. +bt_novice_egg () { # bt_novice_egg + sed 's/^experience=.*/experience=novice/' "$1" > "$2" + echo "[bench] NOTE: novice egg $2 -- crits/heat/jams are GATED OFF (harness requirement)" +} diff --git a/scratchpad/night6/mp4_cross.sh b/scratchpad/night6/mp4_cross.sh new file mode 100644 index 0000000..2cc19da --- /dev/null +++ b/scratchpad/night6/mp4_cross.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# #81/#45 FOUR-NODE CROSS-FIRE: real weapon kills, real attribution. +# +# No self-damage anywhere. All four pilots chase the nearest live peer +# (BT_GOTO=enemy), hold at 150m, and fire in bursts (BT_AF_PERIOD throttles +# BOTH the gun and missile groups -- unthrottled spam trips the documented +# FailureHeat all-weapons brick and combat dies). Every death is a weapon +# kill by another pilot, resolved cross-machine through the authentic path. +# +# Launched through bench_common.sh: every window is configured EXACTLY like a +# player's shortcut (cockpit view, glass cockpit, MFDs) on an EXPERT egg, so +# heat/crits/jams behave as they do in the field. +# +# PASS = matchlog KILL rows with killer != victim, panels mirror (SBMIRROR + +# panel DRAW), every death START -> RESET, 0 SWALLOWED / 0 MISMATCH / +# 0 DISCARDED / 0 GHOST / 0 crash. +set -x +. /c/git/bt411/scratchpad/night6/bench_common.sh +cd /c/git/bt411/content || exit 1 +bt_assert_player_env +rm -f mp4c_a.log mp4c_b.log mp4c_c.log mp4c_d.log +A=/c/git/bt411/scratchpad/night6/runs/$(date +%H%M%S); mkdir -p "$A"; mv matchlog_*.txt "$A"/ 2>/dev/null + +bt_expert_egg MP4.EGG MP4X.EGG +PD=/c/git/bt411/scratchpad/night6/procdump.exe + +launch () { # $1=log $2=affinity $3=port + BT_MP_LOG=1 BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 BT_TLOC_LOG=1 \ + BT_GOTO=enemy BT_GOTO_STOP=150 BT_AUTOFIRE=1 BT_AF_MISSILE=1 BT_AF_PERIOD=7 \ + bt_launch "$1" MP4X.EGG "$2" -net "$3" + sleep 2 + # crash net: full dump on any unhandled exception (a node died silently once) + if [ -x "$PD" ]; then + for pid in $(tasklist //FI "IMAGENAME eq btl4.exe" //NH 2>/dev/null | awk '{print $2}'); do + [ -f "/c/git/bt411/scratchpad/night6/pd_$pid.txt" ] && continue + "$PD" -accepteula -e -ma "$pid" \ + "/c/git/bt411/scratchpad/night6/crash_$pid.dmp" \ + > "/c/git/bt411/scratchpad/night6/pd_$pid.txt" 2>&1 & + done + fi +} +rm -f /c/git/bt411/scratchpad/night6/pd_*.txt /c/git/bt411/scratchpad/night6/crash_*.dmp +launch mp4c_d.log 0xC0 1801 +launch mp4c_c.log 0x30 1701 +launch mp4c_b.log 0x0C 1601 +launch mp4c_a.log 0x03 1501 +sleep 5 +python ../tools/btconsole.py MP4X.EGG 127.0.0.1:1501 127.0.0.1:1601 \ + 127.0.0.1:1701 127.0.0.1:1801 & +PC=$! + +sleep 480 +kill $PC 2>/dev/null +taskkill //F //IM btl4.exe 2>/dev/null +rm -f MP4X.EGG +echo "=== DONE ===" diff --git a/scratchpad/night6/mp4_limp.sh b/scratchpad/night6/mp4_limp.sh new file mode 100644 index 0000000..8bee0c3 --- /dev/null +++ b/scratchpad/night6/mp4_limp.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# #82 FOUR-NODE LIMP VERIFICATION: do peers see a gimped mech step-drag, or skate? +# +# Node A gimps a LEG ~2s after it is alive (BT_SELF_DAMAGE_ZONE=leg resolves the +# zone symbolically -- numeric ids are per-mech-type) and circles slowly in its +# own spawn area from the first second. B/C/D are PLAIN observers: no +# automation, full keyboard control, parked at spawn. Drive one over and look. +# +# Launched through bench_common.sh, so every window is configured EXACTLY like +# a player's shortcut -- COCKPIT view, glass cockpit, MFDs. +# +# ⚠ THE ONE DELIBERATE DIVERGENCE: this bench flies a NOVICE egg. On expert the +# self-damage bursts roll CRITS on the aimed leg and one myomers crit leaves the +# mech turning-but-not-moving, which destroys the limp. Crits/heat/jams are +# therefore OFF here -- do not read damage behaviour off this bench. +# +# USER CHECK: the limping mech must STEP-DRAG, never glide/skate. +# LOG CHECK: [animind] inst=1 entering gait states 22-27 on the observers. +set -x +. /c/git/bt411/scratchpad/night6/bench_common.sh +cd /c/git/bt411/content || exit 1 +bt_assert_player_env +rm -f mp4l_a.log mp4l_b.log mp4l_c.log mp4l_d.log +A=/c/git/bt411/scratchpad/night6/runs/$(date +%H%M%S); mkdir -p "$A"; mv matchlog_*.txt "$A"/ 2>/dev/null + +bt_novice_egg MP4L.EGG MP4LN.EGG + +observer () { # $1=log $2=affinity $3=port + BT_MP_LOG=1 BT_SYNC_LOG=1 BT_REPL_LOG=1 BT_MATCHLOG=1 BT_AUDIO_SPATIAL=1 \ + bt_launch "$1" MP4LN.EGG "$2" -net "$3" +} +observer mp4l_d.log 0xC0 1801 +sleep 1 +observer mp4l_c.log 0x30 1701 +sleep 1 +observer mp4l_b.log 0x0C 1601 +sleep 1 +# the LIMPER: symbolic leg zone, 2 ticks of 60 -> gimp (no kill) ~2s after +# alive; slow tight circle from t0 so it never leaves its spawn area. +BT_MP_LOG=1 BT_DMG_LOG=1 BT_MATCHLOG=1 BT_AUDIO_SPATIAL=1 \ +BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=leg BT_SELF_DAMAGE_TICKS=2 BT_SELF_DAMAGE_DELAY=2 \ +BT_AUTODRIVE=0.4 BT_FORCE_TURN=0.45 \ + bt_launch mp4l_a.log MP4LN.EGG 0x03 -net 1501 +sleep 5 +python ../tools/btconsole.py MP4LN.EGG 127.0.0.1:1501 127.0.0.1:1601 \ + 127.0.0.1:1701 127.0.0.1:1801 & +PC=$! + +sleep 420 +kill $PC 2>/dev/null +taskkill //F //IM btl4.exe 2>/dev/null +rm -f MP4LN.EGG +echo "=== DONE ===" diff --git a/scratchpad/night6/mp4_panel.sh b/scratchpad/night6/mp4_panel.sh new file mode 100644 index 0000000..7b50b07 --- /dev/null +++ b/scratchpad/night6/mp4_panel.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# #81/#45 FOUR-NODE RESPAWN + SCOREBOARD BENCH. +# +# Four networked instances, ALL dying repeatedly on staggered timers so death +# cycles overlap (the field condition: several respawn handshakes in flight at +# once, each answered by an arbitrary peer's DropZone). +# +# Launched through bench_common.sh, so every window is configured EXACTLY like +# a player's play_solo/join shortcut (cockpit view, glass cockpit, MFDs) and +# flies an EXPERT egg like every shipped one. What you see here is what the +# field sees. +# +# PASS = every death cycle pairs START -> RESET on its own node +# 0 SWALLOWED / 0 MISMATCH / 0 DISCARDED / 0 GHOST / 0 crash +# and each node's PLAYER_DEAD tally increments 1 per death. +# WATCH = the comms panel KILLS/DEATHS columns, and the blue respawn vortex. +set -x +. /c/git/bt411/scratchpad/night6/bench_common.sh +cd /c/git/bt411/content || exit 1 +bt_assert_player_env +rm -f mp4p_a.log mp4p_b.log mp4p_c.log mp4p_d.log +A=/c/git/bt411/scratchpad/night6/runs/$(date +%H%M%S); mkdir -p "$A"; mv matchlog_*.txt "$A"/ 2>/dev/null + +bt_expert_egg MP4.EGG MP4X.EGG + +# staggered first deaths so the cycles interleave instead of lining up +launch () { # $1=log $2=affinity $3=port $4=delay + BT_MP_LOG=1 BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 BT_PERF=1 \ + BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_DELAY=$4 BT_SELF_DAMAGE_REPEAT=1 \ + bt_launch "$1" MP4X.EGG "$2" -net "$3" +} +launch mp4p_d.log 0xC0 1801 34 +sleep 1 +launch mp4p_c.log 0x30 1701 41 +sleep 1 +launch mp4p_b.log 0x0C 1601 48 +sleep 1 +launch mp4p_a.log 0x03 1501 55 +sleep 5 +python ../tools/btconsole.py MP4X.EGG 127.0.0.1:1501 127.0.0.1:1601 \ + 127.0.0.1:1701 127.0.0.1:1801 & +PC=$! + +sleep 150 +kill $PC 2>/dev/null +taskkill //F //IM btl4.exe 2>/dev/null +rm -f MP4X.EGG +echo "=== DONE ===" diff --git a/scratchpad/night6/mp_ghost.sh b/scratchpad/night6/mp_ghost.sh index cf4c283..f3f9fce 100644 --- a/scratchpad/night6/mp_ghost.sh +++ b/scratchpad/night6/mp_ghost.sh @@ -14,11 +14,11 @@ sed 's/^experience=expert/experience=novice/' MP.EGG > MPG.EGG # B first (listener), then A -- same order as the working mp_skate.sh BT_LOG=mp_b.log BT_AFFINITY=0x2 BT_MP_LOG=1 BT_SCORE_LOG=1 BT_DEATH_LOG=1 \ - BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_DELAY=40 BT_SELF_DAMAGE_REPEAT=1 \ + BT_TLOC_LOG=1 BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_DELAY=40 BT_SELF_DAMAGE_REPEAT=1 \ ../build/Release/btl4.exe -egg MPG.EGG -net 1601 & sleep 2 BT_LOG=mp_a.log BT_AFFINITY=0x1 BT_MP_LOG=1 BT_SCORE_LOG=1 BT_DEATH_LOG=1 \ - BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_DELAY=55 BT_SELF_DAMAGE_REPEAT=1 \ + BT_TLOC_LOG=1 BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_DELAY=55 BT_SELF_DAMAGE_REPEAT=1 \ ../build/Release/btl4.exe -egg MPG.EGG -net 1501 & sleep 4 python ../tools/btconsole.py MPG.EGG 127.0.0.1:1501 127.0.0.1:1601 &