tas2580
Blog über Webentwicklung und Linux Server

Gameserver Status Query

tas2580  

Aus den meisten Gameservern lassen sich über einen Query-Port Daten zu ihrem Status auslesen. So kann man auf z.b. einer Webseite anzeigen lassen welche Map gerade gespielt wird oder wie viele Spieler auf dem Server sind. Es gibt im Internet Dienste die Gameserver dauerhaft überwachen und Statistiken erstellen, oft kann man dort auch die Daten eines Servers in fremde Webseiten einbinden. Wer nicht auf einen externen Dienst zurück greifen möchte kann sich selber ein Script erstellen um Gameserver abzufragen, das Problem dabei ist dass man wissen muss wie man mit dem Server sprechen muss.

Die Protokolle sind mit Ausnahme von Source oft gar nicht oder nur schlecht dokumentiert. Man muss also probieren oder den Server mit Tools wie HLSW abfragen und nebenbei den Netzwerk Traffic mitschneiden um zu sehen wie HLSW mit dem Server redet. Das ist sehr mühsam und Zeitaufwendig. Ich habe vor kurzem ein Webinterface für Gameserver geschrieben und dabei einige Protokolle gesammelt die ich hier jetzt zusammenfassen möchte.

Das Prinzip ist immer das gleiche, man sendet einen vorgegebenen Query-String an den Gameserver und erhält eine Antwort die man zur Darstellung zerlegen muss. Oft wird das Query einfach an den Gameport gesendet, bei manchen Gameservern gibt es auch einen extra Query-Port. Wenn man einmal ein Protokoll hat kann man damit gleich mehrere Spiele abfragen da es nur einige weinige Protokolle gibt die dann von mehreren Spielen genutzt werden.

Die Protokolle

Doom 3

Query
(int16) Header
(string) getInfo

FF FF 67 65 74 49 6E 66 6F                      ;ÿÿgetInfo

Response
(int16) Header
(string) infoResponse
(string) Schlüssel
(string) Wert
(string) Schlüssel
(string) Wert
(strings) Leerer Schlüssel/Wert (Ende von Schlüssel/Wert Paaren)
(byte) Spieler Nummer
(int16) Spieler Ping
(int16) Spieler Rate
(string) Unbekannt
(string) Unbekannt
(string) Spieler Name
(byte) Spieler #32 (Ende des Pakets?)

Gamespy

Query
(string) info

5C 69 6E 66 6F 5C                               ;\info\

Response
(string) Schlüssel
(string) Wert
(string) Schlüssel
(string) Wert
(string) Query ID (id.fragment)
(string) Finales Fragment

Gamespy 2

Query
(int64) Header
(byte) Trennzeichen
(string) Ping Wert (kann irgend etwas sein und dient zum prüfen ob die Antwort valide ist)
(byte) Gibt Server Info und Rules zurück wenn 0xFF, falls die Information nicht gewünscht ist kann stattdessen 0x00 gesendet werden.
(byte) Gibt Informationen zu den Spieler zurück wenn 0xFF, falls die Information nicht gewünscht ist kann stattdessen 0x00 gesendet werden.
(byte) Gibt Informationen zu den Teams zurück wenn 0xFF, falls die Information nicht gewünscht ist kann stattdessen 0x00 gesendet werden.

FE FD 00 43 4F 52 59 FF FF 00                   ;þý.CORYÿÿ.

Response
(byte) Trennzeichen
(string) Ping Wert
(string) Schlüssel
(string) Wert
(strings) Leerer Schlüssel/Wert (Ende des Schlüssel/Wert Teils)
(byte) Anzahl der Spieler
(strings) Player Info Beschreibung
(string) Leerer string (Ende der Player Info Beschreibung)
(strings) Player Info

Quake3

Query
(int16) Header
(string) getstatus

FF FF FF FF 67 65 74 73 74 61 74 75 73 0A       ;ÿÿÿÿgetstatus.

Response
(int32) Header
(string) statusResponse
(string) Schlüssel
(string) Wert
(string) Schlüssel
(string) Wert
(string) Player Info
(string) Player Info

