GSP-Agent-Windows/Installer/Install-GSP-WindowsAgent.ps1
2026-06-10 19:54:11 -04:00

466 lines
16 KiB
PowerShell

# GSP Windows Agent Installer - PowerShell Script
# This script handles the complete installation of GSP Windows Agent
#Requires -RunAsAdministrator
param(
[string]$InstallPath = "C:\GSP",
[string]$GitRepoUrl = "https://github.com/OpenGamePanel/OGP-Agent-Windows.git",
[string]$GitBranch = "master",
[string]$PortableGitPath,
[switch]$SkipGitExtraction = $false
)
# Get the directory where this script is located
$Script:ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
if ([string]::IsNullOrEmpty($PortableGitPath)) {
$PortableGitPath = Join-Path $Script:ScriptDir "PortableGit-2.54.0-64-bit.7z.exe"
}
$Script:AgentDir = Join-Path $InstallPath "Agent"
$Script:GitDir = Join-Path $InstallPath "tools\PortableGit"
$Script:HomeDir = Join-Path $InstallPath "home"
$Script:BackupDir = Join-Path $InstallPath "backups\agent"
$Script:LogsDir = Join-Path $InstallPath "logs"
$Script:InstallLogFile = Join-Path $Script:LogsDir "install.log"
# Initialize log
if (-not (Test-Path $Script:LogsDir)) {
New-Item -ItemType Directory -Path $Script:LogsDir -Force | Out-Null
}
function Log-Message {
param([string]$Message, [switch]$IsError = $false)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logLine = "[$timestamp] $Message"
if ($IsError) {
Write-Host $logLine -ForegroundColor Red
} else {
Write-Host $logLine
}
Add-Content -Path $Script:InstallLogFile -Value $logLine -Force
}
function Test-Administrator {
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Create-DirectoryStructure {
Log-Message "Creating directory structure at $InstallPath..."
try {
$dirs = @(
$InstallPath,
(Join-Path $InstallPath "tools"),
(Join-Path $InstallPath "backups"),
$Script:BackupDir,
$Script:HomeDir,
$Script:LogsDir
)
foreach ($dir in $dirs) {
if (-not (Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
Log-Message " Created: $dir"
} else {
Log-Message " Already exists: $dir"
}
}
return $true
} catch {
Log-Message "ERROR creating directories: $_" -IsError $true
return $false
}
}
function Extract-PortableGit {
Log-Message "Extracting Portable Git..."
if (-not (Test-Path $PortableGitPath)) {
Log-Message "ERROR: Portable Git installer not found at: $PortableGitPath" -IsError $true
return $false
}
if ($SkipGitExtraction) {
Log-Message "Skipping Portable Git extraction (--SkipGitExtraction flag set)"
return $true
}
try {
# Remove existing extraction if present
if (Test-Path $Script:GitDir) {
Log-Message "Removing existing PortableGit directory..."
Remove-Item -Path $Script:GitDir -Recurse -Force
}
# Create parent directory for extraction
New-Item -ItemType Directory -Path (Split-Path $Script:GitDir) -Force | Out-Null
# Extract the 7z archive
Log-Message "Running 7z extraction: $PortableGitPath"
Log-Message "Extracting to: $Script:GitDir"
$extractProcess = Start-Process -FilePath $PortableGitPath `
-ArgumentList "-o`"$Script:GitDir`" -y" `
-Wait -PassThru -NoNewWindow
if ($extractProcess.ExitCode -ne 0) {
Log-Message "ERROR: 7z extraction failed with exit code: $($extractProcess.ExitCode)" -IsError $true
return $false
}
# Verify git.exe exists
$gitExe = Join-Path $Script:GitDir "cmd\git.exe"
if (-not (Test-Path $gitExe)) {
Log-Message "ERROR: git.exe not found after extraction at: $gitExe" -IsError $true
return $false
}
Log-Message "Portable Git extracted successfully"
return $true
} catch {
Log-Message "ERROR extracting Portable Git: $_" -IsError $true
return $false
}
}
function Backup-ExistingAgent {
Log-Message "Checking for existing agent installation..."
if (-not (Test-Path $Script:AgentDir)) {
Log-Message "No existing agent found"
return $true
}
# Check if it's a git-managed installation
$gitDir = Join-Path $Script:AgentDir ".git"
if (Test-Path $gitDir) {
Log-Message "Found existing Git-managed installation"
Log-Message "Offering update/reconfigure instead of reinstall"
$response = Read-Host "Updates available. Do you want to update? (Y/N)"
if ($response -eq "Y" -or $response -eq "y") {
Log-Message "Proceding with update instead of fresh install"
# For now, we'll continue with update logic
return $true
} else {
Log-Message "User cancelled update"
return $false
}
} else {
# Backup old manual installation
$backupName = "manual-install-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
$backupPath = Join-Path $Script:BackupDir $backupName
Log-Message "Backing up existing manual installation..."
Log-Message "Backup destination: $backupPath"
try {
Copy-Item -Path $Script:AgentDir -Destination $backupPath -Recurse -Force
Log-Message "Backup completed successfully"
# Preserve specific folders from old installation
$preserveFolders = @("Cfg", "steamcmd", "screenlogs", "startups", "tmp", "shared")
Log-Message "Marked folders for preservation after clone: $($preserveFolders -join ', ')"
return $true
} catch {
Log-Message "ERROR creating backup: $_" -IsError $true
return $false
}
}
}
function Clone-Repository {
Log-Message "Cloning Windows Agent repository..."
$gitExe = Join-Path $Script:GitDir "cmd\git.exe"
if (-not (Test-Path $gitExe)) {
Log-Message "ERROR: git.exe not found at: $gitExe" -IsError $true
return $false
}
# Preserve existing folders if agent directory exists
$preservedFolders = @{}
$preserveFolders = @("Cfg", "steamcmd", "screenlogs", "startups", "tmp", "shared")
if (Test-Path $Script:AgentDir) {
foreach ($folder in $preserveFolders) {
$folderPath = Join-Path $Script:AgentDir $folder
if (Test-Path $folderPath) {
$tempPath = Join-Path $env:TEMP "gsp_preserve_$folder"
Log-Message "Preserving existing folder: $folder"
Copy-Item -Path $folderPath -Destination $tempPath -Recurse -Force
$preservedFolders[$folder] = $tempPath
}
}
# Remove old directory to clone fresh
Log-Message "Removing old agent directory for fresh clone..."
Remove-Item -Path $Script:AgentDir -Recurse -Force -ErrorAction SilentlyContinue
}
try {
Log-Message "Cloning from: $GitRepoUrl (branch: $GitBranch)"
$cloneProcess = Start-Process -FilePath $gitExe `
-ArgumentList "clone", "--branch", $GitBranch, $GitRepoUrl, $Script:AgentDir `
-Wait -PassThru -NoNewWindow -WorkingDirectory $InstallPath
if ($cloneProcess.ExitCode -ne 0) {
Log-Message "ERROR: Git clone failed with exit code: $($cloneProcess.ExitCode)" -IsError $true
return $false
}
Log-Message "Repository cloned successfully"
# Restore preserved folders
foreach ($folder in $preservedFolders.Keys) {
$tempPath = $preservedFolders[$folder]
$targetPath = Join-Path $Script:AgentDir $folder
Log-Message "Restoring preserved folder: $folder"
if (Test-Path $targetPath) {
Remove-Item -Path $targetPath -Recurse -Force
}
Copy-Item -Path $tempPath -Destination $targetPath -Recurse -Force
Remove-Item -Path $tempPath -Recurse -Force
}
return $true
} catch {
Log-Message "ERROR cloning repository: $_" -IsError $true
return $false
}
}
function Create-RequiredRuntimeFolders {
Log-Message "Creating runtime folders in agent directory..."
$runtimeFolders = @(
"runtime_status",
"screenlogs",
"startups",
"tmp",
"shared"
)
try {
foreach ($folder in $runtimeFolders) {
$folderPath = Join-Path $Script:AgentDir $folder
if (-not (Test-Path $folderPath)) {
New-Item -ItemType Directory -Path $folderPath -Force | Out-Null
Log-Message " Created: $folder"
}
}
return $true
} catch {
Log-Message "ERROR creating runtime folders: $_" -IsError $true
return $false
}
}
function Configure-Agent {
Log-Message "Configuring agent..."
try {
# Call the configuration script if it exists
$configScript = Join-Path $Script:ScriptDir "Configure-GSP-WindowsAgent.ps1"
if (Test-Path $configScript) {
Log-Message "Running configuration script..."
& $configScript -AgentPath $Script:AgentDir -InstallPath $InstallPath
} else {
Log-Message "WARNING: Configuration script not found, skipping configuration"
Log-Message "You can run configuration manually later with: Configure-GSP-WindowsAgent.ps1"
}
return $true
} catch {
Log-Message "ERROR during configuration: $_" -IsError $true
return $false
}
}
function Create-ScheduledTask {
Log-Message "Creating Windows Task Scheduler entry..."
try {
# Path to the agent start script in the cloned repo
$agentStartScript = Join-Path $Script:AgentDir "OGP64\agent_start.bat"
if (-not (Test-Path $agentStartScript)) {
Log-Message "WARNING: agent_start.bat not found at expected location"
Log-Message "Task creation skipped - you may need to create the task manually"
return $true
}
# Remove existing task if present
try {
Unregister-ScheduledTask -TaskName "GSP Windows Agent" -Confirm:$false -ErrorAction SilentlyContinue
Log-Message "Removed existing task"
} catch {
# Task doesn't exist or couldn't be removed, continue
}
# Create task action
$action = New-ScheduledTaskAction `
-Execute $agentStartScript `
-WorkingDirectory (Split-Path $agentStartScript)
# Create task trigger at system startup
$trigger = New-ScheduledTaskTrigger -AtStartup
# Create task settings
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-Compatibility Win7
# Create and register the task
$principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$task = New-ScheduledTask `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Description "GSP Windows Game Server Agent - Auto-start at system boot"
Register-ScheduledTask -TaskName "GSP Windows Agent" -InputObject $task | Out-Null
Log-Message "Scheduled task created successfully"
Log-Message "Task name: GSP Windows Agent"
Log-Message "Trigger: At system startup"
Log-Message "Privileges: System (highest)"
return $true
} catch {
Log-Message "ERROR creating scheduled task: $_" -IsError $true
Log-Message "You may need to create the task manually or run with elevated privileges"
return $false
}
}
function Start-Agent {
Log-Message "Starting agent..."
try {
$agentStartScript = Join-Path $Script:AgentDir "OGP64\agent_start.bat"
if (-not (Test-Path $agentStartScript)) {
Log-Message "WARNING: agent_start.bat not found"
return $false
}
Log-Message "Agent start script located at: $agentStartScript"
Log-Message "To start the agent manually, run: $agentStartScript"
# Note: We don't auto-start the agent here because it needs to run at system startup
# via the scheduled task, which handles Cygwin environment setup properly
return $true
} catch {
Log-Message "ERROR starting agent: $_" -IsError $true
return $false
}
}
function Test-AdminPrivileges {
if (-not (Test-Administrator)) {
Log-Message "ERROR: This script must be run as Administrator" -IsError $true
Log-Message "Please run this script with Administrator privileges"
return $false
}
Log-Message "Administrator privileges verified"
return $true
}
# Main installation flow
function Main {
Log-Message "========================================="
Log-Message "GSP Windows Agent Installation"
Log-Message "========================================="
Log-Message "Install Path: $InstallPath"
Log-Message "Git Repository: $GitRepoUrl"
Log-Message "Git Branch: $GitBranch"
Log-Message ""
# Check admin
if (-not (Test-AdminPrivileges)) {
exit 1
}
# Create directory structure
if (-not (Create-DirectoryStructure)) {
Log-Message "FATAL: Failed to create directory structure" -IsError $true
exit 1
}
# Extract Portable Git
if (-not (Extract-PortableGit)) {
Log-Message "FATAL: Failed to extract Portable Git" -IsError $true
exit 1
}
# Backup existing installation if present
if (-not (Backup-ExistingAgent)) {
Log-Message "Installation cancelled or backup failed" -IsError $true
exit 1
}
# Clone repository
if (-not (Clone-Repository)) {
Log-Message "FATAL: Failed to clone repository" -IsError $true
exit 1
}
# Create runtime folders
if (-not (Create-RequiredRuntimeFolders)) {
Log-Message "WARNING: Failed to create some runtime folders" -IsError $true
# Don't exit - these may already exist
}
# Configure agent
if (-not (Configure-Agent)) {
Log-Message "WARNING: Configuration had errors" -IsError $true
# Don't exit - user can configure manually
}
# Create scheduled task
if (-not (Create-ScheduledTask)) {
Log-Message "WARNING: Failed to create scheduled task" -IsError $true
# Don't exit - user can create manually
}
# Start the scheduled task
try {
Start-ScheduledTask -TaskName "GSP Windows Agent" -ErrorAction SilentlyContinue
Log-Message "Scheduled task started"
} catch {
Log-Message "WARNING: Could not start scheduled task immediately - it will start at next boot"
}
Log-Message ""
Log-Message "========================================="
Log-Message "Installation Complete!"
Log-Message "========================================="
Log-Message "Install location: $InstallPath"
Log-Message "Agent will start at next system boot"
Log-Message "Log file: $Script:InstallLogFile"
Log-Message ""
Log-Message "To update the agent later, run: update.bat"
Log-Message "To rollback to a previous version, run: rollback.bat"
Log-Message ""
Read-Host "Press Enter to close this window"
}
# Run main installation
Main