#native_company# #native_desc#
#native_cta#

gQuery// – Game Server Query

By Jon Grotar Borgrsson
on October 19, 2001

Version: 0.4.0 class

Type: Class

Category: Games

License: GNU General Public License

Description: gQuery// is a game server query tool for PHP. It works in many ways
in the same manner as qStat but by using PHP instead of C it allows
much easyer intergration into the web along with overall flexibility.
It supports Quake2, Quake3, Half-Life and Unreal Tournament servers & more.
Visit the homepage at http://gdome.sourceforge.net/

<?php
/*
 +------------------------------------------------------------------------+
 | gQuery// version 0.4.0 beta                                            |
 | Copyright (c) 2001 Jon Gretar Borgthorsson. All rights reserved.       |
 +------------------------------------------------------------------------+
 | gQuery// is a part of the gDome// project. Find more information       |
 | about gDome// and related projects at http://gdome.sourcefoge.net/     |
 +------------------------------------------------------------------------+
 |                                                                        |
 | This program is free software; you can redistribute it and/or          |
 | modify it under the terms of the GNU General Public License version 2  |
 | as published by the Free Software Foundation                           |
 |                                                                        |
 | Software distributed under the License is distributed on an "AS IS"    |
 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.        |
 | See the GNU General Public License in License.html for more details.   |
 |                                                                        |
 +------------------------------------------------------------------------+
 | Website: http://gdome.sourceforge.net/                                 |
 +------------------------------------------------------------------------+
 | Authors:                                                               |
 |   Jon Gretar Borgthorsson <[email protected]>            |
 +------------------------------------------------------------------------+
*/

class gQuery
{

	// gQuery system variables 
	var $errors = array();	// Errors in the script
	var $fp;				// File pointer
	
	// The info you give the script
	var $cgametype;			// Type of game
	var $caddress;			// Connection address
	var $cport;				// Connection port
	
	// Responders
	var $serverinfo;		// Server info array
	var $playerinfo;		// Player info array
	var $systeminfo;		// System info array
	var $pcount = 0;		// Player count
		
	// Debug variables	
	var $DEBUG;				// DEBUG
	var $DEBUG2;			// DEBUG
	
	// Types of games
	var $gametypes = array(
		"HL" => "Half-Life",
		"HL2" => "Half-Life 2",
		"Q1" => "Quake",
		"Q2" => "Quake 2",
		"Q3" => "Quake: Arena",
		"QW" => "Quake World",
		"UT" => "Unreal Tournament",
		"TB2" => "Tribes 2"
	);
	
	// default ports for games
	var $defaultports = array(
		"HL" => "27015",
		"HL2" => "27015",
		"Q1" => "26000",
		"Q2" => "27910",
		"Q3" => "27960",
		"QW" => "27500",
		"UT" => "7777",
		"TB2" => "28000"
	);	
	
	// Activation commands
	var $commands = array(
		"HL" => array(
			"Serverinfo" => "detailsx00",
			"Playerlist" => "playersx00"
			),
		"Q2" => array(
			"Serverinfo" => "statusx00"
			),
		"Q3" => array(
			"Serverinfo" => "getstatusx00"
			),
		"UT" => array(
			"Serverinfo" => "status",
			"Playerlist" => "status",
			"TheEnd" => "final"
			)
	);

	
	/**
	* Function gameinfo(GAMETYPE=string, ADDRESS=string, [PORT=string])
	* The main function. Loads info on the server
	*/
	function gQuery($gametype ,$address, $port = "")
	{
		
		
		// Set the user provided connection settings
		$this->cgametype = $gametype;
		$this->caddress = $address;
		
		// Set the port to default if not defined
		if ($ports != "") 
		{
			
			$this->cport = $this->defaultports[$this->cgametype];
		}
		else
		{
			$this->cport = $port;
		}
		
		
		
		// Make the connection
		$this->fp = fsockopen("udp://" . $this->caddress, $this->cport, &$errno, &$errstr, 4); 

		if (!$this->fp) 
		{ 
			//unable to connect to host
			$this->serverinfo["name"] = "Error connecting to server";
			$this->serverinfo["address"] = $this->caddress;
			$this->addError("gQuery// had an error connecting to " . $this->caddress . ":" . $this->cport . "($errno - $errstr)");
		} 
		else 
		{		
			// Run the appropriate parser.
			eval("$this->parseServerstat" . $this->cgametype . "();");
			
			// Close the connection
			fclose($this->fp);
		}
		
		
		
		// Put up the list of variabes so ppl can see what info is available
		$this->systeminfo["gamevariables_count"] = 0;
		while (list($key) = each ($this->serverinfo)) 
		{
			if ($this->systeminfo["gamevariables_count"] != 0) $this->systeminfo["gamevariables"] .= ", ";
			$this->systeminfo["gamevariables"] .= $key;
			$this->systeminfo["gamevariables_count"]++;
		}
		
		$this->systeminfo["playervariables_count"] = 0;
		if ( $this->serverinfo["player"] != 0 )
		{
			while (list($key) = each ($this->playerinfo[0])) 
			{
				if ($this->systeminfo["playervariables_count"] != 0) $this->systeminfo["playervariables"] .= ", ";
				$this->systeminfo["playervariables"] .= $key;
				$this->systeminfo["playervariables_count"]++;
			}
		}
	}


