# Control menu for Windows: double-click cs.cmd (pick a number) # or directly: cs.cmd status | start | stop | restart | map | mode dm|gg | bots | kills # | say | join | console | update param([Parameter(ValueFromRemainingArguments = $true)][string[]]$Cmd) . (Join-Path $PSScriptRoot 'common.ps1') trap { Write-Host "error: $($_.Exception.Message)" -ForegroundColor Red; exit 1 } $Root = Split-Path -Parent $PSScriptRoot $settingsFile = Join-Path $Root 'settings.txt' if (-not (Test-Path -LiteralPath $settingsFile)) { throw 'not set up yet: double-click setup.cmd first' } $H = Join-Path $Root 'hlds'; $C = Join-Path $H 'cstrike' function Port { [int](Read-Settings $settingsFile).CS_PORT } function Rcon($command) { Invoke-Rcon (Port) (Get-RconPassword $Root) $command } function Answered($reply) { -not $reply.StartsWith('(no answer') } function Show-Status { if (-not (Get-ServerProcess $H)) { Write-Host ' server is stopped (start it: 9)'; return } $i = Get-A2SInfo '127.0.0.1' (Port) 2000 if ($i) { Write-Host " $(Format-A2SInfo $i)" } else { Write-Host ' server window open, not answering yet (starting or changing map?)' } Write-Host " mode: $(Get-ServerMode $Root)" } function Show-Players { Show-Status if (Get-ServerProcess $H) { (Rcon 'status') -split "`n" | Where-Object { $_ -match '^(#|map|players)' } | ForEach-Object { Write-Host " $_" } } } function Set-Map($m) { $maps = @(Get-ChildItem -LiteralPath "$C\maps" -Filter *.bsp -ErrorAction SilentlyContinue | ForEach-Object { $_.BaseName } | Sort-Object) if (-not $m) { Write-Host ' Maps on this server:' for ($i = 0; $i -lt $maps.Count; $i++) { Write-Host (' {0,3}) {1}' -f ($i + 1), $maps[$i]) } $m = (Read-Host ' map name or number').Trim() if ($m -match '^\d+$' -and [int]$m -ge 1 -and [int]$m -le $maps.Count) { $m = $maps[[int]$m - 1] } } if (-not $m) { return } if ($maps -notcontains $m) { Write-Host " no map called '$m' in $C\maps"; return } $r = Rcon "changelevel $m" if (Answered $r) { Write-Host " changing to $m" } else { Write-Host " $r" } } function Set-Mode($m) { if (-not $m) { $m = (Read-Host ' mode: dm = deathmatch, gg = GunGame').Trim() } if ($m -ne 'dm' -and $m -ne 'gg') { Write-Host ' use dm or gg'; return } $r = Rcon "esef_mode $m" if (Answered $r) { ($r -split "`n" | Where-Object { $_ -match 'esef_mode' }) | ForEach-Object { Write-Host " $_" } } else { Write-Host " $r" } } function Set-Bots($n) { $s = Read-Settings $settingsFile if (-not $n) { $n = (Read-Host " fill the server up to how many players with bots? (0 = no bots, now $($s.CS_BOTS))").Trim() } if ($n -notmatch '^\d+$') { Write-Host ' give a number'; return } Write-Setting $settingsFile 'CS_BOTS' $n # remembered for the next start $r = Rcon "yb_quota $n" if (Answered $r) { Write-Host " bots: up to $n players (saved)" } else { Write-Host " saved, applies when the server runs" } } function Show-Kills { $t = Update-KillTally "$C\logs" (Join-Path $Root 'kills-tally.json') Write-Host '' Write-Host (' ALL-TIME KILLS: {0:N0} ({1} days of carnage, {2} maps played)' -f $t.Total, $t.Days.Count, $t.Maps) Write-Host ''; Write-Host ' top fraggers:' $i = 0 foreach ($e in ($t.Who.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 5)) { $i++; Write-Host (' {0}. {1,-18} {2:N0}' -f $i, $e.Key, $e.Value) } Write-Host ''; Write-Host ' weapons of choice:' foreach ($e in ($t.Weapons.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 5)) { Write-Host (' {0,-18} {1:N0}' -f $e.Key, $e.Value) } } function Start-Server { if (Start-CsServer $Root) { Write-Host -NoNewline ' starting' $i = Wait-CsServer (Port) 60 Write-Host '' if ($i) { Write-Host " up: $(Format-A2SInfo $i)" } else { Write-Host ' no answer after 60 s, last lines:'; Get-ConsoleTail $Root 20 | ForEach-Object { Write-Host " $_" } } } } function Stop-Server { if (Stop-CsServer $Root) { Write-Host ' stopped' } else { Write-Host ' the server is not running' } } function Open-Console { Write-Host ' CS 1.6 console: status, changelevel de_nuke, yb_quota 6, esef_mode gg, say hi. Empty line or exit to leave.' while ($true) { $l = (Read-Host 'cs16').Trim() if ($l -eq '' -or $l -eq 'exit' -or $l -eq 'quit') { break } Write-Host (Rcon $l) } } function Run($choice, $rest) { switch ($choice) { { $_ -in 'status', '1' } { Show-Players } { $_ -in 'map', '2' } { Set-Map $rest } { $_ -in 'mode', '3' } { Set-Mode $rest } { $_ -in 'bots', '4' } { Set-Bots $rest } { $_ -in 'kills', '5' } { Show-Kills } { $_ -in 'say', '6' } { $t = $rest; if (-not $t) { $t = (Read-Host ' message').Trim() } if ($t) { [void](Rcon "say $t"); Write-Host ' sent' } } { $_ -in 'restart', '7' } { [void](Stop-CsServer $Root); Start-Server } { $_ -in 'stop', '8' } { Stop-Server } { $_ -in 'start', '9' } { Start-Server } { $_ -in 'join', '10' } { & (Join-Path $PSScriptRoot 'join.ps1') -Root $Root } { $_ -in 'console', '11' } { Open-Console } { $_ -in 'update', '12' } { & (Join-Path $PSScriptRoot 'setup.ps1') -Yes -Update } default { Write-Host " unknown: $choice" } } } if ($Cmd.Count -gt 0) { Run $Cmd[0] (($Cmd | Select-Object -Skip 1) -join ' '); exit 0 } while ($true) { $s = Read-Settings $settingsFile Write-Host '' Write-Host "== $($s.CS_HOSTNAME) == (UDP $($s.CS_PORT))" -ForegroundColor Cyan Show-Status Write-Host @' 1) who's playing 7) restart server 2) change map 8) stop server 3) game mode (DM / GG) 9) start server 4) bots 10) how friends join 5) kill counter 11) console (type server commands) 6) message to players 12) update game + mods 0) quit '@ $choice = (Read-Host '>').Trim() if ($choice -eq '' -or $choice -eq '0' -or $choice -eq 'q') { break } Run $choice '' }