Source

Query
(int16) Header
(byte) T (0x54)
(string) Source Engine Query

FF FF FF FF 54 53 6F 75 72 63 65 20 45 6E 67 69   ÿÿÿÿTSource Engi
6E 65 20 51 75 65 72 79 00                        ne Query

Response
(byte) Header
(byte) Version
(string) Server Name
(string) Map
(string) Game Directory
(string) Game Beschreibung
(short) AppID
(byte) Anzahl Spieler
(byte) Max.Spieler
(byte) Anzahl Bots
(byte) Dedicated "d" oder SourceTV "p"
(byte) OS "l" für Linux, "w" für Windows
(byte) Passwort geschützter Server
(byte) VAC gesichert
(string) Game Version

Ein Beispiel in PHP

Zu erst benötigt man eine Klasse zur Kommunikation mit dem Server. Die Klasse enthält Methoden um einen Socket auf zu machen, Daten zu senden und zu lesen, außerdem gibt es dort Methoden um die Antwort auszuwerten.


class server_communication
{
    var 
$socket null;
    var 
$ip '';
    var 
$port 0;
    var 
$network_protocol 'tcp';
    var 
$connected false;
    
    function 
__construct($ip$port)
    {
        
$this->ip gethostbyname($ip);
        
$this->port = (int) $port;
        if(!
$this->connect())
        {
            die(
'No Connection');
        }
    }
    
    function 
__destruct()
    {
        
$this->close();
    }
    
    function 
close()
    {
        if(
is_resource($this->socket))
        {
            
fclose($this->socket);
        }    
    }
    function 
connect()
    {
        if(
$this->network_protocol == 'tcp')
        {
            if(
$this->socket fsockopen($this->ip$this->port$errno$error3))
            {
                
$this->connected true;
                return 
true;
            }
        }
        else
        {
            if(
$this->socket fsockopen('udp://' $this->ip$this->port$errno$error3))
            {
                
$this->connected true;
                return 
true;
            }
        }
    }
    function 
send($cmd)
    {
        if(
is_resource($this->socket) && $this->connected)
        {
            
fwrite($this->socket$cmdstrlen($cmd));
        }
    }
    function 
read($timeout 3)
    {
        if(
is_resource($this->socket) && $this->connected)
        {
            
socket_set_timeout($this->socket$timeout);
            
$reply '';
            do 
            {
                
$reply .= fgetc($this->socket);
                
$status socket_get_status($this->socket);
            } 
            while(
$status['unread_bytes']);
            return 
$reply;
        }
    }
        
    function 
get_byte()
    {
        return 
ord(fread($this->socket1));
    }
    function 
get_char() 
    {
        return 
fread($this->socket1);
    }
    function 
get_int16() 
    {
        if(
$data fread($this->socket2))
        {
            
$unpacked unpack('sint'$data);
            return 
$unpacked['int'];
        }
    }
    function 
get_int32() 
    {
        if(
$data fread($this->socket4))
        {
            
$unpacked unpack('iint'$data);
            return 
$unpacked['int'];
        }
    }
    function 
get_float32()
    {
        if(
$data fread($this->socket4))
        {
            
$unpacked unpack('fint'$data);
            return 
$unpacked['int'];
        }
    }
    function 
get_string() 
    {
        
$str '';
        while((
$char fread($this->socket1)) != chr(0)) 
        {
            
$str .= $char;
        }
        return 
$str;
    }
    