	/**
	* Function about()
	* Displays info about this class.
	*/
	function about()
	{
		echo "<P><B>gQuery// gameserver info class. Version 0.4.0. n";
		echo "<BR>&copy; 2001 tar Borgrsson <I>([email protected])</I> n";
		echo "<BR>http://gdome.sourceforge.net/</B></P> n";
	}
	

	/**
	* Function debug()
	* Mostly just for debugging. Displays all kinds of info.
	*/
	function debug()
	{
		
		echo "<TABLE cellspacing='1' cellpadding='10'><TR><TD bgcolor='lightgreen'>";
		echo "<B>Debug Info:</B><BLOCKQUOTE>";
		echo "Address: " . $this->caddress . "<BR>n";
		echo "Port: " . $this->cport . "<BR>n";
		echo "Gametype: " . $this->cgametype . " <I>(" . $this->gametypes[$this->cgametype] . ")</I><BR>n";
		echo "Server Vars: " . $this->systeminfo["gamevariables_count"] . " vars <I>(" . $this->systeminfo["gamevariables"] . ")</I><BR>n";
		echo "Player Vars: " . $this->systeminfo["playervariables_count"] . " vars <I>(" . $this->systeminfo["playervariables"] . ")</I><BR>n";
		
		echo "<B>Errors:</B><OL>";
		for ($i = 0; $i <= sizeof($this->errors)-1; $i++) 
		{
			echo "<LI> " . $this->errors[$i] . "n";
		}
		echo "</OL>";
		
		echo "</BLOCKQUOTE>";
		
		echo "</TD></TR></TABLE>";
		
	}


	/**
	* Function addError(ERRORMSG=string, [HALT=bool])
	* Adds an error into the error array
	*/
	function addError($errorMsg, $halt = false)
	{
		// Check wether to stop the script or not		
		if (!$halt) 
		{
			$this->errors[count($this->errors)] = "WARNING: " . $errorMsg;
		}
		else
		{
			$this->errors[count($this->errors)] = "ERROR: " . $errorMsg;
			exit;
		}
	}	


	/**
	* Function parseServerstatHL()
	* Requests the server info from the server and parses it down
	*/
	function parseServerstatHL()
	{ 
		
		fwrite($this->fp,$this->commands[$this->cgametype]["Serverinfo"]);
		// Get rid of the header
		$header = fread($this->fp,5);
		  
		// Get server address and Port
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ($str == "x00") $do = false;
		}
		$this->serverinfo["address"] = $result;

