Code PHP :
Pour copier le contenu exact du code,
Veuillez citer le message
<?php
-
/*
* gsQuery - Querys GameServer protocol compatible Gameservers (and more)
* Copyright (c) 2003 Jeremias Reith <
>
* http://gsquery.terragate.net
*
* This file is part of the gsQuery library.
*
* The gsQuery library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* The gsQuery library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the gsQuery library; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
-
global $GSQUERYDEFINED;
-
// to be sure that it is only included once (does this really help?)
if(!$GSQUERYDEFINED) {
$GSQUERYDEFINED=TRUE;
-
class gsQuery
{
// version
var $version;
// this must be known
var $address;
var $queryport;
// status info
var $gamename;
var $hostport;
var $gameversion;
var $servertitle;
var $hostport;
var $mapname;
var $maptitle;
var $gametype;
var $numplayers;
var $maxplayers;
var $password;
var $nextmap;
// players
var $players;
// list of available playerinfos
// name=1, score=1, ping=1 in most cases
var $playerkeys;
-
// contains the names of the teams (if available)
var $playerteams;
-
// serverrules
var $rules;
// Holds an errormessage if something goes wrong (not implemented yet)
var $errstr;
// Holds debug infos
var $debug;
// constructor
function gsQuery($address, $queryport)
{
$this->version = "0.0.5";
$this->address = $address;
$this->queryport = $queryport;
$this->gamename = "";
$this->hostport = 0;
$this->gameversion = "";
$this->servertitle = "";
$this->hostport = "";
$this->mapname = "";
$this->maptitle = "";
$this->gametype = "";
$this->numplayers = 0;
$this->maxplayers = 0;
$this->password = -1; // -1 stands for unknown
// 0 for no password
// 1 for password
$this->nextmap="";
-
$this->players = array();
$this->playerkeys = array();
$this->playerteams = array();
$this->rules = array();
$this->debug = array();
$this->errstr="";
}
// use this function to query the server
// returns FALSE on failure
function query_server($protocol="gsqp",$getPlayers=TRUE,$getRules=TRUE)
{
$this->playerkeys=array();
$this->debug=array();
$this->password=-1;
switch($protocol) {
case "gsqp":
return $this->_query_gsqp_server($getPlayers,$getRules);
case "hlife":
return $this->_query_hlife_server($getPlayers,$getRules);
case "q3a":
return $this->_query_q3a_server($getPlayers);
default:
return FALSE;
}
}
// sorts the players
function sortPlayers($players, $sortkey="name")
{
switch($sortkey) {
default:
case "name":
uasort($players, array("gsQuery", "_sortbyName"));
break;
case "score":
uasort($players, array("gsQuery", "_sortbyScore"));
break;
case "frags":
uasort($players, array("gsQuery", "_sortbyFrags"));
break;
case "deaths":
uasort($players, array("gsQuery", "_sortbyDeaths"));
break;
case "honor":
uasort($players, array("gsQuery", "_sortbyHonor"));
break;
case "time":
uasort($players, array("gsQuery", "_sortbyTime"));
break;
}
return ($players);
}
-
// private member functions
// this works for most games
function _query_gsqp_server($getPlayers=TRUE,$getRules=TRUE)
{
$command="basicinfo";
if(!($result=$this->_sendCommand($this->address,$this->queryport,$command, "final"))) {
// trying it more generic (America's Army)
if(!($result=$this->_sendCommand($this->address,$this->queryport,"status"))) {
return FALSE;
}
}
$temp=explode("",$result);
for($i=1;$i <count($temp);$i++) {
$rawdata[$temp[$i]]=$temp[++$i];
}
// Whatever we got it is not useful for us
if(!$rawdata["gamename"]) {
return FALSE;
}
-
$this->gamename = $rawdata["gamename"];
$this->hostport = $rawdata["hostport"];
$this->gameversion = $rawdata["gamever"];
$this->servertitle = $rawdata["hostname"];
$this->maptitle = $rawdata["maptitle"];
$this->mapname = $rawdata["mapname"];
$this->gametype = $rawdata["gametype"];
$this->numplayers = $rawdata["numplayers"];
$this->maxplayers = $rawdata["maxplayers"];
if($rawdata["password"]==0 || $rawdata["password"]==1) {
$this->password=$rawdata["password"];
}
// get players
if($this->numplayers && $getPlayers) {
$cmd="players";
if(!($response=$this->_sendCommand($this->address,$this->queryport,$cmd))) {
return FALSE;
}
$temp=explode("", $this->_sortByQueryId($response));
$this->playerkeys["name"]=TRUE;
for($i=1;$i<count($temp);$i++) {
list($var, $playerid)=explode("_", $temp[$i]);
switch($var) {
case "player":
$players[$playerid]["name"]=$temp[++$i];
break;
case "teamname":
$this->playerteams[$playerid]=$temp[++$i];
break;
default:
$players[$playerid][$var]=$temp[++$i];
$this->playerkeys[$var]=TRUE;
}
}
$this->players=$players;
}
-
// get rules
if($getRules) {
$cmd="rules";
if(!($response=$this->_sendCommand($this->address,$this->queryport,$cmd))) {
return FALSE;
}
$temp=explode("",$response);
for($i=1;$i<count($temp);$i++) {
if($temp[$i]!="queryid" && $temp[$i]!="final" && $temp[$i]!="password") {
$this->rules[$temp[$i]]=$temp[++$i];
} else {
if($temp[$i++]=="password") {
$this->password=$temp[$i];
}
}
}
}
return TRUE;
}
// sorts gsqp data
// sometimes a packet get lost but we do not have the time to rerequest it
function _sortByQueryId($data)
{
$data=preg_replace("/final/", "", $data);
$exploded_data=explode("queryid", $data);
for($i=0;$i<count($exploded_data)-1;$i++) {
preg_match("/^d+.(d+)/", $exploded_data[$i+1], $id);
$sorted_data[$id[1]]=$exploded_data[$i];
$exploded_data[$i+1]=substr($exploded_data[$i+1],strlen($id[0]-1),strlen($exploded_data[$i+1]));
}
ksort($sorted_data);
foreach($sorted_data as $key => $value) {
$result.=$value;
}
return($result);
}
// code is very ugly (does anyone have the protocol specs?)
// There are a lot of bugs in it
function _query_hlife_server($getPlayers=TRUE,$getRules=TRUE)
{
$command="xFFxFFxFFxFFx54x53x6Fx75x72x63x65x20x45x6Ex67x69x6Ex65x20x51x75x65x72x79x00";
if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
return FALSE;
}
$exploded_data = explode("x00", $result);
list($ip, $this->hostport) = explode(":", $exploded_data[0]);
$this->gamename = "hlife_" . $exploded_data[3];
$this->gametype = $exploded_data[3];
$this->servertitle = $exploded_data[1];
$this->mapname = $exploded_data[2];
$this->numplayers = ord(substr($exploded_data[5], 0, 1));
if($this->numplayers) {
$this->maxplayers = ord(substr($exploded_data[5], 1, 1));
}
else {
$this->maxplayers = ord(substr($exploded_data[6], 0, 1));
}
$this->gameversion = "";
$this->maptitle = "";
// get players
if($this->numplayers && $getPlayers) {
$command="xFFxFFxFFxFFx57";
if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
return FALSE;
}
$exploded_data = explode("x00", substr($result, 4, strlen($result)));
$players[0]["name"]=substr($exploded_data[0], 3, strlen($exploded_data[0]));
$players[0]["score"]=hexdec(bin2hex(substr($exploded_data[1],0,1)));
// correction to signed short (is there a better way in PHP to do that?)
if($players[0]["score"]>128) {
$players[0]["score"]-=256;
}
for($i=1;$i<count($exploded_data);$i++) {
if(strlen($exploded_data[$i])>4) {
$players[$i]["name"]=substr($exploded_data[$i], 5, strlen($exploded_data[$i]));
$players[$i]["score"]=hexdec(bin2hex(substr($exploded_data[$i+1],0,1)));
}
}
$this->playerkeys["name"]=TRUE;
$this->playerkeys["score"]=TRUE;
$this->players=$players;
}
// get rules
$command="xFFxFFxFFxFFx57";
if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
return FALSE;
}
$exploded_data = explode("x00", $result);
$this->password = -1;
for($i=1;$i<count($exploded_data);$i++) {
if($exploded_data[$i]!="sv_password" && $exploded_data[$i]!="") {
$this->rules[$exploded_data[$i]]=$exploded_data[++$i];
} else {
if($exploded_data[$i++]=="sv_password") {
$this->password=$exploded_data[$i];
}
}
}
return TRUE;
}
function _query_q3a_server($getPlayers=TRUE)
{
$command="xFFxFFxFFxFFx02getstatusx0ax00";
if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
return FALSE;
}
$temp=explode("x0a",$result);
$rawdata=explode("",substr($temp[1],1,strlen($temp[1])));
-
// get rules and basic infos
for($i=0;$i< count($rawdata);$i++) {
switch ($rawdata[$i++]) {
case "g_gametypestring":
$this->gametype=$rawdata[$i];
break;
case "gamename":
$this->gametype=$rawdata[$i];
$this->gamename="q3a_" . $rawdata[$i];
break;
case "version":
$this->gameversion=$rawdata[$i];
break;
case "sv_hostname":
$this->servertitle=$rawdata[$i];
break;
case "mapname":
$this->mapname=$rawdata[$i];
break;
case "g_needpass":
$this->password=$rawdata[$i];
break;
case "sv_maplist":
$maps=explode($rawdata[$i], " ");
break;
default:
$this->rules[$rawdata[$i-1]] = $rawdata[$i];
}
}
-
// for MoHAA
if(!$this->gamename && eregi("Medal of Honor", $this->gameversion)) {
$this->gamename="mohaa";
}
-
if($maps) {
$i=0;
while($this->mapname!=$maps[$i++] && $i<count($maps));
if($i==count($maps)-1) {
$this->nextmap=$maps[0];
}
else {
$this->nextmap=$maps[$i];
}
}
-
//for MoHAA
$this->mapname=preg_replace("/.*//", "", $this->mapname);
-
$this->hostport = $this->queryport;
$this->maxplayers = $this->rules["sv_maxclients"]-$this->rules["sv_privateClients"];
-
//get playerdata and strip colortags away
$temp=substr($result,strlen($temp[0])+strlen($temp[1])+1,strlen($result));
$temp=preg_replace("/^[^"]/","",$temp);
$allplayers=explode("n", $temp);
$this->numplayers=count($allplayers)-2;
-
// get players
if(count($allplayers)-2 && $getPlayers) {
for($i=1;$i< count($allplayers)-1;$i++) {
if(preg_match("/(d+)[^0-9](d+)[^0-9]"(.*)"/", $allplayers[$i], $curplayer)) {
$players[$i-1]["name"]=$curplayer[3];
$players[$i-1]["score"]=$curplayer[1];
$players[$i-1]["ping"]=$curplayer[2];
}
else {
if(preg_match("/(d+)."(.*)"/", $allplayers[$i], $curplayer)) {
$players[$i-1]["name"]=$curplayer[2];
$players[$i-1]["ping"]=$curplayer[1];
$pingOnly=TRUE; // for MoHAA
}
else {
$this->errstr="Could not extract player infos!";
echo "Unmatched Player: " . $allplayers[$i] . "<br>";
return FALSE;
}
}
}
if(!$pingOnly) {
$this->playerkeys["name"]=TRUE;
$this->playerkeys["score"]=TRUE;
$this->playerkeys["ping"]=TRUE;
}
else {
$this->playerkeys["name"]=TRUE;
$this->playerkeys["ping"]=TRUE;
}
$this->players=$players;
}
return TRUE;
}
-
-
// better idea?
function _sortbyName($a, $b)
{
return(strcasecmp($a["name"], $b["name"]));
}
-
function _sortbyScore($a, $b)
{
if($a["score"]==$b["score"]) { return 0; }
elseif($a["score"]<$b["score"]) { return 1; }
else { return -1; }
}
-
function _sortbyFrags($a, $b)
{
if($a["frags"]==$b["frags"]) { return 0; }
elseif($a["frags"]<$b["frags"]) { return 1; }
else { return -1; }
}
-
function _sortbyDeaths($a, $b)
{
if($a["deaths"]==$b["deaths"]) { return 0; }
elseif($a["deaths"]<$b["deaths"]) { return 1; }
else { return -1; }
}
-
function _sortbyHonor($a, $b)
{
if($a["honor"]==$b["honor"]) { return 0; }
elseif($a["honor"]<$b["honor"]) { return 1; }
else { return -1; }
}
-
function _sortbyTime($a, $b)
{
if($a["time"]==$b["time"]) { return 0; }
elseif($a["time"]<$b["time"]) { return 1; }
else { return -1; }
}
// this function is far away from being finished
// TODO: Make sure that packets are recieved in the correct order
function _sendCommand($address,$port,$command)
{
if (!$socket=@fsockopen("udp://".$address,$port)) {
$this->debug[$command]="Couldn't reach server";
return FALSE;
} else {
socket_set_blocking($socket,true);
//waiting half a second max
socket_set_timeout($socket,0,500000);
// send command
if(fwrite($socket,$command,strlen($command))==-1) {
fclose($socket);
$this->debug[$command]="Unable to write on open socket!";
return FALSE;
}
-
$result="";
do {
$result .= fread($socket,128);
$socketstatus = socket_get_status($socket);
} while ($socketstatus["unread_bytes"]);
fclose($socket);
if(empty($result)) {
$this->debug[$command]="Empty reply recieved!";
return FALSE;
}
$this->debug[$command]=$result;
return $result;
}
}
}
}
?>
-