Add basic PowerShell module for SteamCMD

This commit is contained in:
Pat Hartl 2026-01-20 00:48:13 -06:00
parent fec743a630
commit 676898608b
2 changed files with 214 additions and 0 deletions

View file

@ -0,0 +1,14 @@
@{
RootModule = 'SteamCMD.psm1'
ModuleVersion = '1.0.0'
GUID = 'd9e2b2b7-7c4a-4c1c-9c33-93d5f65a4a21'
Author = 'Pat Hartl'
CompanyName = 'LANCommander'
Description = 'Install and update Steam games using SteamCMD'
PowerShellVersion = '7.0'
FunctionsToExport = @('Install-SteamGame')
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
}

View file

@ -0,0 +1,200 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Invoke-SteamCmd {
[CmdletBinding()]
param(
[Parameter(Mandatory)] [string] $SteamCmdPath,
[Parameter(Mandatory)] [string[]] $Arguments,
[Parameter()] [string] $LogPath = ""
)
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $SteamCmdPath
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
foreach ($a in $Arguments) { [void]$psi.ArgumentList.Add($a) }
$proc = [System.Diagnostics.Process]::new()
$proc.StartInfo = $psi
[void]$proc.Start()
$stdout = $proc.StandardOutput.ReadToEnd()
$stderr = $proc.StandardError.ReadToEnd()
$proc.WaitForExit()
$combined = @(
"===== SteamCMD STDOUT ====="
$stdout
"===== SteamCMD STDERR ====="
$stderr
) -join "`n"
if ($LogPath) {
$dir = Split-Path -Parent $LogPath
if ($dir) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
Set-Content -LiteralPath $LogPath -Value $combined -Encoding UTF8
}
[PSCustomObject]@{
ExitCode = $proc.ExitCode
StdOut = $stdout
StdErr = $stderr
Combined = $combined
}
}
function Install-SteamGame {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[int] $AppId,
[Parameter(Mandatory)]
[string] $GameName,
[Parameter(Mandatory)]
[string] $InstallRoot,
[Parameter()]
[string] $SteamCmdPath = "steamcmd",
[Parameter()]
[string] $Username = "anonymous",
[Parameter()]
[string] $Password = "",
[Parameter()]
[string] $Branch = "",
[Parameter()]
[string] $BranchPassword = "",
[Parameter()]
[switch] $Validate,
[Parameter()]
[switch] $IncludeSteamNews = $true
)
function Join-PathPortable([string] $A, [string] $B) {
if ($A.EndsWith("/") -or $A.EndsWith("\")) { return "$A$B" }
if ($A -match "^[a-zA-Z]:\\") { return "$A\$B" }
return "$A/$B"
}
function Find-AppManifest([string] $InstallDir, [int] $AppIdValue) {
$expected = Join-PathPortable $InstallDir ("steamapps/appmanifest_{0}.acf" -f $AppIdValue)
if (Test-Path -LiteralPath $expected) { return $expected }
Get-ChildItem -LiteralPath $InstallDir -Recurse -File `
-Filter ("appmanifest_{0}.acf" -f $AppIdValue) `
-ErrorAction SilentlyContinue |
Select-Object -First 1 |
ForEach-Object FullName
}
function Parse-AppManifestBuildInfo([string] $ManifestPath) {
$content = Get-Content -LiteralPath $ManifestPath -Raw
$buildId = [regex]::Match($content, '"buildid"\s+"(\d+)"').Groups[1].Value
$lastUpdated = [regex]::Match($content, '"LastUpdated"\s+"(\d+)"').Groups[1].Value
[PSCustomObject]@{
BuildId = $buildId
LastUpdatedUtc = if ($lastUpdated) {
[DateTimeOffset]::FromUnixTimeSeconds([int64]$lastUpdated).UtcDateTime
}
}
}
function Get-SteamNewsChangelog([int] $AppIdValue) {
try {
$uri = "https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid=$AppIdValue&count=3&maxlength=800&format=json"
$resp = Invoke-RestMethod -Uri $uri -TimeoutSec 15
$resp.appnews.newsitems |
ForEach-Object {
"[{0}] {1}`n{2}" -f `
([DateTimeOffset]::FromUnixTimeSeconds($_.date).ToString("u")),
$_.title,
$_.contents
} -join "`n`n"
}
catch {
""
}
}
# ---- Install ----
$installDir = Join-PathPortable $InstallRoot $GameName
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
$args = @(
"+force_install_dir", $installDir,
"+login", $Username
)
if ($Username -ne "anonymous" -and $Password) {
$args += $Password
}
$args += "+app_update", $AppId
if ($Branch) {
$args += "-beta", $Branch
if ($BranchPassword) {
$args += "-betapassword", $BranchPassword
}
}
if ($Validate) {
$args += "validate"
}
$args += "+quit"
$logPath = Join-Path -Path $installDir -ChildPath "steamcmd.log"
$result = Invoke-SteamCmd -SteamCmdPath $SteamCmdPath -Arguments $args -LogPath $logPath
if ($result.ExitCode -ne 0) {
$tail = ($result.Combined -split "`r?`n" | Select-Object -Last 80) -join "`n"
throw @(
"SteamCMD failed with exit code $($result.ExitCode)."
"Log: $logPath"
""
"Last 80 lines:"
$tail
) -join "`n"
}
# ---- Version / Changelog ----
$manifest = Find-AppManifest -InstallDir $installDir -AppIdValue $AppId
$build = if ($manifest) { Parse-AppManifestBuildInfo $manifest }
$version = $build?.BuildId ?? "unknown"
$changelog = if ($IncludeSteamNews) {
Get-SteamNewsChangelog $AppId
}
if (-not $changelog) {
$changelog = "BuildID: $version"
}
return [PSCustomObject]@{
Path = $installDir
Version = $version
Changelog = $changelog
}
}
Export-ModuleMember -Function Install-SteamGame