Location: PHPKode > projects > ClanLite > clanlite/service/gsquery/hlife.php
<?php

/*
 *  gsQuery - Querys game servers
 *  Copyright (c) 2002-2004 Jeremias Reith <hide@address.com>
 *  http://www.gsquery.org
 *
 *  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
 *
 */

/**
 * @brief Querys a halflife server
 * @author Jeremias Reith (hide@address.com)
 * @version $Rev: 195 $
 *
 * Code is very ugly at the moment.
 * Does anyone have the protocol specs?<br>
 *
 * This class works with Halflife only.
 */
class hlife extends gsQuery
{
  var $playerFormat = '/sscore/x2/ftime';

  function rcon_query_server($command, $rcon_pwd)
  {
    $get_challenge="\xFF\xFF\xFF\xFFchallenge rcon\n";
    if(!($challenge_rcon=$this->_sendCommand($this->address,$this->queryport,$get_challenge))) {
      $this->debug['Command send ' . $command]='No challenge rcon received';
      return FALSE;
    }
    if (!ereg("challenge rcon ([0-9]+)", $challenge_rcon)) {
      $this->debug['Command send ' . $command]='No valid challenge rcon received';
      return FALSE;
    }
    $challenge_rcon=substr($challenge_rcon, 19,10);
    $command="\xFF\xFF\xFF\xFFrcon \"".$challenge_rcon."\" ".$rcon_pwd.' '.$command."\n";
    if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
      $this->debug['Command send ' . $command]='No reply received';
      return FALSE;
    } else {
      return substr($result, 5);
    }
  }
 
  function getGameJoinerURI()
  {
    return 'gamejoin://hlife@'. $this->address .':'. $this->hostport .'/'. $this->gametype;
  }

  function query_server($getPlayers=TRUE,$getRules=TRUE)
  {     
    // flushing old data if necessary
    if($this->online) {
      $this->_init();
    }
    
    $command="\xFF\xFF\xFF\xFFinfostring\n";
    if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
      return FALSE;
    }
    
    
    $exploded_data = explode("\\", $result);
    for($i=1;$i<count($exploded_data);$i++) {
      switch($exploded_data[$i++]) {
      case 'address':
	if ($exploded_data[$i] == 'loopback') {
	  $this->hostport = $this->queryport;
	} else {
	  list($ip, $this->hostport) = explode(':', $exploded_data[$i]);
	}
	break;
      case 'hostname':
	$this->servertitle = $exploded_data[$i];
	break;
      case 'map':
	$this->mapname = $exploded_data[$i];
	break;
      case 'players':
	$this->numplayers = $exploded_data[$i];
	break;
      case 'max':
	$this->maxplayers = $exploded_data[$i];
	break;
      case 'protocol':
	$this->gameversion = ($exploded_data[$i] == 47)? '1.6' : '1.5';
	break;
      case 'gamedir':
	$this->gamename = 'hlife_' . $exploded_data[$i];
	$this->gametype = $exploded_data[$i];
	break;
      }
    }
    
    // get players
    if($this->numplayers && $getPlayers) {
      $command="\xFF\xFF\xFF\xFFplayers\n";
      if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
	return FALSE;
      }
      
      $this->_processPlayers($result, $this->playerFormat, 8);
        
      $this->playerkeys['name']=TRUE;
      $this->playerkeys['score']=TRUE;
      $this->playerkeys['time']=TRUE;

    }
    $this->gametype = ($this->gametype == 'cstrike') ? $this->gametype.' '.$this->gameversion : $this->gametype;
    
    // get rules
    $command="\xFF\xFF\xFF\xFFrules\n";
    if(!($result=$this->_sendCommand($this->address,$this->queryport,$command))) {
      return FALSE;
    }
    
    // rules can be in multiple packets, we have to sort it out
    // First packet has a 16 byte header, subsequent packet has an 8 byte header.
    $str="/\xFE\xFF\xFF\xFF/";// packet signature (both first and second start with this)
    
    $block=preg_split($str,$result,-1,PREG_SPLIT_NO_EMPTY);
    
    $str="/\xFF\xFF\xFF\xFF/"; // first packet signature (only first packet matches this)
    
    if(!empty($block[0]) && !empty($block[1])) {
      if(preg_match($str, $block[0])) {
	$result = substr($block[0], 12, strlen($block[0])).substr($block[1], 5, strlen($block[1]));      
      } elseif(preg_match($str, $block[1])) {
	$result = substr($block[1], 12, strlen($block[1])).substr($block[0], 5, strlen($block[1])).substr($block[0], 5, strlen($block[0]));
      }
    } elseif (!empty($block[0])) {
      $result = substr($block[0], 5, strlen($block[0]));
    }


    $exploded_data = explode("\x00", $result);
    $this->password = -1;

    for($i=0;$i<count($exploded_data);$i++) {
      switch($exploded_data[$i++]) {
      case 'sv_password':
	$this->password=$exploded_data[$i];
	break;
      case 'amx_nextmap':
	$this->nextmap=$exploded_data[$i];
	break;
      case 'cm_nextmap':
	$this->nextmap=$exploded_data[$i];
	break;
      default:
	if(isset($exploded_data[$i-1]) && isset($exploded_data[$i])) {
	  $this->rules[$exploded_data[$i-1]]=$exploded_data[$i];
	}
      }
    }
    $this->online = TRUE;
    return TRUE;
  }

  function getDebugDumps($html=FALSE, $dumper=NULL) {
    require_once(GSQUERY_DIR . 'requires/HexDumper.class.php');    

    if(!isset($dumper)) {
      $dumper = new HexDumper();
      $dumper->setDelimiter(0x00);
      $dumper->setEndOfHeader(0x04);
    }

    return parent::getDebugDumps($html, $dumper);
  }


  function _processPlayers($data, $format, $formatLength) 
  {
    $len = strlen($data);

    $posNextPlayer=$len;

    for($i=6;$i<$len;$i=$endPlayerName+$formatLength+1) { 
      // finding end of player name
      $endPlayerName = strpos($data, "\x00", ++$i);
      if($endPlayerName == FALSE) { return FALSE; } // abort on bogus data
      // unpacking player's score and time
      $curPlayer = unpack('@'.($endPlayerName+1).$format, $data);
      // format time
      if(array_key_exists('time', $curPlayer)) {
	$curPlayer['time'] = adodb_date('H:i:s', mktime(0, 0, $curPlayer['time']));
      }
      // extract player name
      $curPlayer['name'] = substr($data, $i, $endPlayerName-$i);
      // add player to the list of players
      $this->players[] = $curPlayer; 
    }
  }

 
}

?>
Return current item: ClanLite