Benutzer-Werkzeuge

Webseiten-Werkzeuge


tutorial:server:php_querry_script

Dies ist eine alte Version des Dokuments!


PHP Server-Query Script

Das hier ist ein Tutorial für ein PHP Server-Query Script das Informationen von einem HL2/CS:S Server auslesen und anzeigen kann.

Vorwort

Vor einiger Zeit fragte sich Chrisber, ob es möglich ist, einen HL² Server zu kontaktieren und Informationen auszulesen. Da ich vor langer zeit über so ein Script Script im Web gestolpert bin, habe ich es mal rausgewühlt. Ein Script, was nur halb funktionierte und kaum eine Funktionierende Funktion hatte. Nach und nach haben wir beide noch Funktionen hinzugefügt, geändert und verbessert.

Hier mal ein Beispiel wie das Ergebnis ausschaut: http://www.server.hsfighter.net

Zur Funktion

Um nur die Informationen zu bekommen (z.B. Servername, Map usw)

  • Verbindung zum Server herstellen
  • Einen Wert zum Server schicken
  • Die Daten die der Server zurückschickt auffangen und auswerten
  • Daten ins Array laden

Um Player vom Server ab zu fragen (z.B. Playername, Frags usw.), muss man eine Art „Ticket“ anfordern. Dieses Ticket ist # eine begrenzte Zeit gültig und ermöglicht es die Informationen zu bekommen.

  • Verbindung zum Server herstellen
  • Ein Ticket anfordern (Challenge)
  • Einen Wert zum Server schicken mit angehängten Ticket (das was wir vorher abgefragt haben)
  • Die Daten die der Server zurückschickt auffangen und auswerten
  • Daten ins Array laden

Serversettings sind ähnlich wie die Player, auch mit einem Ticket

OK, soweit zum Prinzip.

Code:
Ich gehe mal davon aus das PHP Kentnisse vorhanden sind.
Wir wollen das ganze in einer Klasse zusammenfassen, damit man es später leicht anpassen und für verschiedene Server verwenden kann.1)

class HLServerAbfrage {
 
}       

Variablen deklarieren

Jetzt werden wir erst mal unsere wichtigsten Variablen deklarieren und einige schon mit Werten füllen da wir diese Werte dann nachher zu unserem Gameserver Senden:

var $server_address;
var $ip;
var $port;
var $fp;
var $challenge;
var $serverinfo;
var $playerlist;
var $cvarlist;
 
var $A2S_SERVERQUERY_GETCHALLENGE = "\x55\xFF\xFF\xFF\xFF"; // challenge
var $A2S_INFO = "TSource Engine Query\x00"; // info
var $A2S_PLAYER = "\x55"; // player
var $A2S_RULES = "\x56"; // rules                                   

Jetzt kommen wir zu den Funktionen die wir brauchen:

Funktion um IP und PORT aus der Serveradressen Variable zu trennen

function hlserver($server_address = 0) {
  list($this->ip, $this->port) = explode(":", $server_address);
}      

Funktion um eine Verbindung zum Server aufzubauen:

function connect() {
  $this->fp = fsockopen("udp://".$this->ip, $this->port, $errno, $errstr, 3);
  if (!$this->fp) {
    $Fehler = 1;
  }
}      

Damit stellen wir eine und Verbindung zu unserem Server her und speichern sie in der Variablen “$this→fp“. Die Funktion fsockopen brauch dazu die beiden Variablen mit der IP und dem PORT.

Funktion um mit dem Server zu kommunizieren

function send_strcmd($strcmd) {
  fwrite($this->fp, sprintf('%c%c%c%c%s%c', 0xFF, 0xFF, 0xFF, 0xFF, $strcmd, 0x00));
}      

Wir benutzten die Funktion fwrite. Wir brauchen uns mit dem Rest nicht auseinander setzten, nur die Variable “$strcmd ist noch interessant. Damit werden später die Daten übergeben, die wir zum Server schicken.

Funktion für die Auswertung

Jetzt brauchen wir Funktionen, die die Daten auswertet die uns der Server zurück schick.[/b] Man muss sich das folgendermaßen vorstellen:

Der Server schickt uns eine Kette mit Informationen, die alle hintereinander kommen. Die müssen wir an bestimmten stellen trennen, damit wir die Informationen unterteilen können.
Bsp. Server schickt uns das: „de_dust25161“
Da sind folgende Informationen enthalten: Map, Max Player, Player auf dem Server und Anzahl der Bots.
Diese müssen jetzt getrennt werden!

// 1 Byte vom Server holen
function get_byte() {
  return ord(fread($this->fp, 1));
}
 
// 1 Zeichen (1 Byte) vom Server holen
function get_char() {
  return fread($this->fp, 1);
}
 