		// Get server name
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ($str == "x00") $do = false;
		}
		$this->serverinfo["name"] = $result;			

		// Get server map
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ($str == "x00") $do = false;
		}
		$this->serverinfo["map"] = $result;					

		// Get server moddir
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ($str == "x00") $do = false;
		}
		$this->serverinfo["moddir"] = $result;		
		
		// Get server modname
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ($str == "x00") $do = false;
		}
		$this->serverinfo["modname"] = $result;		

		// Get number of players online
		$str = fread($this->fp,1);
		$this->serverinfo["players"] = ord($str);	

		// Get maximum number of online player
		$str = fread($this->fp,1);
		$this->serverinfo["maxplayers"] = ord($str);
		
		// Get server protocol
		$str = fread($this->fp,1);
		$this->serverinfo["protocol"] = ord($str);
		
		// Get Type of server
		$str = fread($this->fp,1);
		if ($str == "l") 
		{
			$this->serverinfo["typeofserver"] = "Listen";
		} 
		else if ($str == "d") 
		{
			$this->serverinfo["typeofserver"] = "Deticated";
		} 
		else
		{
			$this->serverinfo["typeofserver"] = "Unknown";
		}
		
		// Get server OS
		$str = fread($this->fp,1);
		if ($str == "l") 
		{
			$this->serverinfo["serveros"] = "Linux";
		} 
		else if ($str == "w") 
		{
			$this->serverinfo["serveros"] = "Win32";
		} 
		else
		{
			$this->serverinfo["serveros"] = "Unknown";
		}
		
		// Get wether server locked or not
		$str = fread($this->fp,1);
		if (ord($str) == 1) 
		{
			$this->serverinfo["locked"] = "true";
		} 
		else
		{
			$this->serverinfo["locked"] = "false";
		}

		// Get the unknown thing
		// Don't know what this variable stands for
		// Please send me info if you find out
		$str = fread($this->fp,1);
		$this->serverinfo["unknown"] = ord($str);		
		
		// Get the URL of the mod
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ($str == "x00") $do = false;
		}
		$this->serverinfo["modurl"] = $result;


		// Then get the Playerinfo	
		// We will trust that the connection is ok.
		fclose($this->fp);
		$this->fp = fsockopen("udp://" . $this->caddress, $this->cport, &$errno, &$errstr, 4); 				
		
		fwrite($this->fp,$this->commands[$this->cgametype]["Playerlist"]);
		//wait till we receive a header and store it (isn't used at the moment) 

		$str = fread($this->fp, 6); 
		$top = ord($str[5]);
		$this->pcount = $top;
		
		
		//socket_set_blocking($this->fp,0);
		for($usr = 0; $usr <= ($top-1); $usr++) 
		{ 
			$do = true; 
			
			// Get player id
			$str = fread($this->fp, 1); 
			$this->playerinfo[$usr]["index"] = ord($str); 
			 
			
			// Get player name
			$temp = "";
			while($do) 
			{ 
				$str = fread($this->fp,1); 
				if($str == "x00") 
				{ 
					$do = false; 
				} 
				else 
				{ 
						$temp .= $str; 
				} 
			} 
			$this->playerinfo[$usr]["name"] = $temp;
			
			// Get player frags
			$tot = 0; 
			$str = fread($this->fp, 1); 
			$tot += ord($str); 
			$str = fread($this->fp, 1); 
			$tot += ord($str) * 256; 
			$str = fread($this->fp, 1); 
			$tot += ord($str) * 65536; 
			$str = fread($this->fp, 1); 
			$tot += ord($str) * 16777216; 
			
			if($tot >= 16777216) 
			{ // ie is negative 
				$tot -= (4294967296); 
			} 
			$this->playerinfo[$usr]["frags"] = $tot; 


			// Get player time
			$bin = ''; 
			
			for($loop = 0; $loop <= 3; $loop++) 
			{ 
				$bin = str_pad(decbin(ord(fread($this->fp, 1))), 8, '0', STR_PAD_LEFT).$bin; 
			} 
			
			// get sign 
			$sign = bindec(substr($bin, 0, 1)); 
			
			// get exponent and adjust for special case and bias 
			$exponent = bindec(substr($bin, 1, 8)); 
			$exponent = ($exponent)? $exponent - 127 : $exponent; 
			
			if($exponent) { 
				// get the binary number of the mantissa 
				$int = bindec('1'.substr($bin, 9, $exponent)); 
				$dec = bindec(substr($bin, 9 + $exponent)); 
				
				$time = "$int.$dec"; 
				
				$this->playerinfo[$usr]["time"] = number_format($time / 60, 2); 
			} 
			else 
			{ 
				$this->playerinfo[$usr]["time"] = "0.0"; 
			} 
			
		} 
	}



	/**
	* Function parseServerstatQ2()
	* Requests the server info from the server and parses it down
	*/
	function parseServerstatQ2()
	{ 
		
		fwrite($this->fp,$this->commands[$this->cgametype]["Serverinfo"]);
		
		// Get rid of the header
		$header = fread($this->fp,10);
		
		// Read in the info from the server.
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ( $str ==  "n" ) $do = false;
		}
		$this->serverinfo["allinfo"] = $result;
			
		// Now recieve the player info
		socket_set_blocking($this->fp,0);
		

		echo $temp;
		
		$do = true;
		$i = 0;
		while ($do)
		{
			$temp = fgets($this->fp, 100);
			if ($temp == "") 
			{
				$do = false;
			}
			else
			{
				$tempplayers[$i] = $temp;
			}
			$i++;
		}
		$this->pcount = sizeof($tempplayers);
		$this->serverinfo["players"] = sizeof($tempplayers);
			
		
		// Now resolve the server info
		$temparray = explode ("", $this->serverinfo["allinfo"]);
		
		$i = 1;
		while($i <= sizeof($temparray)) 
		{ 
			$this->serverinfo[$temparray[$i++]] = $temparray[$i++];
		}			

		// Resolve the player info
		$i = 0;
		while($i <= $this->pcount-1)
		{
			$this->playerinfo[$i]["index"] = "$i";
			$temparray = explode (""", $tempplayers[$i]);
			
			$this->playerinfo[$i]["name"] = $temparray[1];
			
/*	
			$this->playerinfo[$i]["namecolored"] = $temparray[1];
			//Remove colortags
			$result = "";
			$b = 0;
			while($b <= strlen($temparray[1])) 
			{ 
				$str = substr($temparray[1], $b, 1); 
				if ($str == "^")
				{
					$b++;
				}
				else
				{
					$result .= $str;
				}
				$b++;
			}
			$this->playerinfo[$i]["name"] = $result;
*/
			$temparray2 = explode (" ", $temparray[0]);
			$this->playerinfo[$i]["frags"] = $temparray2[0];
			$this->playerinfo[$i]["ping"] = $temparray2[1];
			$i++;
		}


		
		// Standardize. For easyer info.
		$this->serverinfo["address"] = $this->caddress . ":" . $this->cport;
		$this->serverinfo["name"] = $this->serverinfo["hostname"];
		$this->serverinfo["moddir"] = $this->serverinfo["gamename"];
		$this->serverinfo["maxplayers"] = $this->serverinfo["maxclients"];
		$this->serverinfo["map"] = $this->serverinfo["mapname"];
		$this->serverinfo["typeofserver"] = "Unknown";
		$this->serverinfo["serveros"] = "Unknown";	
		if ($this->serverinfo["needpass"] == 1) { $this->serverinfo["locked"] = "true"; }
		else { $this->serverinfo["locked"] = "false"; }
				
	}

	/**
	* Function parseServerstatQ3()
	* Requests the server info from the server and parses it down
	*/
	function parseServerstatQ3()
	{

			fwrite($this->fp,$this->commands[$this->cgametype]["Serverinfo"]);

			$header=fgets($this->fp, 20); 
			//now we get the the servers statusdata in one line 
			$this->serverinfo["allinfo"] = fgets($this->fp, 1500);
			// echo $returnstring;  // DEBUG
			$temparray = explode ("", $this->serverinfo["allinfo"]);
			
			$i = 1;
			while($i <= sizeof($temparray)) 
			{ 
				$this->serverinfo[$temparray[$i++]] = $temparray[$i++];
			}

			
			// Now recieve the player info
			socket_set_blocking($this->fp,0);
			
			$do = true;
			$i = 0;
			while ($do)
			{
				$temp = fgets($this->fp, 100);
				if ($temp == "") 
				{
					$do = false;
				}
				else
				{
					$tempplayers[$i] = $temp;
				}
				$i++;
			}
			$this->pcount = sizeof($tempplayers);
			
			// Resolve the player info
			$i = 0;
			while($i <= $this->pcount-1)
			{
				$this->playerinfo[$i]["index"] = "$i";
				$temparray = explode (""", $tempplayers[$i]);
				$this->playerinfo[$i]["namecolored"] = $temparray[1];
				
				//Remove colortags
				$result = "";
				$b = 0;
				while($b <= strlen($temparray[1])) 
				{ 
					$str = substr($temparray[1], $b, 1); 
					if ($str == "^")
					{
						$b++;
					}
					else
					{
						$result .= $str;
					}
					$b++;
				}
				$this->playerinfo[$i]["name"] = $result;
				
				$temparray2 = explode (" ", $temparray[0]);
				$this->playerinfo[$i]["frags"] = $temparray2[0];
				$this->playerinfo[$i]["ping"] = $temparray2[1];
				$i++;
			}
			
			
			// Standardize. For easyer info.
			$this->serverinfo["address"] = $this->caddress . ":" . $this->cport;
			$this->serverinfo["name"] = $this->serverinfo["sv_hostname"];
			$this->serverinfo["moddir"] = $this->serverinfo["gamename"];
			$this->serverinfo["players"] = $this->serverinfo["sv_privateClients"];
			$this->serverinfo["maxplayers"] = $this->serverinfo["sv_maxclients"];
			$this->serverinfo["map"] = $this->serverinfo["mapname"];
			$this->serverinfo["typeofserver"] = "Unknown";
			$this->serverinfo["serveros"] = "Unknown";	
			$this->serverinfo["players"] = $this->pcount;
			if ($this->serverinfo["g_needpass"] == 1) { $this->serverinfo["locked"] = "true"; }
			else { $this->serverinfo["locked"] = "false"; }
	
			
	}




	/**
	* Function parseServerstatUT()
	* Requests the server info from the server and parses it down
	*/
	function parseServerstatUT()
	{

		fwrite($this->fp,$this->commands[$this->cgametype]["Serverinfo"]);
		  
		// Get server address and Port
		$do = true; 
		$result = "";
		while($do) 
		{ 
			$str = fread($this->fp,1);
			$result .= $str;
			if ( substr($result,strlen($result)-7) == $this->commands[$this->cgametype]["TheEnd"] ) $do = false;
		}
		$this->serverinfo["allinfo"] = $result;
		
		// Remove the final from the var list
		$this->serverinfo["allinfo"] = substr($this->serverinfo["allinfo"],0,strlen($this->serverinfo["allinfo"])-7);
		
		// Now resolve the info
		$temparray = explode ("", $this->serverinfo["allinfo"]);
		
		$i = 1;
		while($i <= sizeof($temparray)) 
		{ 
			$this->serverinfo[$temparray[$i++]] = $temparray[$i++];
		}
		
		// Standardize. For easyer info.
		$this->serverinfo["address"] = $this->caddress . ":" . $this->cport;
		$this->serverinfo["name"] = $this->serverinfo["hostname"];
		$this->serverinfo["modname"] = $this->serverinfo["gamename"];
		$this->serverinfo["players"] = $this->serverinfo["numplayers"];
		$this->serverinfo["map"] = strtolower($this->serverinfo["mapname"]);
		$this->serverinfo["version"] = $this->serverinfo["gamever"];
		$this->serverinfo["typeofserver"] = "Unknown";
		$this->serverinfo["serveros"] = "Unknown";	
		if ($this->serverinfo["password"] != "False") { $this->serverinfo["locked"] = "true"; }
		else { $this->serverinfo["locked"] = "false"; }				
		
		
		// Get those users fixed up
		for($i=0; $i <= $this->serverinfo["numplayers"]; $i++)
		{
			$this->playerinfo[$i]["index"] = $i;
			$this->playerinfo[$i]["name"] = $this->serverinfo["player_".$i];
			$this->playerinfo[$i]["ping"] = $this->serverinfo["ping_".$i];
			$this->playerinfo[$i]["frags"] = $this->serverinfo["frags_".$i];
			$this->playerinfo[$i]["team"] = $this->serverinfo["team_".$i];
			$this->playerinfo[$i]["skin"] = $this->serverinfo["skin_".$i];
			$this->playerinfo[$i]["mesh"] = $this->serverinfo["mesh_".$i];
			$this->playerinfo[$i]["face"] = $this->serverinfo["face_".$i];
			$this->playerinfo[$i]["ngsecret"] = $this->serverinfo["ngsecret_".$i];
			
			//Unset those variables so the will stop being in the way.
			unset( $this->serverinfo["player_".$i] );
			unset( $this->serverinfo["ping_".$i] );
			unset( $this->serverinfo["frags_".$i] );
			unset( $this->serverinfo["team_".$i] );
			unset( $this->serverinfo["skin_".$i] );
			unset( $this->serverinfo["mesh_".$i] );
			unset( $this->serverinfo["face_".$i] );
			unset( $this->serverinfo["ngsecret_".$i] );
			unset( $this->serverinfo[""] );
		}
	}	










}
?>