#include-once ;~ #include "../V.au3" ; #INDEX# ======================================================================================================================= ; Title .........: SrcDSQLib ; Version........: 1.5 ; AutoIt Version : 3.3.0.0++ ; Language ......: German ; Description ...: Dieses Modul erhält Funktionen zur Abfrage eines Source Servers. ; Licence........: GPL 2.0 http://creativecommons.org/licenses/GPL/2.0/deed.de ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ;_SrcDSQ_Challenge ;_SrcDSQ_Info ;_SrcDSQ_Init ;_SrcDSQ_Ping ;_SrcDSQ_Player ;_SrcDSQ_RCon ;_SrcDSQ_RCon_Init ;_SrcDSQ_RCon_Shutdown ;_SrcDSQ_Rules ;_SrcDSQ_GetServerList ;_SrcDSQ_Shutdown ; =============================================================================================================================== ; #INTERNAL_USE_ONLY#============================================================================================================ ;_SrcDSQ_Request ;_SrcDSQ_SetType ;__CheckBytes ;__ParseMasterServerQuery ;__RCONPackageCreate ;__ReadHexNum ;__ReadHexStr ;__ToHex ; =============================================================================================================================== Global Const $__BYTE = 1 Global Const $__SHORT = 2 Global Const $__LONG = 4 ; Source versions Global Const $__SrcDSQ_GoldSrc = 48 Global Const $__SrcDSQ_Src06 = 7 ;~ Global Const $__SrcDSQ_SrcMac = 15 ; Queries Global Const $__SrcDSQ_QR = "FFFFFFFF" ; -1 Global Const $__SrcDSQ_RM = "FEFFFFFF" ; -2 Global Const $__SrcDSQ_Challenge_Q = "56FFFFFFFF"; __ToHex("V") & __ToHex(-1) Global Const $__SrcDSQ_Challenge_R = "41" ;__ToHex("A") Global Const $__SrcDSQ_Info_Q = "54536F7572636520456E67696E6520517565727900" ;__ToHex("TSource Engine Query") & "00" Global Const $__SrcDSQ_Info_R = "49" ;__ToHex("I") Global Const $__SrcDSQ_Ping_Q = "69" ;__ToHex("i") Global Const $__SrcDSQ_Ping_R_Src = "6A303030303030303030303030303000" ;__ToHex("j00000000000000") & "00" Global Const $__SrcDSQ_Ping_R_GSrc = "6A00" ;__ToHex("j") & "00" Global Const $__SrcDSQ_Ping_R_Ban = "6C42616E6E6564206279207365727665720A00" ;__ToHex("lBanned by server" & @LF) & "00" Global Const $__SrcDSQ_Player_Q = "55" ;__ToHex("U") Global Const $__SrcDSQ_Player_R = "44" ;__ToHex("D") Global Const $__SrcDSQ_Rules_Q = "56" ;__ToHex("V") Global Const $__SrcDSQ_Rules_R = "45" ;__ToHex("E") ; RCon Global Const $__SrcDSQ_RCon_AUTH = 3 Global Const $__SrcDSQ_RCon_AUTH_RESP = 2 Global Const $__SrcDSQ_RCon_EXEC = 2 Global Const $__SrcDSQ_RCon_RESP_VAL = 0 ; #GLOBAL_USE# ================================================================================================================== ; Name...........: _SrcDSQ_Timeout ; Description ...: Wartezeit für ein Packet in Millisekunden. ; Remarks .......: Valve Standard liegt bei 2000 ms. ; =============================================================================================================================== Global $_SrcDSQ_Timeout = 2000 #Region SrcDSQ ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_Challenge ; Description ...: Liefert die Challenge Nummer für _SrcDSQ_Player() und _SrcDSQ_Rules() ; Syntax.........: _SrcDSQ_Challenge($vIP[, $iPort = 27015]) ; Parameters ....: $vIP - Eine gültige IP-Adresse z.B."192.168.0.1" oder ein Socket, der von _SrcDSQ_Init erstellt wurde ; $iPort - Serverport ; Return values .: Erfolg - Challenge ID ; Fehler - 0, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA Error ; |3 - Source Server ist nicht vorhanden oder antwortet nicht ; |4 - Deine IP wurde vom Server verbannt ; |5 - Datenpacket kann nicht gelesen werden ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Challenge(ByRef $vIP, $iPort = 27015) If IsArray($vIP) Then Local $ahSrcDSQSocket = $vIP Else Local $ahSrcDSQSocket = _SrcDSQ_Init($vIP, $iPort, 0) If @error Then Return SetError(@error, @extended, "") $ahSrcDSQSocket[2] = 1 EndIf Local $sRecv = _SrcDSQ_Request($ahSrcDSQSocket, $__SrcDSQ_Challenge_Q) If @error Then Return SetError(@error, @extended, "") If Not __CheckBytes($sRecv, $__SrcDSQ_Challenge_R) Then SetError(5, @extended, "") Return StringLeft($sRecv, 8) EndFunc ;==>_SrcDSQ_Challenge Func _A2S_SERVERQUERY_GETCHALLENGE($vIP, $iPort = 27015) Return _SrcDSQ_Challenge($vIP, $iPort) EndFunc ;==>_A2S_SERVERQUERY_GETCHALLENGE ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_Info ; Description ...: Fragt allgemeine Serverdaten wie Servername, Map, Spieleranzahl, etc. ab. ; Syntax.........: _SrcDSQ_Info($vIP[, $iPort = 27015]) ; Parameters ....: $vIP - Eine gültige IP-Adresse z.B."192.168.0.1" oder ein Socket, der von _SrcDSQ_Init erstellt wurde ; $iPort - Serverport ; Return values .: Erfolg - Array mit Daten ; [1] <- Protokollversion -- number ; [2] <- Servername -- string ; [3] <- Mapname -- string ; [4] <- Spielverzeichnis -- string ; [5] <- Spielname -- string ; [6] <- AppID -- number ; [7] <- Spieleranzahl -- number ; [8] <- Maximale Spieleranzahl-- number ; [9] <- Botanzahl -- number ; [10] <- Servertyp -- string: "d"-dedicated / "l"-listened / "p"-SourceTV ; [11] <- Betriebssystem -- string: "w"-Windows / "l"-Linux ; [12] <- Passwortstatus -- boolean ; [13] <- VAC Status -- boolean ; [14] <- Spielversion -- string ; [15] <- Erweitert -- boolean ; [16] <- Port -- number ; [17] <- SteamID -- number ; [18] <- HLTV -- string: HLTV Port mit Servernamen durch ":" getrennt z.b.'30001:"War live!"' ; [19] <- Tags -- string: Sind durch ein Komma (,) getrennt ; ; Fehler - 0, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA Error ; |3 - Source Server ist nicht vorhanden oder antwortet nicht ; |4 - Deine IP wurde vom Server verbannt ; |5 - Datenpacket kann nicht gelesen werden ; Remarks .......: Alle Daten sind in Strings konvertiert. ; Indizies [16] - [19] sind nur dann vorhanden, wenn [15] "1" ist. Ob diese einen Wert haben, ist ; von der Serverversion abhängig. ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Info($vIP, $iPort = 27015) If IsArray($vIP) Then Local $ahSrcDSQSocket = $vIP Else Local $ahSrcDSQSocket = _SrcDSQ_Init($vIP, $iPort, 0) If @error Then Return SetError(@error, @extended, 0) $ahSrcDSQSocket[2] = 1 EndIf Local $sRecv = _SrcDSQ_Request($ahSrcDSQSocket, $__SrcDSQ_Info_Q) If @error Then Return SetError(@error, @extended, 0) If Not __CheckBytes($sRecv, $__SrcDSQ_Info_R) Then Return SetError(5, @extended, 0) ;Header check Local $asData[16] $asData[0] = 15 $asData[1] = __ReadHexNum($sRecv, $__BYTE); version -- byte $asData[2] = __ReadHexStr($sRecv) ; name -- string $asData[3] = __ReadHexStr($sRecv) ; map name -- string $asData[4] = __ReadHexStr($sRecv) ; game dir -- string $asData[5] = __ReadHexStr($sRecv) ; game name -- string $asData[6] = __ReadHexNum($sRecv, $__SHORT); AppID -- short $asData[7] = __ReadHexNum($sRecv, $__BYTE); players -- byte $asData[8] = __ReadHexNum($sRecv, $__BYTE); Max players -- byte $asData[9] = __ReadHexNum($sRecv, $__BYTE); Bots -- byte $asData[10] = __ReadHexStr($sRecv, $__BYTE) ;Type -- byte $asData[11] = __ReadHexStr($sRecv, $__BYTE) ; OS -- byte $asData[12] = __ReadHexNum($sRecv, $__BYTE); Password status -- byte $asData[13] = __ReadHexNum($sRecv, $__BYTE); VAC status -- byte $asData[14] = __ReadHexStr($sRecv) ; Game version -- string If StringLeft($sRecv, 6) <> "" Then ; Extended -- byte Local $iEDF = __ReadHexNum($sRecv, $__BYTE) ReDim $asData[20] $asData[15] = "1" $asData[0] = 19 If BitAND($iEDF, 0x80) Then $asData[16] = __ReadHexNum($sRecv, $__SHORT); Port -- short If BitAND($iEDF, 0x10) Then $asData[17] = __ReadHexNum($sRecv, $__LONG + $__LONG); SteamID -- long long If BitAND($iEDF, 0x40) Then $asData[18] = __ReadHexNum($sRecv, $__SHORT) & ':"' & _ ;Spec Port -- short __ReadHexStr($sRecv) & '"' ; Spec Name -- string If BitAND($iEDF, 0x20) Then $asData[19] = __ReadHexStr($sRecv) ; Tags -- string Else $asData[15] = "0" EndIf Return $asData EndFunc ;==>_SrcDSQ_Info Func _A2S_INFO($sIP, $iPort = 27015) Return _SrcDSQ_Info($sIP, $iPort) EndFunc ;==>_A2S_INFO ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_Init ; Description ...: Erstellt einen Socket, der für weitere SrcDSQ-Funktionen benötigt wird. ; Syntax.........: _SrcDSQ_Init($sIP[, $iPort = 27015[, $iCheck = 1]]) ; Parameters ....: $sIP - Eine gültige IP-Adresse z.B."192.168.0.1" ; $iPort - Serverport ; $iCheck - Schalter zur überprüfung der Version der Server Engine ; Return values .: Erfolg - Socket ; Fehler - 0, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA Error ; |3 - Source Server ist nicht vorhanden oder antwortet nicht ; |4 - Deine IP wurde vom Server verbannt ; Remarks .......: Bei $iCheck < 1 werden die Überprüfungen der Serverversion übersprungen (nicht empfehlenswert, ; es sei denn, man weis über den Server bescheid) ; Related .......: _SrcDSQ_Shutdown ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Init($sIP, $iPort = 27015, $iCheck = 1) If Not (IsString($sIP) And IsInt($iPort) And IsInt($iCheck)) Then Return SetError(1, 0, 0) Local $ahSrcDSQSocket[3] = [0, 0, 0] UDPStartup() $ahSrcDSQSocket[0] = UDPOpen($sIP, $iPort) ; Creating Socket If @error Then ; and checking for WSA errors Local $_err = @error UDPShutdown() Return SetError(2, $_err, 0) EndIf If $iCheck >= 0 Then _SrcDSQ_Ping($sIP, $iPort) ; Is there a source server? A ping test should find it out! If @error Then Switch @error Case 2; Something wrong with connection _SrcDSQ_Shutdown($ahSrcDSQSocket) Return SetError(2, 0, 0) Case 3; Server not responding _SrcDSQ_Shutdown($ahSrcDSQSocket) Return SetError(3, 0, 0) Case 4; You have a banned IP :P _SrcDSQ_Shutdown($ahSrcDSQSocket) Return SetError(4, 0, 0) EndSwitch EndIf If @extended Then $ahSrcDSQSocket[1] = @extended If $iCheck And Not $ahSrcDSQSocket[1] Then _SrcDSQ_SetType($ahSrcDSQSocket) EndIf EndIf ; No errors? Nice, have a socket! Return $ahSrcDSQSocket EndFunc ;==>_SrcDSQ_Init ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_Ping ; Description ...: Misst die Geschwindigkeit, die ein Packet zum Server und zurück braucht. ; Syntax.........: _SrcDSQ_Ping($vIP[, $iPort = 27015]) ; Parameters ....: $vIP - Eine gültige IP-Adresse z.B."192.168.0.1" oder ein Socket, der von _SrcDSQ_Init erstellt wurde ; $iPort - Serverport ; Return values .: Erfolg - Der Ping in Millisekunden ; Fehler - -1, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA Error ; |3 - Source Server ist nicht vorhanden oder antwortet nicht ; |4 - Deine IP wurde vom Server verbannt, der Ping wird trotzdem zurückgegeben ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Ping($vIP, $iPort = 27015) If IsArray($vIP) Then Local $ahSrcDSQSocket = $vIP Else Local $ahSrcDSQSocket = _SrcDSQ_Init($vIP, $iPort, -1) If @error Then Return SetError(@error, @extended, -1) $ahSrcDSQSocket[2] = 1 EndIf Local $t = TimerInit() ; Go! Local $sRecv = _SrcDSQ_Request($ahSrcDSQSocket, $__SrcDSQ_Ping_Q) If @error Then Return SetError(@error, @extended, -1) $t = TimerDiff($t) ; Stop! Switch $sRecv ; Check the validity of the server Case $__SrcDSQ_Ping_R_Src ; Source Return $t ; If header is ok, return the time Case $__SrcDSQ_Ping_R_GSrc ; GoldSrc SetExtended($__SrcDSQ_GoldSrc) Return $t Case $__SrcDSQ_Ping_R_Ban ; Valid too, but your IP is banned! Return SetError(4, 0, $t) EndSwitch Return SetError(3, @extended, -1) ; Even if there is a server, he didn't respond :( EndFunc ;==>_SrcDSQ_Ping Func _A2A_PING($sIP, $iPort = 27015) _SrcDSQ_Ping($sIP, $iPort) EndFunc ;==>_A2A_PING ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_Player ; Description ...: Liefert Daten über Spieler, die sich auf dem Server befinden. ; Syntax.........: _SrcDSQ_Player($vIP[, $iPort = 27015]) ; Parameters ....: $vIP - Eine gültige IP-Adresse z.B."192.168.0.1" oder ein Socket, der von _SrcDSQ_Init erstellt wurde ; $iPort - Serverport ; Return values .: Erfolg - 2D Array mit Daten ; [0][0] <- Anzahl der Spieler -- number ; [i][0] <- Index -- number ; [1] <- Spielername -- string ; [2] <- Punkte -- number ; [3] <- Verbindungszeit -- number: in Sekunden ; ; Fehler - 0, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA Error ; |3 - Source Server ist nicht vorhanden oder antwortet nicht ; |4 - Deine IP wurde vom Server verbannt ; |5 - Datenpacket kann nicht gelesen werden ; |6 - Komprimierte Packete werden nicht unterstützt ; Remarks .......:; Alle Daten sind in Strings konvertiert. ; Spieler, die sich zum Server verbunden haben, aber noch nicht erschienen sind, haben nur Verbindungszeit [3]. ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Player($vIP, $iPort = 27015) If IsArray($vIP) Then Local $ahSrcDSQSocket = $vIP Else Local $ahSrcDSQSocket = _SrcDSQ_Init($vIP, $iPort, 1) If @error Then Return SetError(@error, @extended, 0) $ahSrcDSQSocket[2] = 1 EndIf Local $sRecv = _SrcDSQ_Request($ahSrcDSQSocket, $__SrcDSQ_Player_Q & _SrcDSQ_Challenge($vIP, $iPort)) If @error Then Return SetError(@error, @extended, 0) If Not __CheckBytes($sRecv, $__SrcDSQ_Player_R) Then Return SetError(5, @extended, 0) Local $iNum = __ReadHexNum($sRecv, $__BYTE) ; Player number -- byte Local $asData[$iNum + 1][4] $asData[0][0] = $iNum For $i = 1 To $iNum $asData[$i][0] = __ReadHexNum($sRecv, $__BYTE) ;Index -- byte $asData[$i][1] = __ReadHexStr($sRecv) ;Name -- string $asData[$i][2] = __ReadHexNum($sRecv, $__LONG) ;Kills -- long $asData[$i][3] = __ReadHexNum($sRecv, $__LONG, True) ;Time -- float Next Return $asData EndFunc ;==>_SrcDSQ_Player Func _A2S_PLAYER($sIP, $iPort = 27015) Return _SrcDSQ_Player($sIP, $iPort) EndFunc ;==>_A2S_PLAYER ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_Rules ; Description ...: Gibt einige Servereinstellungen zurück. ; Syntax.........: _SrcDSQ_Rules($vIP[, $iPort = 27015]) ; Parameters ....: $vIP - Eine gültige IP-Adresse z.B."192.168.0.1" oder ein Socket, der von _SrcDSQ_Init erstellt wurde ; $iPort - Serverport ; Return values .: Erfolg - 2D Array mit Daten. ; [0][0] <- Anzahl der Regeln -- number ; [i][0] <- Konsolenbefehl -- string (z.B. "sv_cheats") ; [1] <- Sein Wert -- string (z.B. "0") ; Fehler - 0, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA Error ; |3 - Source Server ist nicht vorhanden oder antwortet nicht ; |4 - Deine IP wurde vom Server verbannt ; |5 - Datenpacket kann nicht gelesen werden ; |6 - Komprimierte Packete werden nicht unterstützt ; Remarks .......:; Alle Daten sind in Strings konvertiert. ; Remarks .......: Die Regelanzahl stimmt nicht immer mit der tatsächlichen überein. ; Manche Befehle enthalten keinen ("") Wert. ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Rules($vIP, $iPort = 27015) If IsArray($vIP) Then Local $ahSrcDSQSocket = $vIP Else Local $ahSrcDSQSocket = _SrcDSQ_Init($vIP, $iPort, 1) If @error Then Return SetError(@error, @extended, 0) $ahSrcDSQSocket[2] = 1 EndIf Local $sRecv = _SrcDSQ_Request($ahSrcDSQSocket, $__SrcDSQ_Rules_Q & _SrcDSQ_Challenge($vIP, $iPort)) If @error Then Return SetError(@error, @extended, 0) If Not __CheckBytes($sRecv, $__SrcDSQ_Rules_R) Then Return SetError(5, @extended, 0) Local $iNum = __ReadHexNum($sRecv, $__SHORT) ; rules count -- short Local $asData[$iNum + 1][2] $asData[0][0] = $iNum ;...goes into first place. For $i = 1 To $iNum $asData[$i][0] = __ReadHexStr($sRecv) ; Rule -- string $asData[$i][1] = __ReadHexStr($sRecv) ; Value -- string Next Return $asData EndFunc ;==>_SrcDSQ_Rules Func _A2S_RULES($sIP, $iPort = 27015) Return _SrcDSQ_Rules($sIP, $iPort) EndFunc ;==>_A2S_RULES ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: _SrcDSQ_Request ; Description ...: Kernfunktion. Sendet Serveranfragen, erwartet Antworten und setzt mehrere Packete zusammen. ; Syntax.........: _SrcDSQ_Request($ahSrcDSQSocket, $sReq) ; Parameters ....: $ahSrcDSQSocket - Das Socket, welches von _SrcDSQ_Init erstellt wurde. ; $sReq - Anfragedaten ; Return values .: Erfolg - Serverantwort als Hexadezimaler String ; Fehler - 0, setzt @error ; |1 - Verbindungsprobleme, @extended enthält WSA ERROR ; |2 - Source Server ist nicht vorhanden oder antwortet nicht ; |5 - Komprimierte Packete werden (noch) nicht unterstützt ; Remarks .......: Wenn @extended > 0 ist, wurden nicht alle Packete empfangen. ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Request(ByRef $ahSrcDSQSocket, $sReq) Local $sRecv = "", $fFristPacket = True, $iPacketSize = 1420 Local $iPacketID, $iPacketCount, $iPacketNum UDPSend($ahSrcDSQSocket[0], "0x" & $__SrcDSQ_QR & $sReq) If @error Then Local $_err = @error If $ahSrcDSQSocket[2] Then _SrcDSQ_Shutdown($ahSrcDSQSocket) Return SetError(2, $_err, 0) EndIf Local $i = 1, $t = TimerInit() While TimerDiff($t) < $_SrcDSQ_Timeout And $i > 0 $sRecv = UDPRecv($ahSrcDSQSocket[0], $iPacketSize, 1) If @error Then Local $_err = @error If $ahSrcDSQSocket[2] Then _SrcDSQ_Shutdown($ahSrcDSQSocket) Return SetError(2, $_err, 0) EndIf ;~ If $sRecv Then v($sRecv) If __CheckBytes($sRecv, "0x" & $__SrcDSQ_QR) Then Return $sRecv If __CheckBytes($sRecv, "0x" & $__SrcDSQ_RM) Then If $fFristPacket Then If BinaryMid("0x" & $sRecv, 4, 1) = 0x80 Then If $ahSrcDSQSocket[2] Then _SrcDSQ_Shutdown($ahSrcDSQSocket) Return SetError(6, 0, 0) ; No support for compressed packets yet, sorry :( EndIf $fFristPacket = False $iPacketID = __ReadHexNum($sRecv, $__LONG) ; packet chain ID Switch $ahSrcDSQSocket[1] Case $__SrcDSQ_Src06 $iPacketCount = __ReadHexNum($sRecv, $__BYTE) ; packet count $iPacketNum = __ReadHexNum($sRecv, $__BYTE) ; packet number Case $__SrcDSQ_GoldSrc $iPacketNum = __ReadHexNum($sRecv, $__BYTE / 2) ; packet number $iPacketCount = __ReadHexNum($sRecv, $__BYTE / 2) ; packet count Case Else $iPacketCount = __ReadHexNum($sRecv, $__BYTE) ; packet count $iPacketNum = __ReadHexNum($sRecv, $__BYTE) ; packet number $iPacketSize = __ReadHexNum($sRecv, $__SHORT) + 12; packet size EndSwitch $i = $iPacketCount Local $asRecv[$iPacketCount] $asRecv[$iPacketNum] = $sRecv ; Capture it! Else If $iPacketID = __ReadHexNum($sRecv, $__LONG) Then ; Has the right ID? Switch $ahSrcDSQSocket[1] ; $asRecv[$PacketNum] -> Put the packets in the right order Case $__SrcDSQ_Src06 ;Source 2006 workaround $sRecv = StringTrimLeft($sRecv, $__BYTE * 2) ; packet count $asRecv[__ReadHexNum($sRecv, $__BYTE)] = $sRecv Case $__SrcDSQ_GoldSrc ;GoldSrc workaround $asRecv[Int(StringLeft($sRecv, 1))] = StringTrimLeft($sRecv, $__BYTE * 2) Case Else $sRecv = StringTrimLeft($sRecv, $__BYTE * 2) ; The count of packets... again, no need for it $asRecv[__ReadHexNum($sRecv, $__BYTE)] = StringTrimLeft($sRecv, $__SHORT * 2) EndSwitch EndIf EndIf $i -= 1 $t = TimerInit() ;Reset timeout for other packet EndIf WEnd If IsDeclared("asRecv") Then $sRecv = "" Local $i_lost = 0 For $j = 0 To $iPacketCount - 1 If $asRecv[$j] = "" Then $i_lost += 1 $sRecv &= $asRecv[$j] Next SetExtended($i_lost) __CheckBytes($sRecv, "FFFFFFFF") Return $sRecv EndIf If $ahSrcDSQSocket[2] Then _SrcDSQ_Shutdown($ahSrcDSQSocket) Return SetError(3, 0, 0) EndFunc ;==>_SrcDSQ_Request ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_Shutdown ; Description ...: Schließt und annulliert das Socket. ; Syntax.........: _SrcDSQ_Shutdown(ByRef $ahSrcDSQSocket) ; Parameters ....: $ahSrcDSQSocket - Das Socket, welches von _SrcDSQ_Init erstellt wurde. ; Return values .: Erfolg - 1 ; Fehler - 0, setzt @error ; | nicht 0 - Socket konnte nicht geschlossen werden, WSA ERROR ; Related .......: _SrcDSQ_Init ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_Shutdown(ByRef $ahSrcDSQSocket) ; Closes the socket and UDP-instance. If Not IsArray($ahSrcDSQSocket) Then Return 0 UDPCloseSocket($ahSrcDSQSocket[0]) If @error Then Return SetError(@error, 0, 0) ;Well, someone has failed. $ahSrcDSQSocket = 0 UDPShutdown() Return 1 EndFunc ;==>_SrcDSQ_Shutdown ; #INTERNAL_USE_ONLY# =========================================================================================================== Func _SrcDSQ_SetType(ByRef $ahSrcDSQSocket) Local $Info = _SrcDSQ_Info($ahSrcDSQSocket) If IsArray($Info) Then $ahSrcDSQSocket[1] = $Info[1] EndIf EndFunc ;==>_SrcDSQ_SetType #EndRegion SrcDSQ #Region MasterQ ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_GetServerlist ; Description ...: Holt vom MasterServer bestimmte Source Server-IPs. ; Syntax.........: _SrcDSQ_GetServerlist($iInit[, $iRegion = 255[, $sType = ""[, $iSecure = 0[, $sGamedir = ""[, $sMap = "" ; [, $iLinux = 0[, $iNotEmpty = 0[, $iNotFull = 0[, $iSpecProxy = 0[, $iNotApp = 0 ; [, $iEmpty = 0[, $iWhite = 0]]]]]]]]]]]]) ; Parameters ....: $iInit - Siehe Remarks. ; - Filtereinstellungen - ; $iRegion - Die Region, wo der Server sich befindet, Standard ist 255 - Alle (siehe Remarks für mehr) ; $sType - Typ des Server: "d" für dediziert, "l" für "listened" ; $iSecure - Ob VAC auf den Server aktiv ist. ; $sGamedir - Welche Mod/Spiel der Server benutzt Bsp. "cstrike", "swarm", "dystopia" ; $sMap - Name der Map. Darf auch unvollständig sein Bsp. "de_dust", "cs_offi" ; $iLinux - Ob der Server auf Linux läuft ; $iNotEmpty - Spieler müssen vorhanden sein ; $iNotFull - Server soll nicht voll sein ; $iSpecProxy - Es werden nur HLTV Server gelistet ; $iNotApp - Server mit bestimmten AppID eines Spiels werden nicht gelistet (wie Left4Dead) ; $iEmpty - Keine Spieler auf dem Server ; $iWhite - Nur Server die auf der Whitelist stehen ; Return values .: Erfolg - 2D Array mit IP und Port ; [0][0] <- Anzahl der Server -- number ; [i][0] <- IP -- string (z.B. "78.125.158.6") ; [1] <- Port -- number (z.B. "27016") ; Fehler - 0, setzt @error ; |1 - Verbindungsprobleme, @extended enthält WSA Error ; |2 - Datenpacket kann nicht gelesen werden ; |3 - MasterServer antwortet nicht ; Remarks .......: $iInit: ; * =1: Re-/Initialization des Sockets und festlegung des Filters. Die Filterparameter können nur in diesem Fall ; gesetzt werden. Es wird das erste Packet mit IP-Adressen zerückgegeben. ; * =0: Mit jedem Aufruf wird ein Packet mit weiteren IP-Adressen zurückgegeben. ; * =-1: Schließt das Socket. ; ; Regioncodes: 0 - US Ostküste, 1 - US Westküste, 2 - Südamerika, 3 - Europa, 4 - Asien, 5 - Australien, ; 6 - Mittlerer Osten, 7 - Afrika, 255 - Rest der Welt (Alle) ; ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_GetServerlist($iInit, $iRegion = 255, $sType = "", $iSecure = 0, $sGamedir = "", $sMap = "", _ $iLinux = 0, $iNotEmpty = 0, $iNotFull = 0, $iSpecProxy = 0, $iNotApp = 0, $iEmpty = 0, $iWhite = 0) Local Static $hMasterServer, $sReqIP = "0.0.0.0:0", $sFilter = "", $_iRegion = 255, $iStarted = 0 If $iInit = 1 Then UDPCloseSocket($hMasterServer) If $iStarted Then UDPShutdown() $iStarted = 0 Else $iStarted = UDPStartup() If @error Then Return SetError(1, @error, 0) EndIf $_iRegion = $iRegion $sReqIP = "0.0.0.0:0" $sFilter = "" $hMasterServer = UDPOpen(TCPNameToIP("hl2master.steampowered.com"), 27011) If @error Then Local $_err = @error UDPShutdown() Return SetError(1, $_err, 0) EndIf If $sType Then $sFilter &= "\type\" & $sType If $iSecure Then $sFilter &= "\secure\" & $iSecure If $sGamedir Then $sFilter &= "\gamedir\" & $sGamedir If $sMap Then $sFilter &= "\map\" & $sMap If $iLinux Then $sFilter &= "\linux\" & $iLinux If $iNotEmpty Then $sFilter &= "\empty\" & $iNotEmpty If $iNotFull Then $sFilter &= "\full\" & $iNotFull If $iSpecProxy Then $sFilter &= "\proxy\" & $iSpecProxy If $iNotApp Then $sFilter &= "\napp\" & $iNotApp If $iEmpty Then $sFilter &= "\noplayers\" & $iEmpty If $iWhite Then $sFilter &= "\white\" & $iWhite ElseIf $iInit = -1 Then UDPCloseSocket($hMasterServer) $hMasterServer = 0 UDPShutdown() Return 0 EndIf If Not IsArray($hMasterServer) Then Return SetError(1, 0, 0) UDPSend($hMasterServer, Binary("1" & Chr($_iRegion) & $sReqIP & Chr(00) & $sFilter & Chr(00))) If @error Then Local $_err = @error UDPCloseSocket($hMasterServer) If $iStarted Then UDPShutdown() $iStarted = 0 EndIf Return SetError(1, $_err, 0) EndIf Local $sRecv = "", $t = TimerInit() Do $sRecv &= UDPRecv($hMasterServer, 1392, 1) Until $sRecv Or TimerDiff($t) >= 500 If Not $sRecv Then Return SetError(3, 0, 0) If Not __CheckBytes($sRecv, "0xFFFFFFFF660A") Then Return SetError(2, 0, 0) Local $asData = __ParseMasterServerPacket($sRecv) Local $last = UBound($asData) - 1 $sReqIP = $asData[$last][0] & ":" & $asData[$last][1] If IsArray($asData) Then Return $asData EndIf Return SetError(3, 0, 0) EndFunc ;==>_SrcDSQ_GetServerlist ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __ParseMasterServerPacket(ByRef $sHex) Local $asData[256][2], $i = 1 While StringLeft($sHex, 2) <> "" For $j = 0 To 3 $asData[$i][0] &= String(Int("0x" & StringLeft($sHex, 2))) $sHex = StringTrimLeft($sHex, 2) If $j < 3 Then $asData[$i][0] &= "." Next If $asData[$i][0] = "0.0.0.0" Or $asData[$i][0] = "" Then ExitLoop $asData[$i][1] = String(Int("0x" & StringLeft($sHex, 4))) $sHex = StringTrimLeft($sHex, 4) $i += 1 WEnd $asData[0][0] = $i - 1 ReDim $asData[$i][2] Return $asData EndFunc ;==>__ParseMasterServerPacket #EndRegion MasterQ #Region RCon ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_RCon_Init ; Description ...: Stellt eine Verbindung zum Server her und prüft die Richtigkeit des Passwortes. ; Syntax.........: _SrcDSQ_RCon_Init($sIP, $iPort, $sPW) ; Parameters ....: $sIP - Eine gültige IP-Adresse z.B."192.168.0.1" ; $iPort - Serverport ; $sPW - Das Rcon-Passwort, dass beim Server durch rcon_password festgelegt wurde ; Return values .: Erfolg - Socket für weitere Funktionen ; Fehler - 0, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA ERROR ; |3 - Server antwortet nicht ; |4 - Das Passwort ist falsch ; Remarks .......: Bei öfteren Eingeben eines falschen Passwortes wird die IP des Clienten verbannt (herauszufinden mit ; _SrcDSQ_Ping()). Jedoch wird das richtige Passwort weiterhin akzeptiert. ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_RCon_Init($sIP, $iPort, $sPW) If Not (IsString($sIP) And IsInt($iPort) And IsString($sPW)) Then Return SetError(1, 0, 0) ;============== Starup and connect TCPStartup() If @error Then Return SetError(1, @error, 0) Local $ahRCONSocket[2] $ahRCONSocket[0] = TCPConnect($sIP, $iPort) If @error Then Local $_err = @error TCPShutdown() Return SetError(2, $_err, 0) EndIf ;============== Create auth packet and send it Local $iRCONID = Random(0, 2 ^ 16, 1) TCPSend($ahRCONSocket[0], __RCONPackageCreate($sPW, $iRCONID, $__SrcDSQ_RCon_AUTH)) If @error Then Local $_err = @error _SrcDSQ_RCon_Shutdown($ahRCONSocket) Return SetError(2, $_err, 0) EndIf ;============== Wait for response packet Local $sRecv, $nt = TimerInit() While TimerDiff($nt) <= $_SrcDSQ_Timeout $sRecv = TCPRecv($ahRCONSocket[0], 14, 1) If @error Then Local $_err = @error _SrcDSQ_RCon_Shutdown($ahRCONSocket) Return SetError(2, $_err, 0) EndIf ;============== Have one! If $sRecv Then Switch $sRecv Case "0x0A000000" & __ToHex($iRCONID) & __ToHex($__SrcDSQ_RCon_RESP_VAL) & "0000" ;"junk" packet, can be ignored ContinueLoop Case "0x0A000000" & __ToHex($iRCONID) & __ToHex($__SrcDSQ_RCon_AUTH_RESP) & "0000" ; Access granted $ahRCONSocket[1] = $sPW Return $ahRCONSocket Case "0x0A000000" & __ToHex(-1) & __ToHex($__SrcDSQ_RCon_AUTH_RESP) & "0000" ; Access denied _SrcDSQ_RCon_Shutdown($ahRCONSocket) Return SetError(4, 0, 0) Case Else ; not a Source server? ExitLoop EndSwitch EndIf WEnd _SrcDSQ_RCon_Shutdown($ahRCONSocket) Return SetError(3, 0, 0) EndFunc ;==>_SrcDSQ_RCon_Init ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_RCon ; Description ...: Sendet eine Anweisung an den Server und gibt dessen Antwort zurück. ; Syntax.........: _SrcDSQ_RCon($vIP[, $vPort = 27015[, $sPW = ""[, $sCom = ""]]]) ; Parameters ....: $vIP - Eine gültige IP-Adresse z.B."192.168.0.1" oder ein Socket, dass von _SrcDSQ_RCon_Init erstellt wurde. ; $vPort - Serverport oder Anweisungen falls über $vIP ein Socket übergeben wurde. ; $sPW - Das Rcon-Passwort, dass beim Server durch rcon_password festgelegt wurde ; $sCon - Die Anweisung, die beim Server ausgeführt werden soll. ; Return values .: Erfolg - Serverantwort ; Fehler - 0, setzt @error ; |1 - Ungültige Parameter ; |2 - Verbindungsprobleme, @extended enthält WSA ERROR ; |3 - Server antwortet nicht ; |4 - Das Passwort ist falsch ; |5 - Nicht alle Packete kamen an, die (unvollständige) Serverantwort wird dennoch zurückgegeben ; Remarks .......: Bei Nutzung des Sockets, statt einer IP-Adresse, erfolgen mehrere Abfragen auf einen einzelnen Server deutlich ; schneller. Das Kommando darf dabei an $vPort übergeben und $sPW und $sCom leergelassen werden. ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_RCon($vIP, $vPort = 27015, $sPW = "", $sCom = "") If IsString($vIP) Then If Not IsString($sCom) Then Return SetError(1, 0, 0) Local $ahRCONSocket = _SrcDSQ_RCon_Init($vIP, $vPort, $sPW) If @error Then Return SetError(@error, 0, 0) Local $hRCONSocket = $ahRCONSocket[0] ElseIf IsArray($vIP) Then Local $hRCONSocket = $vIP[0] $sPW = $vIP[1] $sCom = $vPort Else Return SetError(1, 0, 0) EndIf ;============== Make ID and send request Local $iRCONID = Random(0, 2 ^ 16, 1) TCPSend($hRCONSocket, __RCONPackageCreate($sCom, $iRCONID, $__SrcDSQ_RCon_EXEC)) If @error Then Local $_err = @error If IsString($vIP) Then _SrcDSQ_RCon_Shutdown($hRCONSocket) Return SetError(2, $_err, 0) EndIf ;============== Wait for answer Local $sRecv, $iSize, $iTerminatorID, $sRet = "", $nFirst = True Local $t = TimerInit() While TimerDiff($t) <= $_SrcDSQ_Timeout $sRecv = TCPRecv($hRCONSocket, $__LONG, 1) If @error Then Local $_err = @error If IsString($vIP) Then _SrcDSQ_RCon_Shutdown($hRCONSocket) Return SetError(2, $_err, 0) EndIf ;============== Get the size If $sRecv Then $sRecv = StringTrimLeft($sRecv, 2) $iSize = __ReadHexNum($sRecv, $__LONG) ;============== Get the the whole packet $sRecv = TCPRecv($hRCONSocket, $iSize, 1) If @error Then Local $_err = @error If IsString($vIP) Then _SrcDSQ_RCon_Shutdown($hRCONSocket) Return SetError(2, $_err, 0) EndIf $sRecv = StringTrimLeft($sRecv, 2) ;============== Catch the terminator packet If Not $nFirst And $iSize < 30 And $sRecv = __ToHex($iTerminatorID) & __ToHex($__SrcDSQ_RCon_RESP_VAL) & "0000" Then ExitLoop ;============== The actual packet processing If __ReadHexNum($sRecv, $__LONG) = $iRCONID Then __CheckBytes($sRecv, __ToHex($__SrcDSQ_RCon_RESP_VAL)) $sRet &= __ReadHexStr($sRecv) EndIf ;============== send the terminator packet If $nFirst And $iSize > 3000 Then $iTerminatorID = Random(0, 2 ^ 16, 1) TCPSend($hRCONSocket, __RCONPackageCreate($sPW, $iTerminatorID, $__SrcDSQ_RCon_AUTH)) $nFirst = False EndIf $t = TimerInit() EndIf WEnd If IsString($vIP) Then _SrcDSQ_RCon_Shutdown($hRCONSocket) If Not $nFirst And TimerDiff($t) > $_SrcDSQ_Timeout Then SetError(5) Return $sRet EndFunc ;==>_SrcDSQ_RCon ; #FUNCTION# ==================================================================================================================== ; Name...........: _SrcDSQ_RCon_Shutdown ; Description ...: Schließt und annulliert das Socket. ; Syntax.........: _SrcDSQ_RCon_Shutdown(ByRef $hRCONSocket) ; Parameters ....: $hRCONSocket - Das Socket, welches von _SrcDSQ_RCon_Init erstellt wurde. ; Return values .: Erfolg - 1 ; Fehler - 0, setzt @error ; | nicht 0 - Socket konnte nicht geschlossen werden, WSA ERROR ; Author ........: A.Ix0 http://steamcommunity.com/id/A-I http://twitter.com/A_I7 ; =============================================================================================================================== Func _SrcDSQ_RCon_Shutdown(ByRef $ahRCONSocket) If IsArray($ahRCONSocket) Then TCPCloseSocket($ahRCONSocket[0]) Else TCPCloseSocket($ahRCONSocket) EndIf If @error Then Return SetError(@error, 0, 0) $ahRCONSocket = 0 TCPShutdown() Return 1 EndFunc ;==>_SrcDSQ_RCon_Shutdown ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __RCONPackageCreate($sCom, $iRCONID, $iCom) Return Binary("0x" & __ToHex(StringLen($sCom) + 10) & __ToHex($iRCONID) & __ToHex($iCom) & __ToHex($sCom) & "0000") EndFunc ;==>__RCONPackageCreate #EndRegion RCon #Region Helpfuncs ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __ReadHexStr(ByRef $sHex, $iLen = -1) If Not IsString($sHex) Then Return SetError(1, 0, 0) Local $sRet = "" Do $sRet &= StringLeft($sHex, 2) $sHex = StringTrimLeft($sHex, 2) $iLen -= 1 Until StringRight($sRet, 2) = "00" Or StringLeft($sHex, 2) = "" Or $iLen = 0 If StringRight($sRet, 2) = "00" Then $sRet = StringTrimRight($sRet, 2) Return BinaryToString("0x" & $sRet, 4) EndFunc ;==>__ReadHexStr ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __ReadHexNum(ByRef $sHex, $iLen, $fFloat = False) If Not IsString($sHex) Then Return SetError(1, 0, 0) If $iLen = $__BYTE / 2 Then Local $sNum = StringLeft($sHex, $iLen * 2) Else If not ($iLen > 0) Then Return SetError(2, 0, 0) Local $sNum, $iL = $iLen * 2 Do $sNum &= StringMid($sHex, $iL - 1, 2) $iL -= 2 Until $iL = 0 EndIf $sHex = StringTrimLeft($sHex, $iLen * 2) If $fFloat Then Local $tInt = DllStructCreate("int") Local $tFloat = DllStructCreate("float", DllStructGetPtr($tInt)) DllStructSetData($tInt, 1, Int("0x" & $sNum)) Return DllStructGetData($tFloat, 1) EndIf If $iLen < 5 Then Return String(Dec($sNum)) Else Return String(Int("0x" & $sNum)) EndIf EndFunc ;==>__ReadHexNum ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __CheckBytes(ByRef $sHex, $sBytes) Local $iLen = StringLen($sBytes) If StringLeft($sHex, $iLen) <> $sBytes Then Return 0 Else $sHex = StringTrimLeft($sHex, $iLen) Return 1 EndIf EndFunc ;==>__CheckBytes ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __ToHex($in) If IsInt($in) Then $in = Hex($in) Local $bNum, $l = StringLen($in) Do $bNum &= StringMid($in, $l - 1, 2) $l -= 2 Until $l <= 0 Return $bNum EndIf If IsString($in) Then Return StringMid(StringToBinary($in, 4), 3) EndIf EndFunc ;==>__ToHex #EndRegion Helpfuncs