Compare commits

..

3 Commits

Author SHA1 Message Date
cb90ad8030 upd README 2026-02-17 21:45:56 +00:00
b6fdf021b9 run formatter on examples 2026-02-17 21:38:28 +00:00
2800cbbcd2 Connect-Rcon now optionally accepts a timeouts hashtable
Base class now implements the IDisposable interface

improved error handling
2026-02-17 21:38:19 +00:00
8 changed files with 249 additions and 117 deletions

View File

@ -38,16 +38,14 @@ finally {
## Rcon Class ## Rcon Class
#### `Send($cmd) | Send($cmd, $timeout)` #### `Send($cmd)`
```powershell ```powershell
$rcon.Send("mapname") $rcon.Send("mapname")
$rcon.Send("g_gametype dm") $rcon.Send("g_gametype dm")
$rcon.Send("map_rotate", 2000) $rcon.Send("map_rotate")
``` ```
If the command returns a response it will be printed to the console. If the command returns a response it will be printed to the console.
Pass an optional timeout (ms) for commands that return responses in fragments. (status, map_rotate etc...)

View File

@ -3,20 +3,20 @@ param()
Import-Module ../lib/Q3Rcon.psm1 Import-Module ../lib/Q3Rcon.psm1
Function Read-HostUntilEmpty { function Read-HostUntilEmpty {
param([object]$rcon) param([object]$rcon)
"Input 'Q' or <Enter> to exit." "Input 'Q' or <Enter> to exit."
while (($line = Read-Host -Prompt "Send command") -cne [string]::Empty) { while (($line = Read-Host -Prompt 'Send command') -cne [string]::Empty) {
if ($line -eq "Q") { if ($line -eq 'Q') {
break break
} }
if ($line -in @("fast_restart", "map_rotate", "map_restart")) { if ($line -in @('fast_restart', 'map_rotate', 'map_restart')) {
$cmd = $line -replace '(?:^|_)(\p{L})', { $_.Groups[1].Value.ToUpper() } $cmd = $line -replace '(?:^|_)(\p{L})', { $_.Groups[1].Value.ToUpper() }
$rcon.$cmd() $rcon.$cmd()
} }
elseif ($line.StartsWith("map mp_")) { elseif ($line.StartsWith('map mp_')) {
$mapname = $line.Split()[1] $mapname = $line.Split()[1]
$rcon.SetMap($mapname) $rcon.SetMap($mapname)
} }
@ -26,8 +26,8 @@ Function Read-HostUntilEmpty {
} }
} }
Function Get-ConnFromPSD1 { function Get-ConnFromPSD1 {
$configpath = Join-Path $PSScriptRoot "config.psd1" $configpath = Join-Path $PSScriptRoot 'config.psd1'
return Import-PowerShellDataFile -Path $configpath return Import-PowerShellDataFile -Path $configpath
} }

View File

@ -3,8 +3,8 @@ param()
Import-Module ../lib/Q3Rcon.psm1 Import-Module ../lib/Q3Rcon.psm1
Function Get-ConnFromPSD1 { function Get-ConnFromPSD1 {
$configpath = Join-Path $PSScriptRoot "config.psd1" $configpath = Join-Path $PSScriptRoot 'config.psd1'
return Import-PowerShellDataFile -Path $configpath return Import-PowerShellDataFile -Path $configpath
} }
@ -14,7 +14,7 @@ try {
$rcon.Map() $rcon.Map()
"Rotating the map..." 'Rotating the map...'
$rcon.MapRotate() $rcon.MapRotate()
Start-Sleep -Seconds 3 # wait for map to rotate Start-Sleep -Seconds 3 # wait for map to rotate

View File

@ -3,8 +3,8 @@ param()
Import-Module ../lib/Q3Rcon.psm1 Import-Module ../lib/Q3Rcon.psm1
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") [void] [System.Reflection.Assembly]::LoadWithPartialName('System.Drawing')
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") [void] [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
$OKB = New-Object System.Windows.Forms.Button $OKB = New-Object System.Windows.Forms.Button
$OTB = New-Object System.Windows.Forms.TextBox $OTB = New-Object System.Windows.Forms.TextBox
@ -12,40 +12,40 @@ $CAB = New-Object System.Windows.Forms.Button
$Lbl = New-Object System.Windows.Forms.Label $Lbl = New-Object System.Windows.Forms.Label
$RLbl = New-Object System.Windows.Forms.Label $RLbl = New-Object System.Windows.Forms.Label
Function New-Form { function New-Form {
$form = New-Object System.Windows.Forms.Form $form = New-Object System.Windows.Forms.Form
$form.Text = "Q3Rcon Client" $form.Text = 'Q3Rcon Client'
$form.Size = New-Object System.Drawing.Size(275, 200) $form.Size = New-Object System.Drawing.Size(275, 200)
$form.StartPosition = "CenterScreen" $form.StartPosition = 'CenterScreen'
return $form return $form
} }
Function Add-OkButton { function Add-OkButton {
param($form, $rcon) param($form, $rcon)
$OKB.Location = New-Object System.Drawing.Size(65, 100) $OKB.Location = New-Object System.Drawing.Size(65, 100)
$OKB.Size = New-Object System.Drawing.Size(65, 23) $OKB.Size = New-Object System.Drawing.Size(65, 23)
$OKB.Text = "Send" $OKB.Text = 'Send'
$OKB.Add_Click({ Send-RconCommand -rcon $rcon }) $OKB.Add_Click({ Send-RconCommand -rcon $rcon })
$form.Controls.Add($OKB) $form.Controls.Add($OKB)
} }
Function Add-CloseButton($form) { function Add-CloseButton($form) {
$CAB.Location = New-Object System.Drawing.Size(140, 100) $CAB.Location = New-Object System.Drawing.Size(140, 100)
$CAB.Size = New-Object System.Drawing.Size(65, 23) $CAB.Size = New-Object System.Drawing.Size(65, 23)
$CAB.Text = "Close" $CAB.Text = 'Close'
$CAB.Add_Click({ Write-Host "Disconnecting from Rcon" -ForegroundColor Green; $form.Close() }) $CAB.Add_Click({ Write-Host 'Disconnecting from Rcon' -ForegroundColor Green; $form.Close() })
$form.Controls.Add($CAB) $form.Controls.Add($CAB)
} }
Function Add-Label($form) { function Add-Label($form) {
$Lbl.Location = New-Object System.Drawing.Size(10, 20) $Lbl.Location = New-Object System.Drawing.Size(10, 20)
$Lbl.Size = New-Object System.Drawing.Size(260, 20) $Lbl.Size = New-Object System.Drawing.Size(260, 20)
$Lbl.Text = "Input Rcon Command:" $Lbl.Text = 'Input Rcon Command:'
$form.Controls.Add($Lbl) $form.Controls.Add($Lbl)
} }
Function Add-TextBox { function Add-TextBox {
param($form, $rcon) param($form, $rcon)
$OTB.Location = New-Object System.Drawing.Size(10, 50) $OTB.Location = New-Object System.Drawing.Size(10, 50)
@ -58,31 +58,31 @@ Function Add-TextBox {
$form.Controls.Add($OTB) $form.Controls.Add($OTB)
} }
Function Add-ResponseLabel($form) { function Add-ResponseLabel($form) {
$RLbl.Location = New-Object System.Drawing.Size(10, 75) $RLbl.Location = New-Object System.Drawing.Size(10, 75)
$RLbl.Size = New-Object System.Drawing.Size(260, 20) $RLbl.Size = New-Object System.Drawing.Size(260, 20)
$RLbl.Text = "" $RLbl.Text = ''
$form.Controls.Add($RLbl) $form.Controls.Add($RLbl)
} }
Function Start-Form($form) { function Start-Form($form) {
$form.Topmost = $true $form.Topmost = $true
$form.Add_Shown({ $form.Activate() }) $form.Add_Shown({ $form.Activate() })
[void] $form.ShowDialog() [void] $form.ShowDialog()
} }
Function Send-RconCommand() { function Send-RconCommand() {
param($rcon) param($rcon)
$line = $OTB.Text $line = $OTB.Text
$line | Write-Debug $line | Write-Debug
if ($line -in @("fast_restart", "map_rotate", "map_restart")) { if ($line -in @('fast_restart', 'map_rotate', 'map_restart')) {
$RLbl.Text = "" $RLbl.Text = ''
$cmd = $line -replace '(?:^|_)(\p{L})', { $_.Groups[1].Value.ToUpper() } $cmd = $line -replace '(?:^|_)(\p{L})', { $_.Groups[1].Value.ToUpper() }
$rcon.$cmd() $rcon.$cmd()
} }
elseif ($line.StartsWith("map mp_")) { elseif ($line.StartsWith('map mp_')) {
$RLbl.Text = "" $RLbl.Text = ''
$mapname = $line.Split()[1] $mapname = $line.Split()[1]
$rcon.SetMap($mapname) $rcon.SetMap($mapname)
} }
@ -91,19 +91,19 @@ Function Send-RconCommand() {
} }
if ($resp -match '^["](?<name>[a-z_]+)["]\sis[:]\s["](?<value>.*?)\^7["]\s') { if ($resp -match '^["](?<name>[a-z_]+)["]\sis[:]\s["](?<value>.*?)\^7["]\s') {
$RLbl.Text = $Matches.name + ": " + $Matches.value $RLbl.Text = $Matches.name + ': ' + $Matches.value
} }
$OTB.Text = "" $OTB.Text = ''
} }
Function Get-ConnFromPSD1 { function Get-ConnFromPSD1 {
$configpath = Join-Path $PSScriptRoot "config.psd1" $configpath = Join-Path $PSScriptRoot 'config.psd1'
return Import-PowerShellDataFile -Path $configpath return Import-PowerShellDataFile -Path $configpath
} }
Function Main { function Main {
BEGIN { begin {
$conn = Get-ConnFromPSD1 $conn = Get-ConnFromPSD1
$rcon = Connect-Rcon -hostname $conn.host -port $conn.port -passwd $conn.passwd $rcon = Connect-Rcon -hostname $conn.host -port $conn.port -passwd $conn.passwd
Write-Host $rcon.base.ToString() -ForegroundColor Green Write-Host $rcon.base.ToString() -ForegroundColor Green
@ -115,10 +115,10 @@ Function Main {
Add-ResponseLabel($form) Add-ResponseLabel($form)
Add-TextBox -form $form -rcon $rcon Add-TextBox -form $form -rcon $rcon
} }
PROCESS { process {
Start-Form($form) Start-Form($form)
} }
END { end {
Disconnect-Rcon -rcon $rcon Disconnect-Rcon -rcon $rcon
} }
} }

View File

@ -3,16 +3,16 @@ param()
Import-Module ../lib/Q3Rcon.psm1 Import-Module ../lib/Q3Rcon.psm1
Function Get-ConnFromPSD1 { function Get-ConnFromPSD1 {
$configpath = Join-Path $PSScriptRoot "config.psd1" $configpath = Join-Path $PSScriptRoot 'config.psd1'
return Import-PowerShellDataFile -Path $configpath return Import-PowerShellDataFile -Path $configpath
} }
Function Get-DadJoke { function Get-DadJoke {
Invoke-WebRequest -Uri "https://icanhazdadjoke.com" -Headers @{accept = "application/json" } | Select-Object -ExpandProperty Content | ConvertFrom-Json | Select-Object -ExpandProperty Joke Invoke-WebRequest -Uri 'https://icanhazdadjoke.com' -Headers @{accept = 'application/json' } | Select-Object -ExpandProperty Content | ConvertFrom-Json | Select-Object -ExpandProperty Joke
} }
Function Send-Message { function Send-Message {
param($rcon) param($rcon)
$msg = Get-DadJoke $msg = Get-DadJoke

View File

@ -3,32 +3,41 @@ try {
. (Join-Path $PSScriptRoot base.ps1) . (Join-Path $PSScriptRoot base.ps1)
} }
catch { catch {
throw "unable to dot source module files" throw 'unable to dot source module files'
} }
class Rcon { class Rcon {
[Object]$base static [hashtable]$DefaultTimeouts = @{
'map' = 2000
'map_rotate' = 2000
'map_restart' = 2000
'fast_restart' = 2000
}
Rcon ([string]$hostname, [int]$port, [string]$passwd) { [Object]$base
[hashtable]$timeouts
Rcon ([string]$hostname, [int]$port, [string]$passwd, [hashtable]$timeouts = $null) {
$this.base = New-Base -hostname $hostname -port $port -passwd $passwd $this.base = New-Base -hostname $hostname -port $port -passwd $passwd
$this.timeouts = $timeouts ?? [Rcon]::DefaultTimeouts
} }
[Rcon] _login() { [Rcon] _login() {
$resp = $this.Send("login") $resp = $this.Send('login')
if ($resp -in @("Bad rcon", "Bad rconpassword.", "Invalid password.")) { if ($resp -in @('Bad rcon', 'Bad rconpassword.', 'Invalid password.')) {
throw "invalid rcon password" throw 'invalid rcon password'
} }
$this.base.ToString() | Write-Debug $this.base.ToString() | Write-Debug
return $this return $this
} }
[string] Send([string]$msg) { [string] Send([string]$cmd) {
return $this.base._send($msg) $key = $cmd.Split()[0]
if ($this.timeouts.ContainsKey($key)) {
return $this.base._send($cmd, $this.timeouts[$key])
} }
return $this.base._send($cmd)
[string] Send([string]$msg, [int]$timeout) {
return $this.base._send($msg, $timeout)
} }
[void] Say($msg) { [void] Say($msg) {
@ -36,53 +45,55 @@ class Rcon {
} }
[void] FastRestart() { [void] FastRestart() {
$this.Send("fast_restart", 2000) $this.Send('fast_restart')
} }
[void] MapRotate() { [void] MapRotate() {
$this.Send("map_rotate", 2000) $this.Send('map_rotate')
} }
[void] MapRestart() { [void] MapRestart() {
$this.Send("map_restart", 2000) $this.Send('map_restart')
} }
[string] Map() { [string] Map() {
return $this.Send("mapname") return $this.Send('mapname')
} }
[void] SetMap($mapname) { [void] SetMap($mapname) {
$this.Send("map mp_" + $mapname.TrimStart("mp_"), 2000) $this.Send('map mp_' + $mapname.TrimStart('mp_'))
} }
[string] Gametype() { [string] Gametype() {
return $this.Send("g_gametype") return $this.Send('g_gametype')
} }
[void] SetGametype($gametype) { [void] SetGametype($gametype) {
$this.Send("g_gametype $gametype") $this.Send('g_gametype', $gametype)
} }
[string] HostName() { [string] HostName() {
return $this.Send("sv_hostname") return $this.Send('sv_hostname')
} }
[void] SetHostName($hostname) { [void] SetHostName($hostname) {
$this.Send("sv_hostname $hostname") $this.Send('sv_hostname', $hostname)
} }
} }
Function Connect-Rcon { function Connect-Rcon {
param([string]$hostname, [int]$port, [string]$passwd) param([string]$hostname, [int]$port, [string]$passwd, [Parameter(Mandatory = $false)][hashtable]$timeouts)
[Rcon]::new($hostname, $port, $passwd)._login() [Rcon]::new($hostname, $port, $passwd, $timeouts)._login()
} }
Function Disconnect-Rcon { function Disconnect-Rcon {
param([Rcon]$rcon) param([Rcon]$rcon)
$rcon.base._close() if ($rcon -and $rcon.base) {
"Disconnected from {0}:{1}" -f $rcon.base.hostname, $rcon.base.port | Write-Debug $rcon.base.Dispose()
'Disconnected from {0}:{1}' -f $rcon.base.hostname, $rcon.base.port | Write-Debug
}
} }
Export-ModuleMember -Function Connect-Rcon, Disconnect-Rcon Export-ModuleMember -Function Connect-Rcon, Disconnect-Rcon

View File

@ -1,83 +1,206 @@
class Base { class Base {
static [int]$DEFAULT_RECEIVE_TIMEOUT = 100
static [int]$DEFAULT_BUFFER_SIZE = 4096
static [int]$DEFAULT_SEND_TIMEOUT = 5000
[string]$hostname [string]$hostname
[int]$port [int]$port
[string]$passwd [string]$passwd
[Object]$request [Object]$request
[Object]$response [Object]$response
hidden [System.Net.Sockets.Socket] $_socket hidden [System.Net.Sockets.Socket] $_socket
hidden [bool]$_disposed = $false
hidden [byte[]]$_receiveBuffer
Base ([string]$hostname, [int]$port, [string]$passwd) { Base ([string]$hostname, [int]$port, [string]$passwd) {
if ([string]::IsNullOrWhiteSpace($hostname)) {
throw [System.ArgumentException]::new('Hostname cannot be null or empty', 'hostname')
}
if ($port -le 0 -or $port -gt 65535) {
throw [System.ArgumentOutOfRangeException]::new('port', 'Port must be between 1-65535')
}
if ([string]::IsNullOrWhiteSpace($passwd)) {
throw [System.ArgumentException]::new('Password cannot be null or empty', 'passwd')
}
$this.hostname = $hostname $this.hostname = $hostname
$this.port = $port $this.port = $port
$this.passwd = $passwd $this.passwd = $passwd
$this.request = New-RequestPacket($this.passwd) $this.request = New-RequestPacket($this.passwd)
$this.response = New-ResponsePacket $this.response = New-ResponsePacket
$this._receiveBuffer = [byte[]]::new([Base]::DEFAULT_BUFFER_SIZE)
$ip = [system.net.IPAddress]::Parse([System.Net.Dns]::GetHostAddresses($this.hostname)[0].IPAddressToString) $this._InitializeConnection()
}
$endpoint = New-Object System.Net.IPEndPoint $ip, $this.port hidden [void] _InitializeConnection() {
try { try {
$this._socket = [System.Net.Sockets.Socket]::New( $hostEntry = [System.Net.Dns]::GetHostEntry($this.hostname)
if ($hostEntry.AddressList.Length -eq 0) {
throw [System.Net.Sockets.SocketException]::new([int][System.Net.Sockets.SocketError]::HostNotFound)
}
$ipv4Address = $hostEntry.AddressList | Where-Object { $_.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork } | Select-Object -First 1
if (-not $ipv4Address) {
throw [System.InvalidOperationException]::new("No IPv4 address found for hostname: $($this.hostname)")
}
$endpoint = [System.Net.IPEndPoint]::new($ipv4Address, $this.port)
$this._socket = [System.Net.Sockets.Socket]::new(
[System.Net.Sockets.AddressFamily]::InterNetwork, [System.Net.Sockets.AddressFamily]::InterNetwork,
[System.Net.Sockets.SocketType]::Dgram, [System.Net.Sockets.SocketType]::Dgram,
[System.Net.Sockets.ProtocolType]::UDP [System.Net.Sockets.ProtocolType]::UDP
) )
$this._socket.Connect($endpoint) $this._socket.Connect($endpoint)
$this._socket.ReceiveTimeout = 100 $this._socket.ReceiveTimeout = [Base]::DEFAULT_RECEIVE_TIMEOUT
$this._socket.SendTimeout = [Base]::DEFAULT_SEND_TIMEOUT
} }
catch [System.Net.Sockets.SocketException] { catch [System.Net.Sockets.SocketException] {
throw "Failed to create UDP connection to server." $this._Cleanup()
throw [System.InvalidOperationException]::new(
"Failed to create UDP connection to $($this.hostname):$($this.port). Error: $($_.Exception.Message)",
$_.Exception
)
} }
catch {
$this._Cleanup()
throw [System.InvalidOperationException]::new(
"Failed to initialize connection to $($this.hostname):$($this.port). Error: $($_.Exception.Message)",
$_.Exception
)
}
}
hidden [void] _ThrowIfDisposed() {
if ($this._disposed) {
throw [System.ObjectDisposedException]::new($this.GetType().Name)
}
}
hidden [bool] _IsConnected() {
return $this._socket -and -not $this._disposed -and $this._socket.Connected
} }
[string] ToString () { [string] ToString () {
return "Rcon connection {0}:{1} with pass {2}" -f $this.hostname, $this.port, $this.passwd $status = if ($this._IsConnected()) { 'Connected' } else { 'Disconnected' }
return 'Rcon connection {0}:{1} ({2})' -f $this.hostname, $this.port, $status
} }
[string] _send([string]$msg) { [string] _send([string]$msg) {
$this._socket.Send($this.request.Payload($msg)) return $this._send($msg, [Base]::DEFAULT_RECEIVE_TIMEOUT)
$buf = New-Object System.Byte[] 4096
try {
$this._socket.Receive($buf)
}
catch [System.Net.Sockets.SocketException] {
if ( $_.Exception.SocketErrorCode -eq 'TimedOut' ) {
"finished waiting for fragment" | Write-Debug
}
}
return [System.Text.Encoding]::ASCII.GetString($($buf | Select-Object -Skip $($this.response.Header().Length - 1)))
} }
[string] _send([string]$msg, [int]$timeout) { [string] _send([string]$msg, [int]$timeout) {
$this._socket.Send($this.request.Payload($msg)) if ([string]::IsNullOrEmpty($msg)) {
throw [System.ArgumentException]::new('Message cannot be null or empty', 'msg')
}
if ($timeout -le 0) {
throw [System.ArgumentOutOfRangeException]::new('timeout', 'Timeout must be positive')
}
$this._ThrowIfDisposed()
if (-not $this._IsConnected()) {
throw [System.InvalidOperationException]::new('Socket is not connected')
}
[string[]]$data = @()
$sw = [Diagnostics.Stopwatch]::StartNew()
While ($sw.ElapsedMilliseconds -lt $timeout) {
try { try {
$buf = New-Object System.Byte[] 4096 $payload = $this.request.Payload($msg)
$this._socket.Receive($buf) $bytesSent = $this._socket.Send($payload)
$data += [System.Text.Encoding]::ASCII.GetString($($buf | Select-Object -Skip $($this.response.Header().Length - 1))) if ($bytesSent -ne $payload.Length) {
Write-Warning "Not all bytes were sent. Expected: $($payload.Length), Sent: $bytesSent"
}
$responseData = [System.Text.StringBuilder]::new()
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$headerLength = $this.response.Header().Length
do {
try {
$bytesReceived = $this._socket.Receive($this._receiveBuffer)
if ($bytesReceived -gt 0) {
$dataStartIndex = [Math]::Min($headerLength, $bytesReceived)
$responseText = [System.Text.Encoding]::ASCII.GetString($this._receiveBuffer, $dataStartIndex, $bytesReceived - $dataStartIndex)
$responseData.Append($responseText) | Out-Null
}
} }
catch [System.Net.Sockets.SocketException] { catch [System.Net.Sockets.SocketException] {
if ( $_.Exception.SocketErrorCode -eq 'TimedOut' ) { if ($_.Exception.SocketErrorCode -eq 'TimedOut') {
"finished waiting for fragment" | Write-Debug Write-Debug 'Socket receive timeout - continuing to wait for more data'
} continue
}
else {
throw [System.InvalidOperationException]::new(
"Socket error during receive: $($_.Exception.Message)",
$_.Exception
)
} }
} }
} while ($sw.ElapsedMilliseconds -lt $timeout)
$sw.Stop() $sw.Stop()
return [string]::Join("", $data) return $responseData.ToString()
}
catch [System.Net.Sockets.SocketException] {
throw [System.InvalidOperationException]::new(
"Network error during send/receive: $($_.Exception.Message)",
$_.Exception
)
}
}
hidden [void] _Cleanup() {
if ($this._socket) {
try {
if ($this._socket.Connected) {
$this._socket.Shutdown([System.Net.Sockets.SocketShutdown]::Both)
}
}
catch {
Write-Debug "Error during socket shutdown: $($_.Exception.Message)"
}
finally {
$this._socket.Close()
$this._socket = $null
}
}
} }
[void] _close() { [void] _close() {
$this._socket.Close() $this.Dispose()
}
# Dispose implementation (following IDisposable pattern)
[void] Dispose() {
$this.Dispose($true)
[System.GC]::SuppressFinalize($this)
}
hidden [void] Dispose([bool]$disposing) {
if (-not $this._disposed) {
if ($disposing) {
$this._Cleanup()
}
$this._disposed = $true
}
} }
} }
Function New-Base { function New-Base {
param([string]$hostname, [int]$port, [string]$passwd) param(
[Parameter(Mandatory)]
[string]$hostname,
[Parameter(Mandatory)]
[ValidateRange(1, 65535)]
[int]$port,
[Parameter(Mandatory)]
[string]$passwd
)
[Base]::new($hostname, $port, $passwd) [Base]::new($hostname, $port, $passwd)
} }

View File

@ -2,7 +2,7 @@ class Packet {
[System.Byte[]]$MAGIC = @(, 0xFF * 4) [System.Byte[]]$MAGIC = @(, 0xFF * 4)
[string] Header() { [string] Header() {
throw "method not implemented" throw 'method not implemented'
} }
} }
@ -14,24 +14,24 @@ class RequestPacket : Packet {
} }
[System.Byte[]] Header() { [System.Byte[]] Header() {
return $this.MAGIC + [System.Text.Encoding]::ASCII.GetBytes("rcon") return $this.MAGIC + [System.Text.Encoding]::ASCII.GetBytes('rcon')
} }
[System.Byte[]] Payload([string]$msg) { [System.Byte[]] Payload([string]$msg) {
return $this.Header() + [System.Text.Encoding]::ASCII.GetBytes($(" {0} {1}" -f $this.passwd, $msg)) return $this.Header() + [System.Text.Encoding]::ASCII.GetBytes($(' {0} {1}' -f $this.passwd, $msg))
} }
} }
class ResponsePacket : Packet { class ResponsePacket : Packet {
[System.Byte[]] Header() { [System.Byte[]] Header() {
return $this.MAGIC + [System.Text.Encoding]::ASCII.GetBytes("print\n") return $this.MAGIC + [System.Text.Encoding]::ASCII.GetBytes('print\n')
} }
} }
Function New-RequestPacket([string]$passwd) { function New-RequestPacket([string]$passwd) {
[RequestPacket]::new($passwd) [RequestPacket]::new($passwd)
} }
Function New-ResponsePacket { function New-ResponsePacket {
[ResponsePacket]::new() [ResponsePacket]::new()
} }