    function 
get_string_part($len
    {
        return 
fread($this->socket, (int) $len);
    }
    
    function 
remove_header() 
    {
        
$str '';
        while((
$char fread($this->socket1)) != chr(0)) 
        {
            if(
ord($char) != 0)
            {
                return 
$char;
            }
        }
    }
}

Jetzt kann man mit einer zweiten Klasse den Gameserver abfragen, in dem Beispiel wird ein Gameserver der das Gamespy Protokoll benutzt abgefragt.

class server_query extends server_communication
{
    function 
gamespy()
    {
        
$this->network_protocol 'udp';
        
$this->send("\\info\\");
        
$reply $this->read();
        
$reply preg_split("#\\\#"$reply);
        for(
$i=0; isset($reply[$i]); $i++)
        {
            if(!empty(
$reply[$i]))
            {
                
$key $reply[$i];
                
$i++;
                
$value = isset($reply[$i]) ? $reply[$i] : '';
                
$return[$key] = $value;
            }
        }
        if(isset(
$return) && sizeof($return))
        {
            return 
$return;
        }
    }
}

Das ganze wird dann folgendermaßen aufgerufen:

$gameserver = new server_query($ip_adress$port);
$data $gameserver->gamespy();
print_r($data);

Welches Spiel verwendet welches Protokoll?

Eine weitere Herausforderung besteht darin rauszufeinden welches Protokoll ein bestimmtes Spiel benutzt. Auch hier hilft es wieder einfach den Netzwerk Trafic anzuschauen, wenn man einmal die Protokolle kennt lässt sich recht einfach erkennen was das Spiel spricht. Ich habe mal eine Liste mit Spielen zusammengestellt.

  • ASE (All Seeing Eyes) Protokoll
    • Soldat
  • Doom Protokoll
    • Enemy Territory: Quake Wars
    • Prey
    • Quake 4
  • Gamespy1 Protokoll
    • Battlefield 1942
    • F.E.A.R
    • Halo
    • Medal of Honor Allied Assault
    • Operation Flashpoint
    • Unreal Tournament
  • Gamespy2 Protokoll
    • Americas Army
    • Battlefield Vietnam
  • Gamespy3 Protokoll
    • Battlefield 2
    • Battlefield 2142
    • FarCry 2
    • Unreal Tournament 3
  • Gamespy4 Protokoll
    • Arma2
    • Crysis
    • Minecraft
  • HLDS Protokoll
    • Team Fortress Clasic
    • Half-Life
  • Source Protokoll
    • Alien Swarm
    • Age of Chivalry
    • Counter Strike 1.6
    • Counter Strike Condition Zero
    • Counter Strike: Source
    • Counter Strike: Global Offensive
    • Day of Defeat
    • Day of Defeat: Source
    • DayZ
    • Garry's mod
    • Half-Life 2
    • Insurgency
    • Left 4 dead
    • Left 4 dead 2
    • No More Room in Hell
    • Synergy
    • Rust
    • Zombie Panic! Source
  • Quake2 Protokoll
    • Alien Arena
    • Painkiller
    • Quake 1
    • Quake 2
  • Quake3 Protokoll
    • Call of Duty
    • Call of Duty 2
    • Call of Duty 4
    • Call of Duty: World at War
    • Nexuiz
    • Open Area
    • Quake 3
    • Return to Castle Wolfenstein
    • Soldier of Fortune 2
    • Tremulous
    • Urbanterror
    • War§ow
    • World of Padman
  • SA:MP Protokoll
    • GTA SA:MP

Ich habe auf gameserver-query.gq mal einen Dienst gebaut den ihr nutzen könnt um den Status von Gameservern auslesen zu können.

Über den Autor

tas2580

Weitere Informationen über mich findest du hier.

Ähnliche Beiträge


Kommentare

Moin,

erstmal danke für deine Arbeit =) und die Veröffentlichung deines Codes.

Leider ist mir noch nicht ganz klar, wie ich dann Steam (speziell CS:GO) abfragen soll?! Deine Funktion ist auf Gamespy gepolt, Steam hat doch aber ebenfalls UDP als Abhorchstelle oder nicht? Einen Output bekomme ich jedenfalls nicht.

Vielleicht hast du eine Idee für mich =)

Hi,

ich habe vor einiger Zeit mal eine Klasse gebastelt die alle Protokolle unterstützt die ich kenne. CS:GO sollte das Source Protokoll benutzen.
Die Klasse kannst du auf Github runterladen: https://github.com/Gameserveradmin/gameserver-status


Kommentar schreiben

URLs werden automatisch umgewandelt.
[b]DEIN TEXT[/b] für Fett gedruckt
[quote]DEIN ZITAT[/quote] für Zitate
[code]DEIN CODE[/code] für Code
captcha