// einen int16-Wert (2 Bytes) vom Server holen
function get_int16() {
  $unpacked = unpack('sint', fread($this->fp, 2));
  return $unpacked[int];
}
 
// einen int32-Wert (4 Bytes) vom Server holen
function get_int32() {
  $unpacked = unpack('iint', fread($this->fp, 4));
  return $unpacked[int];
}
 
// einen float32-Wert (4 Bytes) vom Server holen
function get_float32() {
  $unpacked = unpack('fint', fread($this->fp, 4));
  return $unpacked[int];
}
 
// einen String vom Server holen
function get_string() {
  while(($char = fread($this->fp, 1)) != chr(0)) {
    $str .= $char;
  }
  return $str;
}
 
// 4 bytes von der challenge holen
  function get_4(){
  return fread($this->fp, 4);
}      

Die Informationen die wir auswerten, sind von verschiedenen Datentypen. Mit den oben genannten Funktionen kann man sie auswerten. Darauf werde ich erst mal nicht genauer eingehen, ich denke das ist selbsterklärend.

Ticket anfordern

Nun brauchen wir eine Funktion, um ein Ticket anzufordern:

function challenge() {
  $this->connect();
  $this->send_strcmd($this->A2S_SERVERQUERY_GETCHALLENGE);
  $this->get_int32();
  $this->get_byte();
  $challenge = $this->get_4();
  fclose($this->fp);
  return $challenge;
 
}      

Wir benutzen unsere Verbindungsfunktionen und übergeben den Wert “W“ (\x57) den wir vorher bei der Deklaration in die Variable “A2S_SERVERQUERY_GETCHALLENGE“ geschrieben haben.
Wir haben HEX-Zeichen für das W genommen!
Also mit anderen Worten schicken wir ein “W“ (\x57) zum Server.
Darauf antwortet der Server mit einem 4byte großen Datenpaket.
Deswegen benutzen wir die Funktion “get_4“ die wir vorher geschrieben haben.
Zum Schluss trennen wir die Verbindung. Die Funktion gibt die Ticketnummer aus.

Informationen vom Server holen

Jetzt brauchen wir die Funktionen um den Server die Informationen zu entlocken, die wir wollen.
Fangen wir mit den Server Informationen an:

function infos() {
  $this->connect();
  $this->send_strcmd($this -> A2S_INFO);
  $this->get_int32();
  $this->get_byte();
  $this->serverinfo["network_version"] = $this->get_byte();
  $this->serverinfo["name"] = $this->get_string();
  $this->serverinfo["map"] = $this->get_string();
  $this->serverinfo["directory"] = $this->get_string();
  $this->serverinfo["discription"]= $this->get_string();
  $this->serverinfo["steam_id"] = $this->get_int16();
  $this->serverinfo["players"] = $this->get_byte();
  $this->serverinfo["maxplayers"] = $this->get_byte();
  $this->serverinfo["bot"] = $this->get_byte();
  $this->serverinfo["dedicated"] = $this->get_char();
  $this->serverinfo["os"] = $this->get_char();
  $this->serverinfo["password"] = $this->get_byte();
  $this->serverinfo["secure"] = $this->get_byte();
  $this->serverinfo["version"] = $this->get_string();
  fclose($this->fp);
  return $this->serverinfo;
}      

Wir schicken wieder einen Wert zum Server (TSource Engine Query) der in der Variable “A2S_INFO“ gespeichert ist.
Danach rufen wir unseren Auswertungsfunktionen auf, um die Daten die der Server schickt auszuwerten.

Serverinfo Array

Zum Schluss speichern wir das ganze im Serverinfo Array.

Jetzt noch eine Funktion um die Player, Frags usw… vom Server abfragen:

function players() {
  $challenge = $this->challenge();
  $this->connect();
  $this->send_strcmd($this->A2S_PLAYER.$challenge);
  $this->get_int32();
  $this->get_byte();
 
  $playercount = $this->get_byte();
 
  for($i=1; $i <= $playercount; $i++) {
    $this->playerlist["index"][$i] = $this->get_byte();
    $this->playerlist["name"][$i] = $this->get_string();
    $this->playerlist["frags"][$i] = $this->get_int32();
    $this->playerlist["time"][$i] = date('H:i:s', round($this->get_float32(), 0)+82800);
  }
  fclose($this->fp);
  return $this->playerlist;
}      

Funktion zum auslesen der Serverregeln

Wir machen das wieder so wie bei den Infos, nun hängen wir allerdings noch die Ticketnummer (challenge) an und rufen die Funktion auf um sie zu bekommen.

