railroader-setons-special-s.../dist/build-common.ps1
seton 5e18cb84a1 MCP: localhost agent server with reloadable S3.Mcp.Tools plugin pack
Loopback MCP host with observe/control/develop gates and a hot-reload
tools DLL. Default token is local-dev only. Leave the module off unless
you are driving S3 from an agent.
2026-09-11 15:19:24 -04:00

154 lines
6.4 KiB
PowerShell

# Shared build helpers for S³ (dot-sourced by build-local.ps1 and build-release.ps1).
# Not run directly.
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$script:RepoRoot = Split-Path $PSScriptRoot -Parent
$script:ModId = "S3"
function Get-ModVersion {
# Single source of truth: the Version field in Info.json.
$info = Get-Content (Join-Path $RepoRoot "Info.json") -Raw | ConvertFrom-Json
return $info.Version
}
function Invoke-ManagedBuild {
param([string]$Configuration = "Release", [string]$GameDir = "")
Write-Host "=== Building managed (S3.dll, $Configuration) ===" -ForegroundColor Cyan
$csproj = Join-Path $RepoRoot "src\S3.csproj"
$args = @($csproj, "-c", $Configuration, "--nologo", "-v", "minimal")
if ($GameDir) { $args += "/p:GameDir=$GameDir" }
dotnet build @args | Out-Host
if ($LASTEXITCODE -ne 0) { throw "Managed build failed." }
$dll = Join-Path $RepoRoot "src\bin\$Configuration\S3.dll"
if (-not (Test-Path $dll)) { throw "S3.dll not found at $dll" }
return $dll
}
function Invoke-McpToolsBuild {
param([string]$Configuration = "Release", [string]$GameDir = "")
Write-Host "=== Building MCP tools (S3.Mcp.Tools.dll, $Configuration) ===" -ForegroundColor Cyan
$csproj = Join-Path $RepoRoot "mcp-tools\S3.Mcp.Tools.csproj"
$args = @($csproj, "-c", $Configuration, "--nologo", "-v", "minimal")
if ($GameDir) { $args += "/p:GameDir=$GameDir" }
dotnet build @args | Out-Host
if ($LASTEXITCODE -ne 0) { throw "MCP tools build failed." }
}
function Invoke-NativeBuild {
param([string]$Configuration = "Release")
$cmakeLists = Join-Path $RepoRoot "native\CMakeLists.txt"
if (-not (Test-Path $cmakeLists)) {
Write-Host "=== No native/CMakeLists.txt — skipping native build ===" -ForegroundColor DarkYellow
return $null
}
Write-Host "=== Building native (S3Native.dll, $Configuration) ===" -ForegroundColor Cyan
$buildDir = Join-Path $RepoRoot "native\build"
if (-not (Test-Path $buildDir)) { cmake -B $buildDir -A x64 (Join-Path $RepoRoot "native") | Out-Host }
cmake --build $buildDir --config $Configuration | Out-Host
if ($LASTEXITCODE -ne 0) { throw "Native build failed." }
# MSVC multi-config generators place the DLL in a per-config subfolder.
$dll = Join-Path $buildDir "bin\$Configuration\S3Native.dll"
if (-not (Test-Path $dll)) { $dll = Join-Path $buildDir "bin\S3Native.dll" } # single-config fallback
if (-not (Test-Path $dll)) { throw "S3Native.dll not found under $buildDir\bin" }
return $dll
}
function Test-ModDllLocked {
param([Parameter(Mandatory)][string]$Path)
if (-not (Test-Path $Path)) { return $false }
try {
[System.IO.File]::Open($Path, 'Open', 'ReadWrite', 'None').Close()
return $false
} catch {
return $true
}
}
function Get-LockedModDlls {
param([Parameter(Mandatory)][string]$DestModDir)
$locked = @()
foreach ($name in @("S3.dll", "S3Native.dll")) {
if (Test-ModDllLocked (Join-Path $DestModDir $name)) { $locked += $name }
}
return $locked
}
# Poll until Mods\S3 DLLs are writable. Railroader.exe is the usual locker;
# we wait on the file itself so a hung Unity process is still detected.
function Wait-ModUnlocked {
param(
[Parameter(Mandatory)][string]$DestModDir,
[int]$PollSeconds = 2
)
$locked = @(Get-LockedModDlls $DestModDir)
if ($locked.Count -eq 0) { return }
Write-Host "=== Waiting for Railroader to close ($($locked -join ', ') locked) ===" -ForegroundColor Yellow
Write-Host " Close the game; install will run automatically. Ctrl+C to abort." -ForegroundColor DarkYellow
while ($true) {
Start-Sleep -Seconds $PollSeconds
$still = @(Get-LockedModDlls $DestModDir)
if ($still.Count -eq 0) {
Write-Host "=== Unlocked, installing ===" -ForegroundColor Green
return
}
$rr = Get-Process -Name "Railroader" -ErrorAction SilentlyContinue
$hint = if ($rr) { "Railroader.exe still running" } else { "$($still -join ', ') still locked" }
Write-Host " ... $hint" -ForegroundColor DarkGray
}
}
# Assembles the Mods\S3 layout into $DestModDir (the S3 folder itself).
#
# Overwrites in place rather than wiping the folder. A full wipe is dangerous: if
# the game is running, a loaded DLL is locked, so the wipe deletes Info.json and
# the managed DLL, then aborts on the locked native DLL — leaving the mod with no
# manifest, so it silently vanishes from the loader. Overwriting in place keeps
# the last-good install if a copy fails, and preserves the user's settings files.
function New-ModLayout {
param([Parameter(Mandatory)][string]$DestModDir,
[Parameter(Mandatory)][string]$ManagedDll,
[string]$NativeDll = $null,
[switch]$Wait)
New-Item -ItemType Directory -Force -Path $DestModDir | Out-Null
if ($Wait) {
Wait-ModUnlocked -DestModDir $DestModDir
}
# Fail fast with a clear message if a DLL we're about to overwrite is locked
# (game still running) — before touching anything.
$locked = @(Get-LockedModDlls $DestModDir)
if ($locked.Count -gt 0) {
throw "$($locked -join ', ') is locked - close Railroader before installing (or rerun with -Wait)."
}
# Drop stale artifacts from older builds (the pre-rename native DLL, ngen
# caches) so they don't linger, but leave Info.json and settings intact.
$stale = Join-Path $DestModDir "RRPopout.dll"
if (Test-Path $stale) { Remove-Item $stale -Force }
Get-ChildItem $DestModDir -Filter "*.cache" -ErrorAction SilentlyContinue | Remove-Item -Force
Copy-Item (Join-Path $RepoRoot "Info.json") (Join-Path $DestModDir "Info.json") -Force
Copy-Item $ManagedDll (Join-Path $DestModDir "S3.dll") -Force
$icon = Join-Path $RepoRoot "src\Modules\QuickActions\Icons\consist.png"
if (Test-Path $icon) { Copy-Item $icon (Join-Path $DestModDir "consist.png") -Force }
if ($NativeDll) {
Copy-Item $NativeDll (Join-Path $DestModDir "S3Native.dll") -Force
# Ship ProggyClean.ttf alongside the native DLL so IMGUI_DISABLE_DEFAULT_FONT
# can load it at runtime without the base85 blob in the binary.
$fontSrc = Join-Path $RepoRoot "native\fonts\ProggyClean.ttf"
if (Test-Path $fontSrc) {
Copy-Item $fontSrc (Join-Path $DestModDir "ProggyClean.ttf") -Force
}
}
}