diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57d42a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Windows Agent Installation Artifacts +# These files should not be committed to the repository + +# Generated Config Files +OGP64/OGP/Cfg/Config.pm +OGP64/OGP/Cfg/Preferences.pm +OGP64/OGP/Cfg/bash_prefs.cfg + +# Runtime Files +OGP64/OGP/ogp_agent.log +OGP64/OGP/*.pid +OGP64/OGP/ogp_agent_run.pid +OGP64/OGP/OGP.pid +OGP64/var/run/*.pid + +# Screenlogs (server console output) +OGP64/OGP/screenlogs/ + +# Startup Hints +OGP64/OGP/startups/ + +# Runtime Status Files +OGP64/OGP/runtime_status/ + +# Temporary and Cache Files +OGP64/OGP/tmp/ +OGP64/OGP/shared/ +*.tmp +*.swp +*.swo +*~ + +# SteamCMD Cache +OGP64/OGP/steamcmd/linux32/ +OGP64/OGP/steamcmd/steamapps/ + +# Extracted Portable Git (included as 7z but not extracted) +tools/PortableGit/ + +# Logs and Backups (created at C:\GSP during installation) +logs/ +backups/ + +# Windows-specific files +Thumbs.db +*.lnk +.DS_Store + +# Editor and IDE Files +.vscode/ +.idea/ +*.swp +*.swo +*~ +*.sublime-workspace + +# Installer Artifacts (if extracted locally) +Installer/PortableGit-*/ + +# Agent Home Directories (customer servers) +home/ + +# Cygwin runtime (if accidentally committed) +OGP64/bin/ +OGP64/lib/ +OGP64/etc/ +OGP64/sbin/ +OGP64/usr/ + +# Do NOT ignore these important files +!Installer/PortableGit-2.54.0-64-bit.7z.exe +! \ No newline at end of file diff --git a/Installer/Configure-GSP-WindowsAgent.ps1 b/Installer/Configure-GSP-WindowsAgent.ps1 new file mode 100644 index 0000000..720ce08 --- /dev/null +++ b/Installer/Configure-GSP-WindowsAgent.ps1 @@ -0,0 +1,250 @@ +# GSP Windows Agent Configuration Script +# This script processes Cfg/*.default files and creates proper Perl configs + +param( + [string]$AgentPath = "C:\GSP\Agent", + [string]$InstallPath = "C:\GSP" +) + +$Script:CfgDir = Join-Path $AgentPath "OGP64\OGP\Cfg" +$Script:LogsDir = Join-Path $InstallPath "logs" +$Script:ConfigLogFile = Join-Path $Script:LogsDir "config.log" + +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:ConfigLogFile -Value $logLine -Force +} + +function Read-ConfigDefaults { + param([string]$FilePath) + + if (-not (Test-Path $FilePath)) { + return @{} + } + + $content = Get-Content $FilePath -Raw + $config = @{} + + # Parse Perl hash syntax: key => 'value', + if ($content -match '%Cfg::\w+\s*=\s*\((.*)\);') { + $hashContent = $matches[1] + + # Match key => 'value' or key => value patterns + $regex = "(\w+)\s*=>\s*['\"]?([^'\"]*)['\"]?(?:,|$)" + [regex]::Matches($hashContent, $regex) | ForEach-Object { + $key = $_.Groups[1].Value + $value = $_.Groups[2].Value.Trim() + $config[$key] = $value + } + } + + return $config +} + +function Ask-ConfigValue { + param( + [string]$Key, + [string]$DefaultValue, + [string]$Description = "" + ) + + $prompt = $Key + if ($DefaultValue) { + $prompt += " [default: $DefaultValue]" + } + $prompt += ": " + + if ($Description) { + Write-Host $Description -ForegroundColor Cyan + } + + $value = Read-Host $prompt + + if ([string]::IsNullOrEmpty($value)) { + return $DefaultValue + } + + return $value +} + +function Configure-ConfigPm { + Log-Message "Configuring Config.pm..." + + $defaultFile = Join-Path $Script:CfgDir "Config.pm.default" + $configFile = Join-Path $Script:CfgDir "Config.pm" + + if (-not (Test-Path $defaultFile)) { + Log-Message "ERROR: Config.pm.default not found" -IsError $true + return $false + } + + # If config already exists, preserve it + if (Test-Path $configFile) { + Log-Message "Config.pm already exists, preserving it" + return $true + } + + Write-Host "" + Write-Host "=== Configuring Config.pm ===" -ForegroundColor Green + Write-Host "This file contains critical settings for agent communication" + Write-Host "" + + # Read defaults + $defaults = Read-ConfigDefaults $defaultFile + + # Ask for key values + $configs = @{} + $configs["listen_ip"] = Ask-ConfigValue "listen_ip" ($defaults["listen_ip"] ?? "0.0.0.0") "Agent listen IP address (0.0.0.0 = all interfaces)" + $configs["listen_port"] = Ask-ConfigValue "listen_port" ($defaults["listen_port"] ?? "12679") "Agent listen port" + $configs["key"] = Ask-ConfigValue "key" ($defaults["key"] ?? "CHANGE_ME_PANEL_AGENT_KEY") "Encryption key (must match Panel configuration)" + + # Optional settings + $configs["logfile"] = "/OGP/ogp_agent.log" + $configs["version"] = $defaults["version"] ?? "v1.4" + $configs["steam_license"] = $defaults["steam_license"] ?? "Accept" + $configs["sudo_password"] = "" + $configs["web_admin_api_key"] = "" + $configs["web_api_url"] = "" + $configs["steam_dl_limit"] = $defaults["steam_dl_limit"] ?? "0" + + # Write config file + try { + $content = "`%Cfg::Config = (`n" + foreach ($key in $configs.Keys | Sort-Object) { + $value = $configs[$key] + if ($key -eq "key") { + # Key is especially important - always show with quotes + $content += "`t$key => '$value',`n" + } else { + $content += "`t$key => '$value',`n" + } + } + $content += ");`n`n1;`n" + + Set-Content -Path $configFile -Value $content -Encoding UTF8 + Log-Message "Config.pm created successfully" + return $true + } catch { + Log-Message "ERROR creating Config.pm: $_" -IsError $true + return $false + } +} + +function Configure-PreferencesPm { + Log-Message "Configuring Preferences.pm..." + + $defaultFile = Join-Path $Script:CfgDir "Preferences.pm.default" + $configFile = Join-Path $Script:CfgDir "Preferences.pm" + + if (-not (Test-Path $defaultFile)) { + Log-Message "WARNING: Preferences.pm.default not found" + return $false + } + + # If preferences already exist, preserve them + if (Test-Path $configFile) { + Log-Message "Preferences.pm already exists, preserving it" + return $true + } + + Write-Host "" + Write-Host "=== Configuring Preferences.pm ===" -ForegroundColor Green + Write-Host "This file contains optional agent preferences" + Write-Host "" + + try { + Copy-Item -Path $defaultFile -Destination $configFile -Force + Log-Message "Preferences.pm created from defaults" + return $true + } catch { + Log-Message "ERROR creating Preferences.pm: $_" -IsError $true + return $false + } +} + +function Configure-BashPrefs { + Log-Message "Configuring bash_prefs.cfg..." + + $defaultFile = Join-Path $Script:CfgDir "bash_prefs.cfg.default" + $configFile = Join-Path $Script:CfgDir "bash_prefs.cfg" + + if (-not (Test-Path $defaultFile)) { + Log-Message "WARNING: bash_prefs.cfg.default not found" + return $false + } + + # If bash prefs already exist, preserve them + if (Test-Path $configFile) { + Log-Message "bash_prefs.cfg already exists, preserving it" + return $true + } + + try { + Copy-Item -Path $defaultFile -Destination $configFile -Force + Log-Message "bash_prefs.cfg created from defaults" + return $true + } catch { + Log-Message "ERROR creating bash_prefs.cfg: $_" -IsError $true + return $false + } +} + +function Main { + Log-Message "=========================================" + Log-Message "GSP Windows Agent Configuration" + Log-Message "=========================================" + Log-Message "Agent Path: $AgentPath" + Log-Message "Config Directory: $Script:CfgDir" + Log-Message "" + + if (-not (Test-Path $Script:CfgDir)) { + Log-Message "ERROR: Configuration directory not found: $Script:CfgDir" -IsError $true + exit 1 + } + + # Check for necessary permissions + try { + $testFile = Join-Path $Script:CfgDir ".write_test" + "test" | Out-File $testFile + Remove-Item $testFile -Force + } catch { + Log-Message "ERROR: No write permission to config directory" -IsError $true + exit 1 + } + + # Configure each file + $configResults = @{ + "Config.pm" = Configure-ConfigPm + "Preferences.pm" = Configure-PreferencesPm + "bash_prefs.cfg" = Configure-BashPrefs + } + + # Summary + Log-Message "" + Log-Message "=========================================" + Log-Message "Configuration Complete" + Log-Message "=========================================" + + $successCount = ($configResults.Values | Where-Object { $_ -eq $true }).Count + Write-Host "Successfully configured: $successCount/$($configResults.Count) files" + + foreach ($file in $configResults.Keys) { + $status = $configResults[$file] ? "✓" : "✗" + Write-Host " $status $file" + } + + Log-Message "" + Log-Message "Configuration log: $Script:ConfigLogFile" +} + +Main diff --git a/Installer/Install-GSP-WindowsAgent.ps1 b/Installer/Install-GSP-WindowsAgent.ps1 new file mode 100644 index 0000000..70ef385 --- /dev/null +++ b/Installer/Install-GSP-WindowsAgent.ps1 @@ -0,0 +1,466 @@ +# 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 diff --git a/Installer/PortableGit-2.54.0-64-bit.7z.exe b/Installer/PortableGit-2.54.0-64-bit.7z.exe new file mode 100644 index 0000000..2d4448d Binary files /dev/null and b/Installer/PortableGit-2.54.0-64-bit.7z.exe differ diff --git a/Installer/README-FIRST.txt b/Installer/README-FIRST.txt new file mode 100644 index 0000000..836eb5f --- /dev/null +++ b/Installer/README-FIRST.txt @@ -0,0 +1,334 @@ +================================================================================ +GSP WINDOWS AGENT INSTALLER +================================================================================ + +This installer package contains everything needed to deploy the GSP Windows +Agent on a Windows server. The agent runs game server processes and provides +RPC communication with the OGP Panel. + +================================================================================ +SYSTEM REQUIREMENTS +================================================================================ + +- Windows Server 2016 or later (or Windows 10/11 for testing) +- Administrator privileges (required to create C:\GSP and Task Scheduler entry) +- Network connectivity to Panel server +- Sufficient disk space for game server installations + +================================================================================ +BEFORE YOU START +================================================================================ + +1. Unzip this installer package to any folder +2. You may need to edit the installation path (default: C:\GSP) +3. You MUST have the Panel's encryption key (shared key for agent communication) + +================================================================================ +INSTALLATION STEPS +================================================================================ + +1. Right-click on install.bat +2. Select "Run as Administrator" +3. Click "Yes" when prompted for Administrator permission +4. Answer the configuration questions: + - Installation path (default: C:\GSP) + - Git repository URL (can use default) + - Git branch (default: master) + - Agent listen IP (default: 0.0.0.0) + - Agent listen port (default: 12679) + - Encryption key (MUST match Panel configuration) + +The installer will: +- Create C:\GSP directory structure +- Extract Portable Git (included in this package) +- Clone the Windows Agent repository +- Create configuration files from templates +- Set up a Windows Task Scheduler entry +- Configure the agent to auto-start at system boot + +================================================================================ +INSTALLATION DETAILS +================================================================================ + +Install Layout +-------------- +After installation, you'll have: + +C:\GSP\ + Agent\ - Git clone of Windows Agent repository + .git\ - Git metadata (allows updates) + OGP64\ - Bundled Cygwin runtime + Cfg\ - Configuration files + docs\ - Documentation + home\ - Game server files directory (never deleted by installer) + tools\ + PortableGit\ - Bundled Git (for updates/rollback) + backups\ + agent\ - Automatic backups from updates + logs\ - Installation, update, and rollback logs + +First Run +--------- +The agent will: +1. Start automatically with Windows system startup +2. Run under SYSTEM account (highest privileges) +3. Execute startup scripts with Cygwin environment +4. Listen on the configured IP and port +5. Write logs to: C:\GSP\Agent\OGP64\OGP\ogp_agent.log + +Manual Start +------------ +If you need to start the agent manually: + 1. Open Command Prompt as Administrator + 2. Run: C:\GSP\Agent\OGP64\agent_start.bat + +Manual Stop +----------- +To stop the agent: + 1. Open Command Prompt as Administrator + 2. Run: C:\GSP\Agent\OGP64\agent_stop.bat + +================================================================================ +AFTER INSTALLATION +================================================================================ + +Configure Panel +--------------- +1. Log in to the Panel as admin +2. Add the Windows Agent: + - IP/Hostname: (the Windows server's IP) + - Port: 12679 (or your configured port) + - Encryption Key: (use the same key you entered during install) + - Shared Key: (same as encryption key) + +Verify Agent Connection +----------------------- +1. From the Panel, check the agent's status +2. Run a test command from the Panel +3. Check C:\GSP\Agent\OGP64\OGP\ogp_agent.log for debug output + +================================================================================ +UPDATING THE AGENT +================================================================================ + +The bundled Portable Git allows easy updates without requiring system Git. + +To Update +--------- +1. Right-click update.bat (in the original Installer folder) +2. Select "Run as Administrator" +3. The updater will: + - Stop the running agent + - Back up the current version + - Fetch updates from Git + - Restore your configuration and game files + - Restart the agent + +Updates preserve: + - C:\GSP\home (all game server files) + - C:\GSP\Agent\Cfg (agent configuration) + - C:\GSP\Agent\screenlogs (server logs) + - C:\GSP\Agent\startups (startup flags) + - C:\GSP\Agent\OGP64\steamcmd (SteamCMD files) + +================================================================================ +ROLLING BACK +================================================================================ + +If an update causes problems, you can restore a previous version. + +To Rollback +----------- +1. Right-click rollback.bat (in the original Installer folder) +2. Select "Run as Administrator" +3. The rollback script will: + - List available backups (with timestamps) + - Let you select which version to restore + - Optionally preserve your current configuration + - Restore the previous version + - Restart the agent + +Available Backups +----------------- +Backups are stored in: C:\GSP\backups\agent\ + +Each backup includes: + - Agent binaries and scripts + - Perl modules + - Cygwin runtime + - But NOT game server files or logs + +================================================================================ +PERMANENT REMOVAL +================================================================================ + +To Remove Only the Scheduled Task +----------------------------------- +1. Right-click uninstall-task.bat (in the original Installer folder) +2. Select "Run as Administrator" +3. This removes only the Task Scheduler entry +4. The agent directory and game servers remain intact +5. You can manually start the agent later with agent_start.bat + +Full Removal (if needed) +------------------------ +To completely remove the agent: +1. Run uninstall-task.bat first +2. Manually delete C:\GSP\Agent +3. Leave C:\GSP\home (contains your game servers) +4. Leave C:\GSP\backups (for recovery) + +WARNING: Do NOT delete C:\GSP\home - it contains your game server installations! + +================================================================================ +TROUBLESHOOTING +================================================================================ + +Agent Won't Start +----------------- +1. Check the log file: + C:\GSP\Agent\OGP64\OGP\ogp_agent.log + +2. Try manual start: + Right-click C:\GSP\Agent\OGP64\agent_start.bat + Select "Run as Administrator" + +3. Verify configuration: + Check C:\GSP\Agent\OGP64\OGP\Cfg\Config.pm for encryption key + +4. Check Windows Event Viewer: + Windows Logs > System (for Task Scheduler errors) + +Panel Can't Connect +------------------- +1. Verify the agent is running: + tasklist | findstr /i bash + +2. Check Windows Firewall: + Allow C:\GSP\Agent\OGP64\bin\bash.exe through firewall + Allow TCP port 12679 (or configured port) inbound + +3. Verify Panel encryption key matches: + Must match exactly in both Panel and C:\GSP\Agent\OGP64\OGP\Cfg\Config.pm + +4. Check agent log: + C:\GSP\Agent\OGP64\OGP\ogp_agent.log + +Installation Failed +------------------- +1. Check the installation log: + C:\GSP\logs\install.log + +2. Verify permissions: + Run as Administrator (right-click, "Run as Administrator") + +3. Check free disk space: + At least 500MB required for Portable Git and agent + +4. Check Portable Git extraction: + Should be at: C:\GSP\tools\PortableGit\cmd\git.exe + +================================================================================ +ADVANCED TOPICS +================================================================================ + +Configuration Files (reference only) +------------------------------------ +Important Agent configuration files are in: + C:\GSP\Agent\OGP64\OGP\Cfg\ + +Main files: + Config.pm - Encryption key, listen port/IP + Preferences.pm - Optional preferences + bash_prefs.cfg - Bash shell preferences + +Do NOT edit these manually unless you understand Perl syntax! + +Manual Git Operations +--------------------- +You can use Portable Git directly: + C:\GSP\tools\PortableGit\cmd\git.exe [command] + +Example - Check current branch: + cd C:\GSP\Agent + C:\GSP\tools\PortableGit\cmd\git.exe status + +Environmental Variables +------------------------ +The following directories are set by the installer: + +C:\GSP\ + Agent\ - Windows Agent Git clone + home\ - Game servers + tools\ - Portable Git + backups\ - Automatic backups + logs\ - Installation logs + +These locations can be changed by editing install.bat parameters. + +================================================================================ +SUPPORT & DOCUMENTATION +================================================================================ + +More Information +---------------- +- Main documentation: C:\GSP\Agent\README.md +- Agent guide: C:\GSP\Agent\docs\AGENT_ARCHITECTURE.md +- Panel documentation: Check your Panel installation + +Getting Help +------------ +1. Check the relevant log file +2. Review documentation in C:\GSP\Agent\docs\ +3. Check Panel logs on the Panel server +4. Verify network connectivity between servers + +================================================================================ +SECURITY NOTES +================================================================================ + +1. Encryption Key + - The encryption key in Config.pm is critical + - Must match exactly on Panel and Agent + - Treat this key as a production secret + - Do NOT commit Config.pm to public Git repositories + +2. Network Security + - Agent runs as SYSTEM (highest Windows privileges) + - Restrict network access to Panel server IP only + - Use Windows Firewall to limit inbound connections + - Consider VPN or internal network isolation + +3. File Permissions + - C:\GSP\home must be accessible to all game processes + - C:\GSP\Agent must be readable by SYSTEM account + - Configure Windows ACLs as needed for your environment + +4. Logging + - Agent logs contain diagnostic information + - Logs may include command parameters + - Rotate logs to manage disk space + - Archive logs for audit trails if needed + +================================================================================ +VERSION INFORMATION +================================================================================ + +Installer Version: GSP Windows Agent 1.0 +Release Date: June 2026 +Portable Git Version: 2.54.0 (64-bit) + +Compatible With: + - Windows Server 2016+ + - Windows 10/11 (for testing) + - OGP Panel v1.4+ + - Linux Agent v1.4+ (for comparison) + +================================================================================ + +For more information, see: + - Installer folder: README.txt (this file) + - Agent repository: C:\GSP\Agent\README.md + - Documentation folder: C:\GSP\Agent\docs\ + +================================================================================ diff --git a/Installer/Rollback-GSP-WindowsAgent.ps1 b/Installer/Rollback-GSP-WindowsAgent.ps1 new file mode 100644 index 0000000..58bf9a4 --- /dev/null +++ b/Installer/Rollback-GSP-WindowsAgent.ps1 @@ -0,0 +1,260 @@ +# GSP Windows Agent Rollback - PowerShell Script +# This script lets an admin restore a previous agent version from backup + +#Requires -RunAsAdministrator + +param( + [string]$InstallPath = "C:\GSP" +) + +$Script:AgentDir = Join-Path $InstallPath "Agent" +$Script:BackupDir = Join-Path $InstallPath "backups\agent" +$Script:LogsDir = Join-Path $InstallPath "logs" +$Script:RollbackLogFile = Join-Path $Script:LogsDir "rollback.log" + +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:RollbackLogFile -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 List-Backups { + Log-Message "Listing available backups..." + + if (-not (Test-Path $Script:BackupDir)) { + Write-Host "No backups directory found" + return @() + } + + $backups = Get-ChildItem -Path $Script:BackupDir -Directory | Sort-Object Name -Descending + + if ($backups.Count -eq 0) { + Write-Host "No backups available" + return @() + } + + Write-Host "" + Write-Host "Available backups:" -ForegroundColor Green + + $backupList = @() + $index = 1 + foreach ($backup in $backups) { + $path = $backup.FullName + $created = $backup.CreationTime + $size = (Get-ChildItem -Path $path -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB + + Write-Host " [$index] $($backup.Name) (created: $created, size: $([math]::Round($size, 2)) MB)" + $backupList += $backup.FullName + $index++ + } + + return $backupList +} + +function Select-Backup { + param([array]$BackupList) + + if ($BackupList.Count -eq 0) { + Write-Host "ERROR: No backups available" + return $null + } + + if ($BackupList.Count -eq 1) { + Write-Host "" + $response = Read-Host "Only one backup available. Restore it? (Y/N)" + if ($response -eq "Y" -or $response -eq "y") { + return $BackupList[0] + } else { + return $null + } + } + + Write-Host "" + $choice = Read-Host "Select backup number (1-$($BackupList.Count)) or 'Q' to quit" + + if ($choice -eq "Q" -or $choice -eq "q") { + return $null + } + + if ($choice -match '^\d+$' -and [int]$choice -ge 1 -and [int]$choice -le $BackupList.Count) { + return $BackupList[[int]$choice - 1] + } else { + Write-Host "Invalid selection" + return $null + } +} + +function Stop-Agent { + Log-Message "Stopping agent..." + + try { + Stop-ScheduledTask -TaskName "GSP Windows Agent" -ErrorAction SilentlyContinue + Log-Message "Scheduled task stopped" + + Start-Sleep -Seconds 2 + + Get-Process bash,perl -ErrorAction SilentlyContinue | ForEach-Object { + try { + Stop-Process -Id $_.Id -Force + Log-Message "Killed: $($_.Name) (PID: $($_.Id))" + } catch { + # Already gone + } + } + + return $true + } catch { + Log-Message "WARNING stopping agent: $_" -IsError $true + return $false + } +} + +function Restore-Backup { + param([string]$BackupPath) + + Log-Message "Restoring from backup: $BackupPath" + + try { + # Stop agent + Stop-Agent + + # Preserve current Cfg if user wants + $preserveCfg = $false + Write-Host "" + $response = Read-Host "Preserve current Cfg folder? (Y/N, default: Y)" + if ($response -ne "N" -and $response -ne "n") { + $preserveCfg = $true + } + + $currentCfg = Join-Path $Script:AgentDir "OGP64\OGP\Cfg" + $tempCfg = Join-Path $env:TEMP "gsp_preserve_cfg" + + if ($preserveCfg -and (Test-Path $currentCfg)) { + Log-Message "Preserving current Cfg folder" + if (Test-Path $tempCfg) { + Remove-Item $tempCfg -Recurse -Force + } + Copy-Item -Path $currentCfg -Destination $tempCfg -Recurse -Force + } + + # Remove current installation + Log-Message "Removing current agent directory" + if (Test-Path $Script:AgentDir) { + Remove-Item -Path $Script:AgentDir -Recurse -Force + } + + # Restore backup + Log-Message "Copying backup to $Script:AgentDir" + Copy-Item -Path $BackupPath -Destination $Script:AgentDir -Recurse -Force + + # Restore preserved Cfg + if ($preserveCfg -and (Test-Path $tempCfg)) { + Log-Message "Restoring preserved Cfg folder" + $targetCfg = Join-Path $Script:AgentDir "OGP64\OGP\Cfg" + if (Test-Path $targetCfg) { + Remove-Item -Path $targetCfg -Recurse -Force + } + Copy-Item -Path $tempCfg -Destination $targetCfg -Recurse -Force + Remove-Item -Path $tempCfg -Recurse -Force + } + + # Start the task + Log-Message "Starting agent" + try { + Start-ScheduledTask -TaskName "GSP Windows Agent" -ErrorAction SilentlyContinue + } catch { + Log-Message "WARNING: Could not start scheduled task" + } + + Log-Message "Rollback completed successfully" + return $true + } catch { + Log-Message "ERROR during rollback: $_" -IsError $true + return $false + } +} + +function Main { + Log-Message "=========================================" + Log-Message "GSP Windows Agent Rollback" + Log-Message "=========================================" + Log-Message "Install Path: $InstallPath" + Log-Message "Backup Directory: $Script:BackupDir" + Log-Message "" + + if (-not (Test-Administrator)) { + Log-Message "ERROR: This script must be run as Administrator" -IsError $true + exit 1 + } + + if (-not (Test-Path $Script:LogsDir)) { + New-Item -ItemType Directory -Path $Script:LogsDir -Force | Out-Null + } + + # List available backups + $backupList = List-Backups + + if ($backupList.Count -eq 0) { + Write-Host "" + Write-Host "No backups available for rollback" -ForegroundColor Yellow + Read-Host "Press Enter to close this window" + exit 0 + } + + # Select a backup + $selectedBackup = Select-Backup $backupList + + if (-not $selectedBackup) { + Write-Host "Rollback cancelled" + exit 0 + } + + # Confirm restore + Write-Host "" + Write-Host "Selected: $selectedBackup" -ForegroundColor Yellow + $confirm = Read-Host "Restore this backup? (Y/N)" + + if ($confirm -ne "Y" -and $confirm -ne "y") { + Write-Host "Rollback cancelled" + exit 0 + } + + # Perform rollback + if (Restore-Backup $selectedBackup) { + Log-Message "" + Log-Message "=========================================" + Log-Message "Rollback Complete!" + Log-Message "=========================================" + Write-Host "" + Write-Host "Rollback successful. Agent has been restored." -ForegroundColor Green + Write-Host "Log file: $Script:RollbackLogFile" + } else { + Log-Message "" + Log-Message "=========================================" + Log-Message "Rollback Failed!" + Log-Message "=========================================" + Write-Host "" + Write-Host "Rollback failed. Check the log for details." -ForegroundColor Red + Write-Host "Log file: $Script:RollbackLogFile" + } + + Write-Host "" + Read-Host "Press Enter to close this window" +} + +Main diff --git a/Installer/Update-GSP-WindowsAgent.ps1 b/Installer/Update-GSP-WindowsAgent.ps1 new file mode 100644 index 0000000..806a7b6 --- /dev/null +++ b/Installer/Update-GSP-WindowsAgent.ps1 @@ -0,0 +1,341 @@ +# GSP Windows Agent Updater - PowerShell Script +# This script updates an existing Git-managed agent installation + +#Requires -RunAsAdministrator + +param( + [string]$InstallPath = "C:\GSP", + [string]$GitBranch = "master" +) + +$Script:AgentDir = Join-Path $InstallPath "Agent" +$Script:GitDir = Join-Path $InstallPath "tools\PortableGit" +$Script:BackupDir = Join-Path $InstallPath "backups\agent" +$Script:LogsDir = Join-Path $InstallPath "logs" +$Script:UpdateLogFile = Join-Path $Script:LogsDir "update.log" + +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:UpdateLogFile -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 Stop-Agent { + Log-Message "Stopping agent..." + + try { + # Try to stop the scheduled task + Stop-ScheduledTask -TaskName "GSP Windows Agent" -ErrorAction SilentlyContinue + Log-Message "Scheduled task stopped" + + # Give it a moment to shut down gracefully + Start-Sleep -Seconds 2 + + # Kill any remaining bash/perl processes from the agent + Get-Process bash,perl -ErrorAction SilentlyContinue | ForEach-Object { + try { + Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue + Log-Message "Killed remaining process: $($_.Name) (PID: $($_.Id))" + } catch { + # Process may already be gone + } + } + + return $true + } catch { + Log-Message "WARNING stopping agent: $_" -IsError $true + return $false + } +} + +function Backup-Agent { + Log-Message "Backing up current agent..." + + try { + $backupName = "agent-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')" + $backupPath = Join-Path $Script:BackupDir $backupName + + Log-Message "Backup destination: $backupPath" + + Copy-Item -Path $Script:AgentDir -Destination $backupPath -Recurse -Force + + Log-Message "Backup completed successfully" + return $backupPath + } catch { + Log-Message "ERROR creating backup: $_" -IsError $true + return $null + } +} + +function Preserve-LocalFolders { + Log-Message "Preserving local configuration and runtime folders..." + + $preserved = @{} + $preserveFolders = @("Cfg", "steamcmd", "screenlogs", "startups", "tmp", "shared") + + try { + foreach ($folder in $preserveFolders) { + $folderPath = Join-Path $Script:AgentDir $folder + if (Test-Path $folderPath) { + $tempPath = Join-Path $env:TEMP "gsp_preserve_$folder" + if (Test-Path $tempPath) { + Remove-Item $tempPath -Recurse -Force + } + Copy-Item -Path $folderPath -Destination $tempPath -Recurse -Force + $preserved[$folder] = $tempPath + Log-Message " Preserved: $folder" + } + } + + return $preserved + } catch { + Log-Message "ERROR preserving folders: $_" -IsError $true + return @{} + } +} + +function Update-Repository { + Log-Message "Updating repository from Git..." + + try { + $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 + } + + if (-not (Test-Path (Join-Path $Script:AgentDir ".git"))) { + Log-Message "ERROR: Agent directory is not a Git repository (.git folder not found)" -IsError $true + Log-Message "The agent must be installed with the Git-based installer to use this update script" + return $false + } + + Log-Message "Running git fetch..." + $fetchProcess = Start-Process -FilePath $gitExe ` + -ArgumentList "fetch", "origin" ` + -Wait -PassThru -NoNewWindow -WorkingDirectory $Script:AgentDir + + if ($fetchProcess.ExitCode -ne 0) { + Log-Message "ERROR: git fetch failed with code: $($fetchProcess.ExitCode)" -IsError $true + return $false + } + + Log-Message "Running git reset to $GitBranch..." + $resetProcess = Start-Process -FilePath $gitExe ` + -ArgumentList "reset", "--hard", "origin/$GitBranch" ` + -Wait -PassThru -NoNewWindow -WorkingDirectory $Script:AgentDir + + if ($resetProcess.ExitCode -ne 0) { + Log-Message "ERROR: git reset failed with code: $($resetProcess.ExitCode)" -IsError $true + return $false + } + + Log-Message "Repository updated successfully" + return $true + } catch { + Log-Message "ERROR updating repository: $_" -IsError $true + return $false + } +} + +function Restore-LocalFolders { + param([hashtable]$PreservedFolders) + + Log-Message "Restoring preserved local folders..." + + try { + foreach ($folder in $PreservedFolders.Keys) { + $tempPath = $PreservedFolders[$folder] + $targetPath = Join-Path $Script:AgentDir $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 + + Log-Message " Restored: $folder" + } + + return $true + } catch { + Log-Message "ERROR restoring folders: $_" -IsError $true + return $false + } +} + +function Validate-Agent { + Log-Message "Validating agent syntax..." + + try { + $agentScript = Join-Path $Script:AgentDir "OGP64\OGP\ogp_agent.pl" + + if (-not (Test-Path $agentScript)) { + Log-Message "WARNING: ogp_agent.pl not found, skipping syntax check" + return $true + } + + # Try to run perl syntax check + $perlCheck = Start-Process -FilePath "perl.exe" ` + -ArgumentList "-c", $agentScript ` + -Wait -PassThru -NoNewWindow -ErrorAction SilentlyContinue + + if ($perlCheck.ExitCode -ne 0) { + Log-Message "WARNING: Perl syntax check failed, but update may still be valid" -IsError $true + } else { + Log-Message "Perl syntax validation passed" + } + + return $true + } catch { + Log-Message "WARNING: Could not validate agent syntax: $_" -IsError $true + return $true + } +} + +function Start-Agent { + Log-Message "Starting agent..." + + try { + Start-ScheduledTask -TaskName "GSP Windows Agent" -ErrorAction SilentlyContinue + Log-Message "Scheduled task started" + return $true + } catch { + Log-Message "WARNING: Could not start scheduled task immediately" -IsError $true + Log-Message "Agent will start at next system boot" + return $true + } +} + +function Rollback-Update { + param([string]$BackupPath) + + Log-Message "ROLLING BACK UPDATE..." + + if (-not (Test-Path $BackupPath)) { + Log-Message "ERROR: Backup not found, cannot rollback" -IsError $true + return $false + } + + try { + Log-Message "Restoring from backup: $BackupPath" + + # Stop agent first + Stop-Agent | Out-Null + + # Remove updated directory + if (Test-Path $Script:AgentDir) { + Remove-Item -Path $Script:AgentDir -Recurse -Force + } + + # Restore backup + Copy-Item -Path $BackupPath -Destination $Script:AgentDir -Recurse -Force + + # Restart agent + Start-Agent | Out-Null + + Log-Message "Rollback completed successfully" + return $true + } catch { + Log-Message "ERROR during rollback: $_" -IsError $true + return $false + } +} + +function Main { + Log-Message "=========================================" + Log-Message "GSP Windows Agent Update" + Log-Message "=========================================" + Log-Message "Install Path: $InstallPath" + Log-Message "Git Branch: $GitBranch" + Log-Message "" + + # Check admin + if (-not (Test-Administrator)) { + Log-Message "ERROR: This script must be run as Administrator" -IsError $true + exit 1 + } + + # Verify directories exist + if (-not (Test-Path $Script:AgentDir)) { + Log-Message "ERROR: Agent not found at: $Script:AgentDir" -IsError $true + exit 1 + } + + if (-not (Test-Path $Script:GitDir)) { + Log-Message "ERROR: Portable Git not found at: $Script:GitDir" -IsError $true + exit 1 + } + + # Create logs directory if needed + if (-not (Test-Path $Script:LogsDir)) { + New-Item -ItemType Directory -Path $Script:LogsDir -Force | Out-Null + } + + # Stop agent + Stop-Agent + + # Create backup + $backupPath = Backup-Agent + if (-not $backupPath) { + Log-Message "FATAL: Could not create backup" -IsError $true + exit 1 + } + + # Preserve local folders + $preserved = Preserve-LocalFolders + + # Update from Git + if (-not (Update-Repository)) { + Log-Message "FATAL: Update failed, rolling back..." -IsError $true + Rollback-Update $backupPath | Out-Null + exit 1 + } + + # Restore preserved folders + if ($preserved.Count -gt 0) { + if (-not (Restore-LocalFolders $preserved)) { + Log-Message "WARNING: Failed to restore all folders" -IsError $true + Rollback-Update $backupPath | Out-Null + exit 1 + } + } + + # Validate agent + Validate-Agent + + # Start agent + Start-Agent + + Log-Message "" + Log-Message "=========================================" + Log-Message "Update Complete!" + Log-Message "=========================================" + Log-Message "Agent has been updated successfully" + Log-Message "Backup location: $backupPath" + Log-Message "Log file: $Script:UpdateLogFile" + Log-Message "" + Log-Message "The agent will restart at the next opportunity" + Log-Message "" + + Read-Host "Press Enter to close this window" +} + +Main diff --git a/Installer/install.bat b/Installer/install.bat new file mode 100644 index 0000000..7dadf42 --- /dev/null +++ b/Installer/install.bat @@ -0,0 +1,37 @@ +@echo off +REM GSP Windows Agent Installer +REM This script checks for Administrator privileges and launches the PowerShell installer + +setlocal enabledelayedexpansion + +REM Check for Administrator privileges +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo. + echo ERROR: This installer must be run as Administrator. + echo. + echo Please: + echo 1. Right-click this batch file + echo 2. Select "Run as Administrator" + echo 3. Click "Yes" when prompted + echo. + pause + exit /b 1 +) + +REM Get the directory where this batch file is located +set "SCRIPT_DIR=%~dp0" + +REM Verify the PowerShell script exists +if not exist "%SCRIPT_DIR%Install-GSP-WindowsAgent.ps1" ( + echo ERROR: Install-GSP-WindowsAgent.ps1 not found in %SCRIPT_DIR% + pause + exit /b 1 +) + +REM Launch the PowerShell installer +REM Use -NoProfile to skip profile scripts, -ExecutionPolicy Bypass to allow script execution +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%SCRIPT_DIR%Install-GSP-WindowsAgent.ps1'" %* + +REM Exit with PowerShell's exit code +exit /b %ERRORLEVEL% diff --git a/Installer/rollback.bat b/Installer/rollback.bat new file mode 100644 index 0000000..464955b --- /dev/null +++ b/Installer/rollback.bat @@ -0,0 +1,36 @@ +@echo off +REM GSP Windows Agent Rollback Script +REM This script restores a previous agent version from backup + +setlocal enabledelayedexpansion + +REM Check for Administrator privileges +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo. + echo ERROR: This rollback script must be run as Administrator. + echo. + echo Please: + echo 1. Right-click this batch file + echo 2. Select "Run as Administrator" + echo 3. Click "Yes" when prompted + echo. + pause + exit /b 1 +) + +REM Get the directory where this batch file is located +set "SCRIPT_DIR=%~dp0" + +REM Verify the PowerShell script exists +if not exist "%SCRIPT_DIR%Rollback-GSP-WindowsAgent.ps1" ( + echo ERROR: Rollback-GSP-WindowsAgent.ps1 not found in %SCRIPT_DIR% + pause + exit /b 1 +) + +REM Launch the PowerShell rollback script +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%SCRIPT_DIR%Rollback-GSP-WindowsAgent.ps1'" %* + +REM Exit with PowerShell's exit code +exit /b %ERRORLEVEL% diff --git a/Installer/uninstall-task.bat b/Installer/uninstall-task.bat new file mode 100644 index 0000000..3777293 --- /dev/null +++ b/Installer/uninstall-task.bat @@ -0,0 +1,61 @@ +@echo off +REM GSP Windows Agent - Uninstall Task Scheduler Entry +REM This script removes the scheduled task only (does NOT delete C:\GSP\home or any game servers) + +setlocal enabledelayedexpansion + +REM Check for Administrator privileges +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo. + echo ERROR: This uninstaller must be run as Administrator. + echo. + echo Please: + echo 1. Right-click this batch file + echo 2. Select "Run as Administrator" + echo 3. Click "Yes" when prompted + echo. + pause + exit /b 1 +) + +echo. +echo GSP Windows Agent - Remove Scheduled Task +echo. +echo WARNING: This will only remove the Task Scheduler entry. +echo It will NOT delete C:\GSP\home or any game servers. +echo It will NOT delete the agent installation. +echo. + +set /p CONFIRM="Are you sure? (Y/N): " + +if /i "%CONFIRM%"=="Y" ( + echo. + echo Stopping scheduled task... + taskkill /FI "TASKNAME eq GSP Windows Agent" /F 2>nul + + echo Removing scheduled task... + schtasks /delete /tn "GSP Windows Agent" /f + + if %errorlevel% equ 0 ( + echo. + echo SUCCESS: Scheduled task removed. + echo. + echo Agent installation remains at C:\GSP + echo Game servers remain at C:\GSP\home + echo Backups remain at C:\GSP\backups + echo. + echo To manually start the agent, run: + echo C:\GSP\Agent\OGP64\agent_start.bat + echo. + ) else ( + echo. + echo ERROR: Failed to remove scheduled task. + echo. + ) +) else ( + echo Uninstall cancelled. +) + +echo. +pause diff --git a/Installer/update.bat b/Installer/update.bat new file mode 100644 index 0000000..2cd32e4 --- /dev/null +++ b/Installer/update.bat @@ -0,0 +1,36 @@ +@echo off +REM GSP Windows Agent Update Script +REM This script updates an existing Git-managed agent installation + +setlocal enabledelayedexpansion + +REM Check for Administrator privileges +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo. + echo ERROR: This updater must be run as Administrator. + echo. + echo Please: + echo 1. Right-click this batch file + echo 2. Select "Run as Administrator" + echo 3. Click "Yes" when prompted + echo. + pause + exit /b 1 +) + +REM Get the directory where this batch file is located +set "SCRIPT_DIR=%~dp0" + +REM Verify the PowerShell script exists +if not exist "%SCRIPT_DIR%Update-GSP-WindowsAgent.ps1" ( + echo ERROR: Update-GSP-WindowsAgent.ps1 not found in %SCRIPT_DIR% + pause + exit /b 1 +) + +REM Launch the PowerShell updater +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%SCRIPT_DIR%Update-GSP-WindowsAgent.ps1'" %* + +REM Exit with PowerShell's exit code +exit /b %ERRORLEVEL% diff --git a/bin/ogp_agent b/bin/ogp_agent new file mode 100644 index 0000000..4781185 --- /dev/null +++ b/bin/ogp_agent @@ -0,0 +1,333 @@ +#!/bin/bash +# +# +# A wrapper script for the OGP agent perl script. +# Performs auto-restarting of the agent on crash. You can +# extend this to log crashes and more. +# +# The ogp_agent script should be at the top level of the agent tree +# Make sure we are in that directory since the script assumes this is the case + +##################### +# Important VARS # +##################### + +AGENTDIR="/OGP" +BASH_PREFS_CONF="$AGENTDIR/Cfg/bash_prefs.cfg" +REPONAME=OGP-Agent-Windows +GitHubUsername="OpenGamePanel" + +##################### +# FUNCTIONS # +##################### + +setupOGPAndReadBashPrefs(){ + + # Find CURL + if [ -z "$CURL" -o -z "$UNZIP" ]; then + checkDepends + fi + + chmod -Rf 770 $AGENTDIR + find $AGENTDIR -type f -print | xargs chmod ug=rw + if [ -d "$AGENTDIR/steamcmd" ]; then + find $AGENTDIR/steamcmd -iname \*.dll -exec chmod +x {} \; + find $AGENTDIR/steamcmd -iname \*.exe -exec chmod +x {} \; + fi + if [ -d "$AGENTDIR/screenlogs" ]; then + chmod -Rf ug=rwx $AGENTDIR/screenlogs + fi + chmod +x $AGENTDIR/ogp_agent.pl &> /dev/null + chmod +x $AGENTDIR/agent_conf.sh &> /dev/null + chmod +x /usr/bin/ogp_agent &> /dev/null + + # Should we perform an automatic update? + if [ -e $BASH_PREFS_CONF ] + then + source "$BASH_PREFS_CONF" + if [ "X$agent_auto_update" == "X1" ] + then + AUTO_UPDATE="yes" + fi + + if [ "X$run_pureftpd" == "X0" ] + then + RUN_PUREFTPD="no" + else + RUN_PUREFTPD="yes" + fi + + if [ "X$ftp_ip" != "X" ] + then + FTP_IP="$ftp_ip" + else + FTP_IP="0.0.0.0" + fi + + if [ "X$ftp_port" != "X" ] + then + FTP_PORT="$ftp_port" + else + FTP_PORT="21" + fi + + if [ "X$ftp_pasv_range" != "X" ] + then + FTP_PASV_STRING="-p $ftp_pasv_range" + else + FTP_PASV_STRING="" + fi + + # Use custom github update address + if [ ! -z "$github_update_username" ]; then + if [ -f "$CURL" -a -x "$CURL" ] && [ -f "$UNZIP" -a -x "$UNZIP" ]; then + REVISIONTest=`curl -Lks https://github.com/${github_update_username}/${REPONAME}/commits/master.atom | grep -Eo "([a-f0-9]{40})" | head -n 1` + if [ ! -z "$REVISIONTest" ]; then + GitHubUsername=${github_update_username} + fi + fi + fi + + else + AUTO_UPDATE="yes" + RUN_PUREFTPD="yes" + FTP_IP="0.0.0.0" + FTP_PORT="21" + FTP_PASV_STING="" + fi + + if test `id -u` -eq 0; then + echo + echo + echo "************** WARNING ***************" + echo "Running the OGP agent as root " + echo "is highly discouraged. It is generally" + echo "unnecessary to use root privileges to " + echo "execute the agent. " + echo "**************************************" + echo + echo + timeout=10 + while test $timeout -gt 0; do + echo -n "The agent will continue to launch in $timeout seconds\r" + timeout=`expr $timeout - 1` + sleep 1 + done + fi +} + +ogpGitCleanup(){ + echo "Cleaning up..." + rm -Rf ${REPONAME}-* &> /dev/null + if [ -e "ogp_agent_latest.zip" ]; then + rm -f "ogp_agent_latest.zip" + fi +} + +init() { + RESTART="yes" + AGENT="$AGENTDIR/ogp_agent.pl" + TIMEOUT=10 # time to wait after a crash (in seconds) + PID_FILE="" + while test $# -gt 0; do + case "$1" in + "-pidfile") + PID_FILE="$2" + PID_FILE_SET=1 + echo $$ > $PID_FILE + shift ;; + esac + shift + done + + if test ! -f "$AGENT"; then + echo "ERROR: '$AGENT' not found, exiting" + quit 1 + elif test ! -x "$AGENT"; then + # Could try chmod but dont know what we will be + # chmoding so just fail. + echo "ERROR: '$AGENT' not executable, exiting" + quit 1 + fi +} + +syntax () { + # Prints script syntax + + echo "Syntax:" + echo "$0" +} + +checkDepends() { + CURL=`which curl 2>/dev/null` + if test "$?" -gt 0; then + echo "WARNING: Failed to locate curl binary." + else + echo "INFO: Located curl: $CURL" + fi + UNZIP=`which unzip 2>/dev/null` + if test "$?" -gt 0; then + echo "WARNING: Failed to locate unzip binary." + else + echo "INFO: Located unzip: $UNZIP" + fi +} + +update() { + # Run the git update + if test -n "$AUTO_UPDATE"; then + if [ -f "$CURL" -a -x "$CURL" ] && [ -f "$UNZIP" -a -x "$UNZIP" ]; then + cd $AGENTDIR + if [ ! -d tmp ]; then + mkdir tmp + fi + cd tmp + REVISION=`curl -Lks https://github.com/${GitHubUsername}/${REPONAME}/commits/master.atom | grep -Eo "([a-f0-9]{40})" | head -n 1` + curl -Os https://raw.githubusercontent.com/${GitHubUsername}/${REPONAME}/${REVISION}/bin/ogp_agent + currentOGPAgentRunContent=$(cat "./ogp_agent") + # Check to make sure ogp_agent downloaded successfully from GitHub before we attempt to replace it. + # This should fix random 404 people have been experiencing + if [ -s "./ogp_agent" ] && [ "$(echo "$currentOGPAgentRunContent" | head -n 1)" != "404: Not Found" ] && [ ! -z "$(echo "$currentOGPAgentRunContent" | grep "ogp_agent.pl")" ]; then + diff ./ogp_agent /bin/ogp_agent &>/dev/null + if test $? -ne 0; then + cp -f ./ogp_agent /bin/ogp_agent &> /dev/null + if test $? -eq 0; then + cd /bin + chmod ugo+x ogp_agent 2>/dev/null + echo "`date`: The agent updater has been changed, relaunching..." + rm -Rf tmp + /bin/ogp_agent + exit 0 + fi + fi + fi + CURRENT=$(cat $AGENTDIR/Cfg/Config.pm | grep version | grep -Eo '[0-9a-f]{40}') + if [ "$CURRENT" == "$REVISION" ]; then + echo "The agent is up to date." + else + URL=https://github.com/${GitHubUsername}/${REPONAME}/archive/${REVISION}.zip + HEAD=$(curl -L -Os --head -w "%{http_code}" "$URL") + if [ "$HEAD" == "200" ]; then + echo "Updating agent using curl." + curl -L -s "$URL" -o "ogp_agent_latest.zip" + if test $? -ne 0; then + echo "`date`: curl failed to download the update package." + else + unzip -oq "ogp_agent_latest.zip" + if test $? -ne 0; then + echo "`date`: Unable to unzip the update package." + ogpGitCleanup + else + cd ${REPONAME}-${REVISION} + cp -avf OGP/* $AGENTDIR/. &> /dev/null + CP_APP_RET=$? + cp -avf bin/* /usr/bin/. &> /dev/null + CP_BIN_RET=$? + if [ $CP_APP_RET -ne 0 -o $CP_BIN_RET -ne 0 ]; then + echo "`date`: The agent files cannot be overwritten." + cd .. + ogpGitCleanup + echo "Agent update failed." + else + if test ! -e "$AGENTDIR/Cfg/Preferences.pm"; then + cp -f Cfg/Preferences.pm $AGENTDIR/Cfg/Preferences.pm &> /dev/null + fi + echo "Fixing permissions..." + chmod +x $AGENTDIR/ogp_agent.pl &> /dev/null + chmod +x $AGENTDIR/agent_conf.sh &> /dev/null + chmod +x /usr/bin/ogp_agent &> /dev/null + cd .. + ogpGitCleanup + sed -i "s/version.*/version => '${REVISION}',/" $AGENTDIR/Cfg/Config.pm + echo "Agent updated successfully." + fi + fi + fi + else + echo "There is a update available (${REVISION}) but the download source is not ready."; + echo "Try again later." + fi + fi + + else + echo "Update failed." + fi + fi + + return 0 +} + +run() { + # Runs the update and agent + update + if test -n "$RESTART" ; then + echo "Agent will auto-restart if there is a crash." + #loop forever + while true + do + # Run + cd $AGENTDIR + ./ogp_agent.pl + echo "`date`: Agent restart in $TIMEOUT seconds" + # don't thrash the hard disk if the agent dies, wait a little + sleep $TIMEOUT + done # while true + else + cd $AGENTDIR + ./ogp_agent.pl + fi +} + +quit() { + # Exits with the give error code, 1 + # if none specified. + # exit code 2 also prints syntax + exitcode="$1" + + # default to failure + if test -z "$exitcode"; then + exitcode=1 + fi + + case "$exitcode" in + 0) + echo "`date`: OGP Agent Quit" ;; + 2) + syntax ;; + *) + echo "`date`: OGP Agent Failed" ;; + esac + + # Remove pid file + if test -n "$PID_FILE" && test -f "$PID_FILE" ; then + # The specified pid file + rm -f $PID_FILE + fi + + # reset SIGINT and then kill ourselves properly + trap - 2 + kill -2 $$ +} + +##################### +# MAIN APP CODE # +##################### + +PATH=/usr/local/bin:/usr/bin:${PATH} + +# Start PureFTPD if OGP has been configured to manage FTP users +if [ ! -z "$RUN_PUREFTPD" ] && [ "$RUN_PUREFTPD" == "yes" ]; then + /usr/sbin/pure-ftpd.exe -S ${FTP_IP},${FTP_PORT} ${FTP_PASV_STRING} -lpuredb:/etc/pureftpd.pdb -g /var/run/pure-ftpd.pid & +fi + +# Setup OGP and Read Preferences +setupOGPAndReadBashPrefs + +# Initialise +init $* + +# Run +run + +# Quit normally +quit 0 \ No newline at end of file