function cvars() {
  $challenge = $this->challenge();
  $this->connect();
  $this->send_strcmd($this->A2S_RULES.$challenge);
  $this->get_int32();
  $this->get_byte();
 
  $cvarcount = $this->get_int16();
 
  for($i=1; $i <= $cvarcount; $i++) {
    $this->cvarlist[$this->get_string()] = $this->get_string();
  }
  fclose($this->fp);
  return $this->cvarlist;
}      

Das ist das Gleiche wie bei den Playern, nur die Auswertfunktionen sind anders.

Das war es auch schon. :-)

Zusammenfassung der Klasse

<?php
  class HLServerAbfrage {
 
      var $server_address;
      var $ip;
      var $port;
      var $fp;
      var $challenge;
      var $serverinfo;
      var $playerlist;
      var $cvarlist;
 
 
      var $A2S_SERVERQUERY_GETCHALLENGE = "\x55\xFF\xFF\xFF\xFF"; // challenge 
      var $A2S_INFO = "TSource Engine Query\x00"; // info
      var $A2S_PLAYER = "\x55"; // player
      var $A2S_RULES = "\x56"; // rules
 
      // IP und PORT trennen
      function hlserver($server_address = 0) {
            list($this->ip, $this->port) = explode(":", $server_address);
      }
 
      // Verbindung zum Server aufbauen
      function connect() {
        $this->fp = fsockopen("udp://".$this->ip, $this->port, $errno, $errstr, 3);
        if (!$this->fp) {
          $Fehler = 1;
        }
      }
 
       // String-Command" senden
      function send_strcmd($strcmd) {
        fwrite($this->fp, sprintf('%c%c%c%c%s%c', 0xFF, 0xFF, 0xFF, 0xFF, $strcmd, 0x00));
      }
 
      // 1 Byte vom Server holen
      function get_byte() {
        return ord(fread($this->fp, 1));
      }
 
      // 1 Zeichen (1 Byte) vom Server holen
      function get_char() {
        return fread($this->fp, 1);
      }
 
      // einen int16-Wert (2 Bytes) vom Server holen
      function get_int16() {
        $unpacked = unpack('sint', fread($this->fp, 2));
        return $unpacked[int];
      }
 
      // einen int32-Wert (4 Bytes) vom Server holen
      function get_int32() {
        $unpacked = unpack('iint', fread($this->fp, 4));
            return $unpacked[int];
      }
 
      // einen float32-Wert (4 Bytes) vom Server holen
      function get_float32() {
        $unpacked = unpack('fint', fread($this->fp, 4));
        return $unpacked[int];
      }
 
      // einen String vom Server holen
      function get_string() {
        while(($char = fread($this->fp, 1)) != chr(0)) {
          $str .= $char;
        }
        return $str;
      }
      // 4 bytes von der challenge holen
 
      function get_4()
      {
       return fread($this->fp, 4);
      }
 
      // Challenger vom Server holen
      function challenge() {
        $this->connect();
        $this->send_strcmd($this->A2S_SERVERQUERY_GETCHALLENGE);
        $this->get_int32();
        $this->get_byte();
        $challenge = $this->get_4();
        fclose($this->fp);
        return $challenge;
 
      }
 
      // Infos vom Server holen
      function infos() {
        $this->connect();
        $this->send_strcmd($this -> A2S_INFO);
        $this->get_int32();
        $this->get_byte();
 
        $this->serverinfo["network_version"] = $this->get_byte();
        $this->serverinfo["name"] = $this->get_string();
        $this->serverinfo["map"] = $this->get_string();
        $this->serverinfo["directory"] = $this->get_string();
        $this->serverinfo["discription"]= $this->get_string();
        $this->serverinfo["steam_id"] = $this->get_int16();
        $this->serverinfo["players"] = $this->get_byte();
        $this->serverinfo["maxplayers"] = $this->get_byte();
        $this->serverinfo["bot"] = $this->get_byte();
        $this->serverinfo["dedicated"] = $this->get_char();
        $this->serverinfo["os"] = $this->get_char();
        $this->serverinfo["password"] = $this->get_byte();
        $this->serverinfo["secure"] = $this->get_byte();
        $this->serverinfo["version"] = $this->get_string();
 
        fclose($this->fp);
        return $this->serverinfo;
      }
 
      // Player-Liste vom Server holen
      function players() {
        $challenge = $this->challenge();
        $this->connect();
        $this->send_strcmd($this->A2S_PLAYER.$challenge);
        $this->get_int32();
        $this->get_byte();
 
        $playercount = $this->get_byte();
 
        for($i=1; $i <= $playercount; $i++) {
          $this->playerlist["index"][$i] = $this->get_byte();
          $this->playerlist["name"][$i] = $this->get_string();
          $this->playerlist["frags"][$i] = $this->get_int32();
          $this->playerlist["time"][$i] = date('H:i:s', round($this->get_float32(), 0)+82800);
        }
        fclose($this->fp);
        return $this->playerlist;
      }
 
      // Rules-Liste (CVARs) vom Server holen
      function cvars() {
         $challenge = $this->challenge();
         $this->connect();
         $this->send_strcmd($this->A2S_RULES.$challenge);
         $this->get_int32();
         $this->get_byte();
 
          $cvarcount = $this->get_int16();
 
          for($i=1; $i <= $cvarcount; $i++) {
            $this->cvarlist[$this->get_string()] = $this->get_string();
          }
          fclose($this->fp);
          return $this->cvarlist;
      }
}
?>

