diff --git a/.gitignore b/.gitignore index abc7bc2..c221ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ # build output /build/ /build-steam/ + +# packaged distribution (pack-dist.ps1 output) + its zip +/dist/ +/BattleTech-*.zip *.obj *.pdb *.ilk diff --git a/docs/BT412-ROADMAP.md b/docs/BT412-ROADMAP.md index 485f5f9..afded90 100644 --- a/docs/BT412-ROADMAP.md +++ b/docs/BT412-ROADMAP.md @@ -206,6 +206,20 @@ identical-to-baseline → take RP412's file verbatim, diverged → hand-apply th - **Verify**: dist zip runs on a clean machine (no DXSDK); solo loop; packaged 2-machine Steam session. +> **Status (2026-07-16): PACKAGING SCRIPTED (test dist).** `pack-dist.ps1` (repo root) +> assembles `dist\` from a **Release** Steam build (`build-steam\Release\btl4.exe` — Release +> so it runs without VS's non-redistributable debug CRT) + `steam_api.dll` + `OpenAL32.dll`, +> the `content\` data (AUDIO/GAUGE/VIDEO, BTL4.RES, BTDPL.INI, the VREND manifests, +> bindings.txt, a few eggs), `oalinst.exe` (OpenAL fallback), `tools\btconsole.py`, a +> `steam_appid.txt=480` (TEST only), a self-documenting `environ.ini` (BT412STEAM=1, +> L4MFDSPLIT cockpit), `start.bat`, and a README that states the multiplayer status plainly. +> `-Zip` emits `BattleTech-4.12.zip` (~46 MB). Verified: the Release exe boots a live DEV.EGG +> mission (optimizer-clean), and the packaged exe run from `dist\` brings the Steam transport +> up (FakeIP allocated, lobby available) — self-contained. **Remaining for a real release:** +> KeyLight gate (`L4KEYLIGHT` not yet compiled in), a real AppID (drop `steam_appid.txt`), +> the clean-machine acceptance test, and the deferred host→member mission feed +> (`InstallNetworkMission`) before a full Steam mission plays end-to-end. + ## Copy matrix (from RP412) | Treatment | Files | diff --git a/pack-dist.ps1 b/pack-dist.ps1 new file mode 100644 index 0000000..37576fc --- /dev/null +++ b/pack-dist.ps1 @@ -0,0 +1,240 @@ +# ============================================================================ +# 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 the STEAM LOBBY. What works vs. what is still +in progress: + + WORKS - Steam lobby room. 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 to fire the roster. -> Test: two machines, each + signed into a DIFFERENT Steam account, both running this build; confirm + they find each other, see the member list, and the launch fires. + + NOT FINISHED - the networked mission itself. When the host launches, each + member is put into "network pod" mode waiting for the host to stream the + mission over the wire. That host-as-console streaming marshal is the one + remaining piece, so a host+member mission does NOT fully connect and play + yet. The lobby mechanics above are what to exercise in this build. + + CLASSIC MULTIPLAYER (works today, no Steam): the proven path is 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 :1501 :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" +}