Public/Server/Get-SteamServerInfo.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
function Get-SteamServerInfo {
    <#
    .SYNOPSIS
    Query a running steam based game server.

    .DESCRIPTION
    The cmdlet fetches server information from a running game server using UDP/IP packets.
    It will return information ServerName, Map, InstallDir, GameName, AppID, Players
    MaxPlayers, Bots, ServerType, Environment, Visibility, VAC andVersion.

    .PARAMETER IPAddress
    Enter the IP address of the Steam based server.

    .PARAMETER Port
    Enter the port number of the Steam based server.

    .PARAMETER Timeout
    Timeout in milliseconds before giving up querying the server.

    .EXAMPLE
    Get-SteamServerInfo -IPAddress '185.15.73.207' -Port 27015

    ```
    Protocol : 17
    ServerName : SAS Proving Ground 10 (EU)
    Map : TH-SmallTown
    InstallDir : groundbranch
    GameName : Ground Branch
    AppID : 16900
    Players : 6
    MaxPlayers : 10
    Bots : 0
    ServerType : Dedicated
    Environment : Windows
    Visibility : Public
    VAC : Unsecured
    Version : 1.0.0.0
    ExtraDataFlag : 177
    IPAddress : 185.15.73.207
    Port : 27015
    ```

    .NOTES
    Author: Jordan Borean, Chris Dent and Frederik Hjorslev Nylander

    .LINK
    https://hjorslev.github.io/SteamPS/Get-SteamServerInfo.html
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            HelpMessage = 'Enter the IP address of the Steam based server.')]
        [System.Net.IPAddress]$IPAddress,

        [Parameter(Mandatory = $true,
            HelpMessage = 'Enter the port number of the Steam based server.')]
        [int]$Port,

        [Parameter(Mandatory = $false,
            HelpMessage = 'Timeout in milliseconds before giving up querying the server.')]
        [int]$Timeout = 5000
    )

    begin {
        # A2S_INFO: Retrieves information about the server including, but not limited to: its name, the map currently being played, and the number of players.
        # https://developer.valvesoftware.com/wiki/Server_queries#A2S_INFO
        $A2S_INFO = [byte]0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x00
    }

    process {
        try {
            $Client = New-Object -TypeName Net.Sockets.UDPClient(0)
            [void]$Client.Send($A2S_INFO, $A2S_INFO.Length, $IPAddress, $Port)
            $Client.Client.SendTimeout = $Timeout
            $Client.Client.ReceiveTimeout = $Timeout
            $IPEndpoint = New-Object -TypeName Net.IPEndpoint([Net.IPAddress]::Any, 0)
            # The first 4 bytes are 255 which seems to be some sort of header.
            $ReceivedData = $Client.Receive([Ref]$IPEndpoint) | Select-Object -Skip 4
            $Stream = [System.IO.BinaryReader][System.IO.MemoryStream][Byte[]]$ReceivedData
            $Client.Close()
        } catch {
            $Exception = [Exception]::new("Could not reach server {0}:{1}.") -f $IPAddress, $Port
            $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
                $Exception,
                "ServerNotFound",
                [System.Management.Automation.ErrorCategory]::ConnectionError,
                $ReceivedData
            )
            $PSCmdlet.WriteError($ErrorRecord)
        }

        # If we cannot reach the server we will not display the empty object.
        if ($Stream) {
            # This is also a header - that will always be equal to 'I' (0x49).
            $Stream.ReadByte() | Out-Null
            [PSCustomObject]@{
                Protocol      = [int]$Stream.ReadByte()
                ServerName    = Get-PacketString -Stream $Stream
                Map           = Get-PacketString -Stream $Stream
                InstallDir    = Get-PacketString -Stream $Stream
                GameName      = Get-PacketString -Stream $Stream
                AppID         = [int]$Stream.ReadUInt16()
                Players       = [int]$Stream.ReadByte()
                MaxPlayers    = [int]$Stream.ReadByte()
                Bots          = $Stream.ReadByte()
                ServerType    = [ServerType]$Stream.ReadByte()
                Environment   = [OSType]$Stream.ReadByte()
                Visibility    = [Visibility]$Stream.ReadByte()
                VAC           = [VAC]$Stream.ReadByte()
                Version       = Get-PacketString -Stream $Stream
                ExtraDataFlag = $Stream.ReadByte()
                IPAddress     = $IPAddress
                Port          = $Port
            } # PSCustomObject
        }
    } # Process
} # Cmdlet