Files
BT412/pack-dist.ps1
T
CydandClaude Opus 4.8 77f019ed4f 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>
2026-07-17 07:30:17 -05:00

245 lines
11 KiB
PowerShell

# ============================================================================
# pack-dist.ps1 - assemble a runnable BattleTech 4.12 package into dist\
# ============================================================================
#
# Collects everything the game needs at run time:
# - build-steam\Release\btl4.exe (Steam build; Release so it runs on a
# machine WITHOUT Visual Studio -- the Debug CRT is not redistributable)
# - steam_api.dll + OpenAL32.dll beside the exe
# - game data from content\ (AUDIO, GAUGE, VIDEO, BTL4.RES, BTDPL.INI,
# the VREND manifests, bindings.txt, a few eggs) -- NOT the DOS-era
# arcade launch scripts / 16-bit stubs
# - steam_appid.txt = 480 (Spacewar) so Steam init works for TESTING; a
# real Steam release replaces this with the title's AppID
# - tools\btconsole.py for the classic (non-Steam) console-feed MP path
# - a self-documenting environ.ini + start.bat + README
#
# Usage: powershell -ExecutionPolicy Bypass -File pack-dist.ps1 [-Zip]
#
param(
[switch]$Zip
)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
$dist = Join-Path $root 'dist'
$content = Join-Path $root 'content'
$relDir = Join-Path $root 'build-steam\Release'
$exe = Join-Path $relDir 'btl4.exe'
if (-not (Test-Path $exe)) {
throw "build-steam\Release\btl4.exe not found - build the Steam Release first:`n" +
" cmake -S . -B build-steam -G `"Visual Studio 17 2022`" -A Win32 -DBT412_STEAM=ON`n" +
" cmake --build build-steam --config Release --target btl4"
}
# Refuse to touch a dist the game is currently running from.
$running = Get-Process btl4 -ErrorAction SilentlyContinue |
Where-Object { $_.Path -like "$dist\*" }
if ($running) {
throw "btl4.exe is running from $dist (PID $($running.Id)) - close the game first."
}
Write-Host "Packing into $dist"
if (Test-Path $dist) { Remove-Item -Recurse -Force $dist }
New-Item -ItemType Directory -Force $dist | Out-Null
New-Item -ItemType Directory -Force "$dist\tools" | Out-Null
# --- game binary + runtime DLLs --------------------------------------------
Copy-Item $exe $dist
$pdb = Join-Path $relDir 'btl4.pdb'
if (Test-Path $pdb) { Copy-Item $pdb $dist }
# steam_api.dll + OpenAL32.dll are staged beside the exe by the build.
foreach ($dll in 'steam_api.dll', 'OpenAL32.dll') {
Copy-Item (Join-Path $relDir $dll) $dist
}
# --- game data -------------------------------------------------------------
foreach ($dir in 'AUDIO', 'GAUGE', 'VIDEO') {
Write-Host " copying $dir..."
Copy-Item -Recurse (Join-Path $content $dir) $dist
}
# Required loose files: resources, renderer/DPL config, geometry+texture
# manifests (VREND.MNG is loaded by bgfload/L4VIDEO), the input bindings, and
# a handful of eggs (DEV = the -egg dev shortcut, MP = the console-feed target).
$files = 'BTL4.RES', 'BTDPL.INI', 'bindings.txt',
'VREND.MNG', 'VRENDMON.BTL', 'VRNOSTEX.MNG',
'DEV.EGG', 'MP.EGG', 'MP1.EGG'
foreach ($file in $files) {
$src = Join-Path $content $file
if (Test-Path $src) { Copy-Item $src $dist }
else { Write-Warning "content\$file missing - skipped" }
}
# --- OpenAL fallback installer + the console feeder ------------------------
$oalinst = Join-Path $root '..\RP412\assets\RP411\oalinst.exe'
if (Test-Path $oalinst) { Copy-Item $oalinst $dist }
else { Write-Warning "oalinst.exe not found (OpenAL fallback installer) - OpenAL32.dll still ships beside the exe" }
Copy-Item (Join-Path $root 'tools\btconsole.py') "$dist\tools"
# --- steam_appid.txt (TEST ONLY) -------------------------------------------
# 480 = Valve's Spacewar test app. Steamworks (SteamAPI_Init, FakeIP, lobbies)
# works against it so multiplayer can be tested before the title has a real
# AppID. A shipping Steam build replaces this with the real AppID (and Steam
# supplies it, so the file is removed).
Set-Content -Path "$dist\steam_appid.txt" -Encoding ascii -NoNewline -Value "480"
# --- desktop configuration -------------------------------------------------
Set-Content -Path "$dist\environ.ini" -Encoding ascii -Value @"
# ============================================================================
# environ.ini - BattleTech 4.12 configuration
# ============================================================================
# One KEY=VALUE per line, read at game start. Lines starting with # or ;
# are comments; anything without an = is ignored. Delete a line (or comment
# it out) to fall back to the built-in default.
#
# Input bindings live in bindings.txt beside the exe.
# ---- Core (the shipped desktop configuration) ------------------------------
# Control stack: tokens separated by ; or , processed left to right.
# PAD the virtual RIO (XInput controller + keyboard, rebindable)
# KEYBOARD the engine keyboard handler
# RIO real serial cockpit hardware (pod only)
L4CONTROLS=PAD;KEYBOARD
# Renderer bring-up argument (presence checked; any non-empty value works).
DPLARG=1
# DPL (renderer/scene) config file, searched beside the exe.
L4DPLCFG=BTDPL.INI
# Gauge/MFD canvas (a page of GAUGE\L4GAUGE.INI). Needed for the cockpit.
L4GAUGE=640x480x16
# Single-window cockpit: the 3D viewscreen framed by the six MFD surfaces
# (green) with their on-screen RIO button banks, on a 1920x1080 canvas
# auto-scaled to your monitor. 0/unset = the old 800x600 dev view.
L4MFDSPLIT=1
# Plasma glass rendered in-window (rank/ordinal readout). Unset = none.
L4PLASMA=SCREEN
# Simulation/render frame rate (fps). Desktop default 60; the pods ran 30.
TARGETFPS=60
# 1 = Steam networking (the lobby + FakeIP mesh). Needs the Steam client
# running and steam_appid.txt beside the exe; without them the game logs the
# reason and falls back to plain TCP. 0 = TCP only (use this for the classic
# btconsole.py console-feed multiplayer path -- see the README).
BT412STEAM=1
# ---- Optional --------------------------------------------------------------
# Mission-length override, integer seconds (short test missions). The menu's
# LENGTH choice sets this; uncomment to force it.
#BT_MISSION_SECONDS=120
# Anti-aliasing sample count passed to Direct3D 9 (0 = off, else 2..16).
#MULTISAMPLE=0
# Particle budget (default 8192).
#MAXPARTICLES=8192
# Fixed random seed for repeatable runs (any unsigned integer).
#RANDOM=12345
# ---- Classic LAN / console-feed multiplayer (BT412STEAM=0) -----------------
# The host-as-console egg feed for the STEAM lobby is not finished yet (see
# the README). The proven multiplayer path is the external console emulator
# feeding -net pods over TCP -- run tools\btconsole.py (Python 3). Example:
# 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
# Set BT412STEAM=0 above for this path.
"@
Set-Content -Path "$dist\start.bat" -Encoding ascii -Value @"
@echo off
rem BattleTech 4.12 - desktop build. No cockpit hardware needed.
rem Boots into the in-game mission setup menu (front end). With Steam
rem running (BT412STEAM=1 in environ.ini) the menu shows HOST / JOIN.
rem Add "-egg DEV.EGG" to skip the menu and run the canned solo mission.
cd /d "%~dp0"
start btl4.exe
"@
Set-Content -Path "$dist\README.txt" -Encoding ascii -Value @"
BattleTech 4.12 (desktop / Steam build)
========================================
Run start.bat. No cockpit hardware needed. If there is no sound, run
oalinst.exe once (installs the OpenAL runtime).
You boot into the in-game MISSION SETUP MENU (green-on-black): pick the map,
mech, color, time, weather, and mission length, type a pilot name, and press
LAUNCH. The single-window cockpit comes up -- the 3D viewscreen framed by six
green MFD surfaces with the pod's RIO button banks (click them with the mouse;
they light as the game commands their lamps). At the chosen length the mission
stops and a MISSION COMPLETE scoreboard shows, then it returns to the menu.
Controls (XInput controller and/or keyboard) - rebindable in bindings.txt:
Left stick / WASD drive (throttle holds; A/D steer)
X all stop
Space / A fire
mouse click the on-screen cockpit buttons
(see bindings.txt for the full default layout)
MULTIPLAYER - please read
-------------------------
This build is for testing STEAM multiplayer end to end.
Steam lobby + networked mission. With Steam running and BT412STEAM=1, the
menu shows HOST STEAM MISSION and JOIN STEAM MISSION. HOST creates a room;
JOIN finds and enters one. Everyone in the room sees the member list (name,
mech, color) and exchanges their FakeIP + loadout. The host presses LAUNCH
MISSION: it then acts as the in-process console -- it feeds every member the
mission over the wire, holds the whole mesh until all are staged, launches
them together, and stops the mission at the chosen length. The member pods
mesh with the host and each other; kills/deaths are collected and the same
results screen shows on every machine.
-> Test: two (or three) machines, each signed into a DIFFERENT Steam
account, all running this build. Confirm they find each other in the
room, the launch fires, everyone drops into the SAME mission and can
see/fight each other, and results show everywhere.
-> The host->member mission feed is NEWLY IMPLEMENTED. The console
protocol + launch handshake are loopback-verified here, but the full
multi-machine Steam mesh has not been lab-tested. If a member does not
drop in, grab btl4.log from each machine (grep [marshal], [frontend],
net-tx / net-rx) so it can be diagnosed.
CLASSIC MULTIPLAYER (no Steam, proven): the external console emulator feeding
-net pods over TCP. Needs Python 3.
Set BT412STEAM=0 in environ.ini, then:
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
Drive, fire, cross-pod kills and respawn all replicate on this path.
Steam notes (for the lobby test):
- steam_appid.txt (480 = Valve's Spacewar test app) ships beside the exe so
Steam init works before the title has its own AppID. Keep it for testing.
- Use a DIFFERENT Steam account per machine (Steam allows one running game
per account). Disable Steam Input for app 480 if the controller misbehaves.
- If Steam is not running, the game logs the reason and stays on TCP.
Logs: the game writes btl4.log beside the exe (set BT_LOG to rename). Grep it
for [frontend], [marshal], [steam], net-tx / net-rx when reporting an issue.
Source: https://gitea.mysticmachines.com/VWE/BT412
"@
# --- summary / optional zip ------------------------------------------------
$size = (Get-ChildItem $dist -Recurse | Measure-Object Length -Sum).Sum
Write-Host ("dist ready: {0:N1} MB -> $dist" -f ($size / 1MB))
if ($Zip) {
$zipPath = Join-Path $root 'BattleTech-4.12.zip'
Write-Host "zipping to $zipPath..."
if (Test-Path $zipPath) { Remove-Item -Force $zipPath }
Compress-Archive -Path "$dist\*" -DestinationPath $zipPath -Force
Write-Host "zip ready: $zipPath"
}