Files
moonwell-client/run.ps1

204 lines
6.3 KiB
PowerShell

param(
[Parameter(Mandatory=$false)]
[ValidateSet("production", "ptr", "local")]
[string]$Env = "local"
)
# Stop on errors
$ErrorActionPreference = "Stop"
# --- Resolve project root (folder where script is located)
$ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path
. (Join-Path $ROOT 'pipeline-layout.ps1')
# --- Load .env if WOW_HOME not already set
if (-not $env:WOW_HOME) {
$envFile = Join-Path $ROOT ".env"
if (Test-Path $envFile) {
Write-Host "Loading .env file..."
Get-Content $envFile | ForEach-Object {
if ($_ -match "^\s*([^#][^=]+)=(.+)$") {
$name = $matches[1].Trim()
$value = $matches[2].Trim()
[System.Environment]::SetEnvironmentVariable($name, $value)
Set-Item -Path "Env:$name" -Value $value
}
}
}
}
# --- Validate WOW_HOME
if (-not $env:WOW_HOME) {
Write-Error "WOW_HOME is not set (env or .env)"
exit 1
}
$WOW_HOME = $env:WOW_HOME
Write-Host "WOW_HOME = $WOW_HOME"
function Test-ExistingFileWriteAccess {
param([Parameter(Mandatory=$true)][string]$Path)
try {
$stream = [System.IO.File]::Open(
$Path,
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::ReadWrite
)
$stream.Close()
return $true
} catch [System.UnauthorizedAccessException] {
return $false
} catch {
# Sharing violations are handled later by Stop-WowRuntime. They do not
# indicate that the whole script needs an elevated token.
return $true
}
}
$installedWarcraftXL = Join-Path $WOW_HOME "WarcraftXL.dll"
$isAdministrator = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator
)
if ((Test-Path -LiteralPath $installedWarcraftXL -PathType Leaf) -and
-not (Test-ExistingFileWriteAccess -Path $installedWarcraftXL) -and
-not $isAdministrator) {
Write-Host "WarcraftXL.dll requires administrator access. Requesting elevation..."
$elevatedProcess = Start-Process -FilePath "powershell.exe" -Verb RunAs -PassThru -Wait `
-WorkingDirectory $ROOT `
-ArgumentList "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$($MyInvocation.MyCommand.Path)`"", "-Env", $Env
exit $elevatedProcess.ExitCode
}
# --- Paths
$SRC_DIR = Join-Path $ROOT "src"
$DIST_DIR = Join-Path $ROOT "dist"
$TOOL = Join-Path $ROOT "tool\target\release\tool.exe"
$RELOAD_SCRIPT = Join-Path $ROOT "reload_wow.bat"
$WXL_BUILD_SCRIPT = Join-Path $ROOT "build-warcraftxl.ps1"
Assert-SafePackagePath `
-PackagePath $DIST_DIR `
-RepositoryPath $ROOT `
-ClientPath $WOW_HOME
function Stop-WowRuntime {
param([Parameter(Mandatory=$true)][string]$ClientPath)
$clientRoot = [System.IO.Path]::GetFullPath($ClientPath).TrimEnd('\') + '\'
$runtimeProcesses = @(Get-Process -Name "Wow" -ErrorAction SilentlyContinue | Where-Object {
try {
$_.Path -and $_.Path.StartsWith($clientRoot, [System.StringComparison]::OrdinalIgnoreCase)
} catch {
$false
}
})
if (-not $runtimeProcesses.Count) {
return
}
Write-Host "Stopping the running WoW client..."
$runtimeProcesses | Stop-Process -Force
try {
$runtimeProcesses | Wait-Process -Timeout 10 -ErrorAction Stop
} catch {
$stillRunning = @($runtimeProcesses | Where-Object { -not $_.HasExited })
if ($stillRunning.Count) {
$processNames = ($stillRunning | ForEach-Object { "$($_.ProcessName) (PID $($_.Id))" }) -join ", "
throw "Could not stop processes that lock WarcraftXL files: $processNames"
}
}
}
# --- Ensure tool exists
if (!(Test-Path $TOOL)) {
Write-Host "Building Rust tool..."
Push-Location (Join-Path $ROOT "tool")
cargo build --release
Pop-Location
}
# --- Build MPQ archives into a clean dist/
$previousPackageFiles = @(
Get-RelativeFilePaths -Root $DIST_DIR
)
Reset-MoonWellPackageStaging `
-PackagePath $DIST_DIR `
-RepositoryPath $ROOT `
-ClientPath $WOW_HOME
Write-Host "Building MPQ archives from src/ -> dist/..."
& $TOOL $SRC_DIR $DIST_DIR
if ($LASTEXITCODE -ne 0) {
Write-Error "MPQ build failed!"
exit 1
}
# --- Verify result
if (!(Test-Path (Join-Path $DIST_DIR "Data"))) {
Write-Error "dist\Data not found after build!"
exit 1
}
# --- Build/package/deploy WarcraftXL before the dist sync. The packaged files
# are uploaded by upload_to_s3.py and consumed by the launcher manifest.
Stop-WowRuntime -ClientPath $WOW_HOME
Write-Host "Building and packaging WarcraftXL..."
& $WXL_BUILD_SCRIPT -Configuration Release -ClientPath $WOW_HOME `
-PackagePath $DIST_DIR -Deploy
if ($LASTEXITCODE -ne 0) {
Write-Error "WarcraftXL build/deploy failed!"
exit 1
}
# --- Sync dist/ -> WOW_HOME
Write-Host "Syncing dist/ -> WOW_HOME..."
$python = Get-Command python -ErrorAction SilentlyContinue
if (-not $python) {
Write-Error "Python is required for package validation."
exit 1
}
& $python.Source (Join-Path $ROOT 'validate_package.py') --dir $DIST_DIR
if ($LASTEXITCODE -ne 0) {
Write-Error "Package validation failed!"
exit 1
}
$currentPackageFiles = @(Get-RelativeFilePaths -Root $DIST_DIR)
Remove-StalePackageFilesFromClient `
-ClientPath $WOW_HOME `
-PreviousFiles $previousPackageFiles `
-CurrentFiles $currentPackageFiles
Remove-MoonWellRuntimeGarbage -Destination $WOW_HOME
robocopy $DIST_DIR $WOW_HOME /E /NFL /NDL
# robocopy exit codes 0-7 are success/warnings; 8+ are errors
if ($LASTEXITCODE -ge 8) {
Write-Error "robocopy failed with exit code $LASTEXITCODE"
exit 1
}
Write-Host "MPQ archives deployed to $WOW_HOME"
# --- Write realmlist.wtf based on selected environment
$realmlist = switch ($Env) {
"production" { $env:PRODUCTION_REALMLIST }
"ptr" { $env:PTR_REALMLIST }
"local" { $env:LOCAL_REALMLIST }
}
if ($realmlist) {
$realmlistPath = Join-Path $WOW_HOME "Data\ruRU\realmlist.wtf"
Set-Content -Path $realmlistPath -Value "set realmlist $realmlist" -Encoding ASCII
Write-Host "Realmlist ($Env): $realmlist"
} else {
Write-Warning "REALMLIST for '$Env' is not set in .env -- skipping realmlist.wtf"
}
# --- Run WoW reload script
Write-Host "Launching WoW..."
cmd /c $RELOAD_SCRIPT