Files
gregCore/scripts/Invoke-DataCenterModDeploy.ps1

260 lines
9.2 KiB
PowerShell

Set-StrictMode -Version Latest
$script:DeployScriptRoot = if (-not [string]::IsNullOrWhiteSpace($PSScriptRoot)) {
$PSScriptRoot
}
elseif (-not [string]::IsNullOrWhiteSpace($PSCommandPath)) {
Split-Path -Parent $PSCommandPath
}
else {
(Get-Location).Path
}
function Get-ProjectAssemblyName {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$CsprojPath
)
try {
[xml]$projectXml = Get-Content -LiteralPath $CsprojPath -Raw -ErrorAction Stop
$assemblyNameNode = $projectXml.SelectSingleNode('//Project/PropertyGroup/AssemblyName')
if ($null -ne $assemblyNameNode -and -not [string]::IsNullOrWhiteSpace($assemblyNameNode.InnerText)) {
return [string]$assemblyNameNode.InnerText
}
}
catch {
}
return [System.IO.Path]::GetFileNameWithoutExtension($CsprojPath)
}
function Get-ExactGameProcesses {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ExecutablePath
)
$resolvedExecutablePath = [System.IO.Path]::GetFullPath($ExecutablePath)
$processes = @(Get-CimInstance Win32_Process -Filter "Name = 'Data Center.exe'" -ErrorAction SilentlyContinue)
foreach ($proc in $processes) {
if ([string]::IsNullOrWhiteSpace($proc.ExecutablePath)) {
continue
}
$resolvedProcPath = [System.IO.Path]::GetFullPath([string]$proc.ExecutablePath)
if ([string]::Equals($resolvedProcPath, $resolvedExecutablePath, [System.StringComparison]::OrdinalIgnoreCase)) {
[PSCustomObject]@{
Id = [int]$proc.ProcessId
Name = [string]$proc.Name
Path = [string]$proc.ExecutablePath
}
}
}
}
function Invoke-DataCenterModDeploy {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param(
[Parameter()]
[string]$ProjectPath = (Join-Path $script:DeployScriptRoot "..\framework\gregCore.csproj"),
[Parameter()]
[ValidateSet('Debug', 'Release')]
[string]$Configuration = 'Release',
[Parameter()]
[string]$TargetModsDirectory = 'C:\Program Files (x86)\Steam\steamapps\common\Data Center\Mods',
[Parameter()]
[string]$SteamUri = 'steam://rungameid/4170200',
[Parameter()]
[string]$GameExecutablePath = 'C:\Program Files (x86)\Steam\steamapps\common\Data Center\Data Center.exe',
[Parameter()]
[string[]]$GameProcessNames = @('DataCenter', 'datacenter'),
[Parameter()]
[switch]$SkipGameStart
)
$exactGameProcesses = @(Get-ExactGameProcesses -ExecutablePath $GameExecutablePath)
if ($exactGameProcesses.Count -gt 0) {
$exactSummary = ($exactGameProcesses | ForEach-Object { "{0} (PID {1})" -f $_.Path, $_.Id }) -join ', '
Write-Host "[Deploy] Exact game executable process(es) detected: $exactSummary"
if ($PSCmdlet.ShouldProcess($exactSummary, 'Stop exact Data Center.exe process(es) before deploy')) {
foreach ($proc in $exactGameProcesses) {
Stop-Process -Id $proc.Id -Force -ErrorAction Stop
}
$timeoutSeconds = 10
$sw = [System.Diagnostics.Stopwatch]::StartNew()
do {
Start-Sleep -Milliseconds 200
$stillExactRunning = @(Get-ExactGameProcesses -ExecutablePath $GameExecutablePath)
} while ($stillExactRunning.Count -gt 0 -and $sw.Elapsed.TotalSeconds -lt $timeoutSeconds)
if ($stillExactRunning.Count -gt 0) {
throw "Exact game executable process is still running after stop attempt. Aborting deploy."
}
}
}
$resolvedProjectPath = [System.IO.Path]::GetFullPath($ProjectPath)
if (-not (Test-Path -LiteralPath $resolvedProjectPath)) {
throw "Project file not found: $resolvedProjectPath"
}
$projectDirectory = Split-Path -Parent $resolvedProjectPath
$assemblyName = Get-ProjectAssemblyName -CsprojPath $resolvedProjectPath
$outputDllPath = Join-Path $projectDirectory ("bin\$Configuration\net6.0\$assemblyName.dll")
$targetDllPath = Join-Path $TargetModsDirectory ("$assemblyName.dll")
Write-Host "[Deploy] Project: $resolvedProjectPath"
Write-Host "[Deploy] Output DLL: $outputDllPath"
Write-Host "[Deploy] Target DLL: $targetDllPath"
$runningGameProcesses = @(Get-Process -ErrorAction SilentlyContinue | Where-Object {
$GameProcessNames -contains $_.ProcessName
})
if ($runningGameProcesses.Count -gt 0) {
$processSummary = ($runningGameProcesses | ForEach-Object { "{0} (PID {1})" -f $_.ProcessName, $_.Id }) -join ', '
Write-Host "[Deploy] Running game process(es) detected: $processSummary"
if ($PSCmdlet.ShouldProcess($processSummary, 'Stop running game process(es) before deploy')) {
foreach ($proc in $runningGameProcesses) {
Stop-Process -Id $proc.Id -Force -ErrorAction Stop
}
$timeoutSeconds = 10
$sw = [System.Diagnostics.Stopwatch]::StartNew()
do {
Start-Sleep -Milliseconds 200
$stillRunning = @(Get-Process -ErrorAction SilentlyContinue | Where-Object {
$GameProcessNames -contains $_.ProcessName
})
} while ($stillRunning.Count -gt 0 -and $sw.Elapsed.TotalSeconds -lt $timeoutSeconds)
if ($stillRunning.Count -gt 0) {
throw "Game process is still running after stop attempt. Aborting deploy."
}
}
}
if ($PSCmdlet.ShouldProcess($resolvedProjectPath, "Build project ($Configuration)")) {
& dotnet build $resolvedProjectPath -c $Configuration
if ($LASTEXITCODE -ne 0) {
throw "dotnet build failed with exit code $LASTEXITCODE"
}
}
if (-not (Test-Path -LiteralPath $outputDllPath)) {
if ($WhatIfPreference) {
Write-Host "[Deploy] WhatIf: output DLL check skipped: $outputDllPath"
return
}
throw "Built DLL not found: $outputDllPath"
}
if (-not (Test-Path -LiteralPath $TargetModsDirectory)) {
if ($PSCmdlet.ShouldProcess($TargetModsDirectory, 'Create target Mods directory')) {
New-Item -ItemType Directory -Path $TargetModsDirectory -Force | Out-Null
}
}
if ($PSCmdlet.ShouldProcess($targetDllPath, 'Copy and overwrite deployed DLL')) {
Copy-Item -LiteralPath $outputDllPath -Destination $targetDllPath -Force
Write-Host "[Deploy] Copied $assemblyName.dll to Mods directory."
}
if (-not $SkipGameStart) {
if ($PSCmdlet.ShouldProcess($SteamUri, 'Start game via Steam URI')) {
Start-Process $SteamUri
Write-Host '[Deploy] Game start triggered via Steam.'
}
}
}
function Invoke-Deploy {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param(
[Parameter(Position = 0)]
[ValidateSet('--1', '--all', '1', 'all', 'greg')]
[string]$Target = '--1',
[Parameter()]
[ValidateSet('Debug', 'Release')]
[string]$Configuration = 'Release',
[Parameter()]
[string]$TargetModsDirectory = 'C:\Program Files (x86)\Steam\steamapps\common\Data Center\Mods',
[Parameter()]
[string]$SteamUri = 'steam://rungameid/4170200',
[Parameter()]
[string]$GameExecutablePath = 'C:\Program Files (x86)\Steam\steamapps\common\Data Center\Data Center.exe',
[Parameter()]
[string[]]$GameProcessNames = @('DataCenter', 'datacenter'),
[Parameter()]
[switch]$SkipGameStart
)
$isAll = $Target -in @('--all', 'all')
$usegreg = $Target -in @('--1', '1', 'greg')
$projectPaths = @((Join-Path $script:DeployScriptRoot '..\framework\gregCore.csproj'))
$projectPaths = @($projectPaths)
Write-Host "[Deploy] Target selected: $(if ($isAll) { 'All (--all => gregCore)' } else { 'greg (--1)' })"
$failedTargets = @()
for ($index = 0; $index -lt $projectPaths.Count; $index++) {
$currentProjectPath = $projectPaths[$index]
$isLast = $index -eq ($projectPaths.Count - 1)
$effectiveSkipGameStart = if ($isLast) { $SkipGameStart } else { $true }
$targetLabel = [System.IO.Path]::GetFileName($currentProjectPath)
$deployParams = @{
ProjectPath = $currentProjectPath
Configuration = $Configuration
TargetModsDirectory = $TargetModsDirectory
SteamUri = $SteamUri
GameExecutablePath = $GameExecutablePath
GameProcessNames = $GameProcessNames
SkipGameStart = $effectiveSkipGameStart
WhatIf = $WhatIfPreference
}
try {
Invoke-DataCenterModDeploy @deployParams
}
catch {
if ($projectPaths.Count -gt 1) {
$failedTargets += $targetLabel
Write-Warning "[Deploy] Target failed and will be skipped: $targetLabel`n$($_.Exception.Message)"
continue
}
throw
}
}
if ($failedTargets.Count -gt 0) {
$failedSummary = ($failedTargets | Sort-Object -Unique) -join ', '
throw "One or more deploy targets failed: $failedSummary"
}
}