[CmdletBinding()] param( [string]$ClientPath = '', [string]$PackagePath = '', [ValidateSet('Debug', 'Release')] [string]$Configuration = 'Release', [switch]$SkipDataBuild, [switch]$SkipManifest, [switch]$NativeRenderer, [switch]$StopClient, [switch]$Launch ) $ErrorActionPreference = 'Stop' $repoRoot = $PSScriptRoot function Import-ProjectEnvironment { $envFile = Join-Path $repoRoot '.env' if (-not (Test-Path -LiteralPath $envFile -PathType Leaf)) { return } foreach ($line in Get-Content -LiteralPath $envFile -Encoding UTF8) { if ($line -notmatch '^\s*([^#][^=]+)=(.*)$') { continue } $name = $matches[1].Trim() $value = $matches[2].Trim().Trim('"').Trim("'") if ($name -and -not (Test-Path "Env:$name")) { Set-Item -Path "Env:$name" -Value $value } } } Import-ProjectEnvironment if ([string]::IsNullOrWhiteSpace($ClientPath)) { $ClientPath = if ($env:WOW_HOME) { $env:WOW_HOME } else { 'C:\Program Files (x86)\World of Warcraft' } } if ([string]::IsNullOrWhiteSpace($PackagePath)) { $PackagePath = Join-Path $repoRoot 'dist' } $ClientPath = [System.IO.Path]::GetFullPath($ClientPath) $PackagePath = [System.IO.Path]::GetFullPath($PackagePath) if (-not (Test-Path -LiteralPath $ClientPath -PathType Container)) { throw "Client directory was not found: $ClientPath" } $runningClients = @(Get-Process Wow -ErrorAction SilentlyContinue | Where-Object { $_.Path -and $_.Path.StartsWith($ClientPath, [System.StringComparison]::OrdinalIgnoreCase) }) if ($runningClients.Count) { if (-not $StopClient) { throw 'Wow.exe is running. Close the client or repeat deployment with -StopClient.' } Write-Host 'Stopping the running WoW client...' $runningClients | Stop-Process -Force Start-Sleep -Seconds 2 Get-Process WarcraftXLHost -ErrorAction SilentlyContinue | Where-Object { $_.Path -and $_.Path.StartsWith($ClientPath, [System.StringComparison]::OrdinalIgnoreCase) } | Stop-Process -Force } $env:WOW_HOME = $ClientPath Write-Host "MoonWell client: $ClientPath" Write-Host "Package staging: $PackagePath" Write-Host '[1/5] Initializing WarcraftXL dependencies...' & git -C $repoRoot submodule update --init --recursive if ($LASTEXITCODE -ne 0) { throw 'Git submodule initialization failed.' } New-Item -ItemType Directory -Path $PackagePath -Force | Out-Null if (-not $SkipDataBuild) { Write-Host '[2/5] Building MPQ patches...' $tool = Join-Path $repoRoot 'tool\target\release\tool.exe' if (-not (Test-Path -LiteralPath $tool -PathType Leaf)) { $cargo = Get-Command cargo -ErrorAction SilentlyContinue if (-not $cargo) { throw 'Cargo was not found and the MPQ builder is not compiled.' } & $cargo.Source build --release --manifest-path (Join-Path $repoRoot 'tool\Cargo.toml') if ($LASTEXITCODE -ne 0) { throw 'MPQ builder compilation failed.' } } & $tool (Join-Path $repoRoot 'src') $PackagePath if ($LASTEXITCODE -ne 0) { throw 'MPQ patch build failed.' } } else { Write-Host '[2/5] MPQ build skipped.' } Write-Host '[3/5] Building and installing WarcraftXL...' $buildArguments = @{ Configuration = $Configuration ClientPath = $ClientPath PackagePath = $PackagePath Deploy = $true } if ($NativeRenderer) { $buildArguments.NativeRenderer = $true } & (Join-Path $repoRoot 'build-warcraftxl.ps1') @buildArguments if ($LASTEXITCODE -ne 0) { throw 'WarcraftXL deployment failed.' } Write-Host '[4/5] Synchronizing package files...' & robocopy $PackagePath $ClientPath /E /R:2 /W:1 /NFL /NDL /NJH /NJS /NP $robocopyExitCode = $LASTEXITCODE if ($robocopyExitCode -ge 8) { throw "Package synchronization failed with robocopy exit code $robocopyExitCode." } if (-not $SkipManifest) { Write-Host '[5/5] Building launcher manifest...' $python = Get-Command python -ErrorAction SilentlyContinue if (-not $python) { throw 'Python was not found; launcher manifest was not generated.' } & $python.Source (Join-Path $repoRoot 'build_manifest.py') ` --dir $PackagePath --output (Join-Path $repoRoot 'manifest.json') if ($LASTEXITCODE -ne 0) { throw 'Launcher manifest generation failed.' } } else { Write-Host '[5/5] Manifest generation skipped.' } $wowHash = (Get-FileHash -LiteralPath (Join-Path $ClientPath 'Wow.exe') -Algorithm SHA256).Hash $renderer = if ($NativeRenderer) { 'native D3D9 recovery proxy' } else { 'WarcraftXL D3D9On12' } Write-Host '' Write-Host 'Deployment complete.' Write-Host " Renderer: $renderer" Write-Host " Wow.exe SHA-256: $wowHash" Write-Host " Host: $(Join-Path $ClientPath 'Utils\WarcraftXLHost.exe')" if ($Launch) { Write-Host 'Launching Wow.exe...' Start-Process -FilePath (Join-Path $ClientPath 'Wow.exe') -WorkingDirectory $ClientPath } # robocopy uses successful non-zero exit codes; do not leak one to callers. $global:LASTEXITCODE = 0