Zugriff auf die Klasse

Nun muss nur noch auf die Klasse zugegriffen werden:

<?php
// Server Angaben
 
$ip = '85.131.170.95';
$port = '27015';
$mappic_path = 'http://meine-hp.de/mappics/css/';
 
// Klasse Laden
include ("serverstatus.class.php");
 
// Verbindung zum Server herstellen
 
$verbindung = new HLServerAbfrage;
$verbindung -> hlserver($ip.':'.$port);
 
// Serverinfos abfragen
 
$infos = $verbindung->infos();
 
$infos["os"] = str_replace("l", 'Linux', $infos["os"]);
$infos["os"] = str_replace("w", 'Windows', $infos["os"]);
$infos["password"] = str_replace("1", 'Yes', $infos["password"]);
$infos["password"] = str_replace("0", 'No', $infos["password"]);
$infos["secure"] = str_replace("1", 'VAC2', $infos["secure"]);
$infos["secure"] = str_replace("0", 'None', $infos["secure"]);
$infos["dedicated"] = str_replace("l", 'Listen', $infos["dedicated"]);
$infos["dedicated"] = str_replace("d", 'Dedicated', $infos["dedicated"]);
$infos["dedicated"] = str_replace("p", 'SourceTV', $infos["dedicated"]);
 
// Serverinfos abfragen
 
echo 'Name: '.$infos["name"].'<br>';
echo 'IP: '.$ip.'<br>';
echo 'PORT: '.$port.'<br>';
echo 'OS: '.$infos["os"].'<br>';
echo 'Dedicated: '.$infos["dedicated"].'<br>';
echo 'Version: '.$infos["version"].'<br>';
echo 'Discription: '.$infos["discription"].'<br>';
echo 'Map: '.$infos["map"].'<br>';
echo 'Players: '.$infos["players"].'<br>';
echo 'Max. Players: '.$infos["maxplayers"].'<br>';
echo 'Bots: '.$infos["bot"].'<br>';
echo 'VAC: '.$infos["secure"].'<br>';
echo 'Password: '.$infos["password"].'<br>';
echo '<IMG src="'.$mappic_path.$infos["map"].'.jpg" border=0><br>';
 
// Playerinfos abfragen
 
$players = $verbindung->players();
if (isSet($players)){
  arsort ($players["frags"]); // Player nach Frags sortieren
  $x = 0;
  foreach($players["frags"] as $key => $value) {
    $x++;
    Echo '#'.$x.' -- '; // Nummer
    Echo ''.$players["index"][$key].' -- '; // Index
    Echo ''.$players["name"][$key].' -- ';  // Name
    Echo ''.$players["frags"][$key].' -- '; // Frags
    Echo ''.$players["time"][$key].' -- '; // Zeit
    Echo '<br>';
  }
}else{
  echo "server leer";
} 
 
// Cvarinfos abfragen
 
$nextmap = 'Unknow!!!';
$cvars = $verbindung->cvars();
// Schleife um das Array aus zu lesen
foreach($cvars as $key => $value) {
  // Prüfung des Key's.
  if ($key == 'amx_nextmap')$nextmap = $value;
  if ($key == 'cm_nextmap')$nextmap = $value;
  if ($key == 'am_nextmap')$nextmap = $value;
  if ($key == 'mp_nextmap')$nextmap = $value;
  if ($key == 'mani_nextmap')$nextmap = $value;
  // Anzeige aller Cvars!!!
  Echo ''.$key.' = '.$value.'<br>';
}
Echo '<br>Next map is: <b>'.$nextmap.'</b><br>';
?>

Danksagung und Autor

Danke auch an Chrisber. Ohne ihn hätte ich dieses Tut nicht schrieben können.

Auf dieses Tutorial darf frei Verlinkt werden.
So lange es hier im Wiki auf sourceserver.info verbleibt.
Wenn ihr das Tutorial oder Teile davon wo anders verwenden möchtet, bitte eine PM an HSFighter!

1) z.B. nur um die Serverinfo´s abfragen und auf den Rest zu verzichten
tutorial/server/php_querry_script.1327428497.txt.bz2 · Zuletzt geändert: 2012/01/24 19:08 von HSFighter