[STEAM] Source UDF

  • Ich hab mal vor einiger zeit ein HLSW ähnliches Tool geschrieben und ich hab mir jetzt gedacht, dass sich daraus relativ leicht eine UDF schreiben lässt.

    Die UDF enthält folgende Befehle:
    - _SourceConnect()
    Verbindet sich mit einem SourceServer

    - _SourceGetChallenge()
    Gibt eine challenge number zurück, die für _SourceGetPlayers() und _SourceGetRules() benötigt wird

    - _SourceGetInfo(), _SourceGetPlayers(), _SourceGetRules()
    Geben jeweils ein Array zurück, das nur mit Hilfe dieser Seite verstanden werden kann:

    - _SourceClose()
    Schließt die offene Verbindung

    Getestet habe ich bisher nur Counterstrike Source Server, aber es sollten prinzipiell alle SourceGames gehen.

    Falls hier überhaupt Leute unterwegs sein sollten, die so etwas interessiert, würd ich mich über Feedback freuen!

    UDF:

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Const $S2C_CHALLENGE = Binary("0xFFFFFFFF56FFFFFFFF")
    Const $A2S_RULES = Binary("0xFFFFFFFF56")
    Const $A2S_PLAYER = Binary("0xFFFFFFFF55")
    Const $A2S_INFO = Binary("0xFFFFFFFF54536F7572636520456E67696E6520517565727900")
    Global $g_PING = 0, $socket

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; ////////////////////// Functions \\\\\\\\\\\\\\\\\\\\\\
    ; ===================\
    ;_SourceConnect
    ;_SourceGetChallenge
    ;_SourceGetInfo
    ;_SourceGetPlayers
    ;_SourceGetRules
    ;_SourceClose
    ;
    ;__SourceReceiveData
    ;__SourceBinaryToArray
    ; ===================/

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _SourceConnect
    ; Description ...: Baut ein Verbindung zum Server auf
    ; Syntax ........: _SourceConnect($sIP)
    ; Parameters ....: $sIP - A string value.
    ; Return values .: Socket
    ; ===============================================================================================================================
    Func _SourceConnect($sIP)
    UDPStartup()
    $aIP = StringRegExp($sIP, "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\:([0-9]{1,6})", 3)
    If Not IsArray($aIP) Then
    MsgBox(4112, "Error", "Keine gültige Ip-Addresse!")
    Return
    EndIf
    Local $socket = UDPOpen($aIP[0], $aIP[1])
    If @error Then
    MsgBox(4112, "Error", "UDPOpen failed with WSA error: " & @error)
    Exit
    EndIf
    Return $socket
    EndFunc ;==>_SourceConnect

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _SourceGetChallenge
    ; Description ...: Empfängt die Challenge Number
    ; Syntax ........: _SourceGetChallenge($socket[, $iTimeOut = 2000])
    ; Parameters ....: $socket - A string value.
    ; $iTimeOut - [optional] An integer value. Default is 2000.
    ; Return values .: On Success - Returns Binary
    ; Timeout - -1
    ; ===============================================================================================================================
    Func _SourceGetChallenge($socket, $iTimeOut = 2000)
    Local $timer = TimerInit()
    UDPSend($socket, $S2C_CHALLENGE)
    Do
    Local $data = Binary(UDPRecv($socket, 50))
    If $data <> "" And BinaryLen($data) = 9 Then Return BinaryMid($data, 6)
    Sleep(10)
    Until TimerDiff($timer) > $iTimeOut
    Return -1
    EndFunc ;==>_SourceGetChallenge

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _SourceGetInfo
    ; Description ...: Empfängt die Serverinfo
    ; Syntax ........: _SourceGetInfo($socket)
    ; Parameters ....: $socket - A string value.
    ; Return values .: Array
    Func _SourceGetInfo($socket)
    UDPSend($socket, $A2S_INFO)
    Return __SourceBinaryToArray(__SourceReceiveData($socket), 'nnnhhssssiniiicchhs')
    EndFunc ;==>_SourceGetInfo

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _SourceGetPlayers
    ; Description ...: Empfängt die Spielerdaten
    ; Syntax ........: _SourceGetPlayers($socket, $bChallenge)
    ; Parameters ....: $socket - A string value.
    ; $bChallenge - A binary value.
    ; Return values .: Array
    ; ===============================================================================================================================
    Func _SourceGetPlayers($socket, $bChallenge)
    UDPSend($socket, $A2S_PLAYER + $bChallenge)
    Local $sRecv = __SourceReceiveData($socket)
    Local $iCount = Int(BinaryMid($sRecv, 6, 1))
    If $iCount = 0 Then Return 0
    Local $sPattern = "nnnnn"
    For $iI = 1 To $iCount
    $sPattern &= "nslf"
    Next
    Return __SourceBinaryToArray($sRecv, $sPattern)
    EndFunc ;==>_SourceGetPlayers

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _SourceGetRules
    ; Description ...: Empfängt die Server Rules
    ; Syntax ........: _SourceGetRules($socket, $bChallenge)
    ; Parameters ....: $socket - A string value.
    ; $bChallenge - A binary value.
    ; Return values .: Array
    ; ===============================================================================================================================
    Func _SourceGetRules($socket, $bChallenge)
    UDPSend($socket, $A2S_RULES + $bChallenge)
    Local $iCount, $sPattern
    Local $sRecv = __SourceReceiveData($socket)
    If StringLeft($sRecv, 4) = "0xFE" Then
    $iCount = Int(BinaryMid($sRecv, 18, 1))
    $sPattern = "nnnnnnnnnnnnnnnnnn"
    Else
    $iCount = Int(BinaryMid($sRecv, 6, 1))
    $sPattern = "nn"
    EndIf
    For $iI = 1 To $iCount
    $sPattern &= "ss"
    Next
    Return __SourceBinaryToArray($sRecv, $sPattern)
    EndFunc ;==>_SourceGetRules

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: __SourceReceiveData
    ; Description ...: Empfängt ein oder mehrere Pakete und gibt diese als String aus
    ; Syntax ........: __SourceReceiveData($socket[, $iTimeOut = 2000])
    ; Parameters ....: $socket - A string value.
    ; $iTimeOut - [optional] An integer value. Default is 2000.
    ; Return values .: On Success - Returns String
    ; Timeout - -1
    ; ===============================================================================================================================
    Func __SourceReceiveData($socket, $iTimeOut = 2000)
    Local $timer = TimerInit()
    Do
    Local $sString = Binary(UDPRecv($socket, 2048))
    If $sString <> '' Then
    $g_PING = Round(TimerDiff($timer), 2) & " ms"
    If BinaryMid($sString, 1, 4) = "0xFEFFFFFF" Then ; Falls das Paket in mehrere Parts gesplittet ist
    Local $iPacketCount = Int(BinaryMid($sString, 9, 1))
    For $iCurrentPacket = 1 To $iPacketCount ; Alle Pakete zusammenfügen
    $sChunk = UDPRecv($socket, 2048)
    If $sChunk <> '' Then $sString &= BinaryMid(Binary($sChunk), 0xD)
    If TimerDiff($timer) > $iTimeOut Then Return -1
    Next
    Return $sString
    Else
    Return $sString
    EndIf
    EndIf
    Sleep(10)
    Until TimerDiff($timer) > $iTimeOut
    Return -1
    EndFunc ;==>__SourceReceiveData

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: __SourceBinaryToArray
    ; Description ...: Splittet einen BinaryString nach einem bestimmten Pattern
    ; Syntax ........: __SourceBinaryToArray($sString, $sPattern)
    ; Parameters ....: $sString - A string value.
    ; $sPattern - Zeichenfolge
    ; i = 1 Byte als Ganzzahl
    ; h = 1 Byte in Hexschreibweise
    ; c = 1 Byte als Char
    ; l = long (32bit signed BigEndian)
    ; f = float (32bit BigEndian)
    ; s = Null-terminated String
    ; n = Null Byte (ein Byte überspringen)
    ; Return values .: None
    ; Author ........: Your Name
    ; Modified ......:
    ; Remarks .......:
    ; Related .......:
    ; Link ..........:
    ; Example .......: No
    ; ===============================================================================================================================
    Func __SourceBinaryToArray($sString, $sPattern)
    Local $iByte = 3, $aBytes = StringSplit(StringTrimLeft($sString, 2), ""), $aPattern = StringSplit($sPattern, ""), $aResult[1]
    For $iType = 1 To $aPattern[0]
    Switch $aPattern[$iType]
    Case "i"
    _ArrayAdd($aResult, Int("0x" & $aBytes[$iByte] & $aBytes[$iByte + 1]))
    $iByte += 2
    Case "h"
    _ArrayAdd($aResult, "0x" & $aBytes[$iByte] & $aBytes[$iByte + 1])
    $iByte += 2
    Case "c"
    _ArrayAdd($aResult, Chr("0x" & $aBytes[$iByte] & $aBytes[$iByte + 1]))
    $iByte += 2
    Case "l"
    Local $iLong = 0
    For $iI = 6 To 0 Step -2
    $iLong &= $aBytes[$iByte + $iI] & $aBytes[$iByte + $iI + 1]
    Next
    _ArrayAdd($aResult, Dec($iLong))
    $iByte += 8
    Case "f"
    Local $iFloat = ""
    For $iI = 6 To 0 Step -2
    $iFloat &= $aBytes[$iByte + $iI] & $aBytes[$iByte + $iI + 1]
    Next
    Local $tInt = DllStructCreate("int")
    Local $tFloat = DllStructCreate("float", DllStructGetPtr($tInt))
    DllStructSetData($tInt, 1, Dec($iFloat))
    _ArrayAdd($aResult, DllStructGetData($tFloat, 1))
    $iByte += 8
    Case "s"
    Local $sChunk = "0x"
    Local $sCurrentChar = $aBytes[$iByte] & $aBytes[$iByte + 1]
    While $sCurrentChar <> "00"
    $sChunk &= $sCurrentChar
    $iByte += 2
    $sCurrentChar = $aBytes[$iByte] & $aBytes[$iByte + 1]
    WEnd
    _ArrayAdd($aResult, BinaryToString($sChunk, 4))
    $iByte += 2
    Case "n"
    $iByte += 2
    EndSwitch
    Next
    $aResult[0] = UBound($aResult) - 1
    Return $aResult
    EndFunc ;==>__SourceBinaryToArray

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _SourceClose
    ; Description ...: Schließt die offene UDP-Verbindung
    ; Syntax ........: _SourceClose()
    ; ===============================================================================================================================
    Func _SourceClose($socket)
    UDPCloseSocket($socket)
    UDPShutdown()
    EndFunc ;==>_SourceClose

    [/autoit]

    Testscript:

    Spoiler anzeigen
    [autoit]

    #include "steam.au3"

    [/autoit] [autoit][/autoit] [autoit]

    $socket = _SourceConnect("77.111.213.213:27015")
    $bChallenge = _SourceGetChallenge($socket)
    $aInfo = _SourceGetInfo($socket)
    $aRules = _SourceGetRules($socket, $bChallenge)
    $aPlayers = _SourceGetPlayers($socket, $bChallenge)
    _ArrayDisplay($aInfo)
    _ArrayDisplay($aRules)
    _ArrayDisplay($aPlayers)
    MsgBox(0, "", $g_PING)
    _SourceClose($socket)

    [/autoit]

    K4z