Here is a set of PHP 5 classes I am working on that interact with srcds-based servers. Acknowledgements to the existing scripts I based some code on.
stream_udp_response.php
stream_udp.php
source_query.php
test.php
stream_udp_response.php
Code:
<?php
class Stream_UDP_Response
{
public function __construct($response, $start_index)
{
$this->_response = $response;
$this->_current_index = $start_index;
}
public function GetByte($ord = false)
{
$byte = $this->_response[$this->_current_index];
if ($ord) {
$byte = ord($byte);
}
$this->_current_index++;
return $byte;
}
public function GetShort()
{
$short = null;
for ($i = 0; $i < 2; $i++) {
$short .= $this->GetByte();
}
$short = unpack('S', $short);
return $short[1];
}
public function GetLong()
{
$long = null;
for ($i = 0; $i < 4; $i++) {
$long .= $this->GetByte();
}
$long = unpack('L', $long);
return $long[1];
}
public function GetFloat()
{
$float = null;
for ($i = 0; $i < 4; $i++) {
$float .= $this->GetByte();
}
$float = unpack('f', $float);
return $float[1];
}
public function GetChar()
{
return chr($this->GetByte(true));
}
public function GetString()
{
$start_index = $this->_current_index;
for ($this->_current_index; ($this->_current_index < strlen($this->_response)) && ($this->_response[$this->_current_index] != chr(0)); $this->_current_index++);
$string = substr($this->_response, $start_index, $this->_current_index - $start_index);
$this->_current_index++;
return $string;
}
}
?>
stream_udp.php
Code:
<?php
require_once('stream_udp_response.php');
class Stream_UDP
{
const MAX_PACKET_SIZE = 1400;
private $_socket = null;
private function _Connect()
{
if (!$this->_socket = fsockopen("udp://".$this->_ip, $this->_port, $error_number, $error_string, 1)) {
die($error_string);
}
}
private function _Disconnect()
{
fclose($this->_socket);
}
public function __construct($ip, $port)
{
$this->_ip = $ip;
$this->_port = $port;
}
public function Send($buffer, $start_index = 0, $raw = false)
{
$this->_Connect();
fwrite($this->_socket, $buffer);
$response = fread($this->_socket, Stream_UDP::MAX_PACKET_SIZE);
$this->_Disconnect();
if ($raw) {
return $response;
} else {
return new Stream_UDP_Response($response, $start_index);
}
}
}
?>
source_query.php
Code:
<?php
require_once('stream_udp.php');
class Source_Query
{
const A2S_INFO = "\xFF\xFF\xFF\xFF\x54\x53\x6F\x75\x72\x63\x65\x20\x45\x6E\x67\x69\x6E\x65\x20\x51\x75\x65\x72\x79\x00";
const A2S_GETCHALLENGE = "\xFF\xFF\xFF\xFF\x57";
const A2S_PLAYER = "\xFF\xFF\xFF\xFF\x55";
const A2S_RULES = "\xFF\xFF\xFF\xFF\x56";
private $_socket = null;
private function _GetDedicated($response)
{
$type = $response->GetChar();
if ($type == 'l') {
return 'Listen';
} elseif ($type == 'd') {
return 'Dedicated';
} else {
return 'SourceTV';
}
}
private function _GetOS($response)
{
if ($response->GetChar() == 'l') {
return 'Linux';
} else {
return 'Windows';
}
}
public function __construct($ip, $port)
{
$this->_socket = new Stream_UDP($ip, $port);
}
public function GetInfo()
{
$info = array();
$response = $this->_socket->Send(Source_Query::A2S_INFO, 5);
$info['net_ver'] = $response->GetByte(true);
$info['name'] = $response->GetString();
$info['map'] = $response->GetString();
$info['dir'] = $response->GetString();
$info['desc'] = $response->GetString();
$info['app_id'] = $response->GetShort();
$info['num_players'] = $response->GetByte(true);
$info['max_players'] = $response->GetByte(true);
$info['num_bots'] = $response->GetByte(true);
$info['dedicated'] = $this->_GetDedicated($response);
$info['os'] = $this->_GetOS($response);
$info['private'] = $response->GetByte(true);
$info['secure'] = $response->GetByte(true);
$info['game_ver'] = $response->GetString();
return $info;
}
public function GetPlayers()
{
$players = array();
$challenge = substr($this->_socket->Send(Source_Query::A2S_GETCHALLENGE, 0, true), 5);
$response = $this->_socket->Send(Source_Query::A2S_PLAYER.$challenge, 5);
$num_players = $response->GetByte(true);
for ($player_index = 0; $player_index < $num_players; $player_index++) {
$response->GetByte();
$player = array();
$player['name'] = $response->GetString();
$player['kills'] = $response->GetLong();
$player['time'] = date("H:i:s", mktime(0, 0, $response->GetFloat()));
$players[$player_index] = $player;
}
return $players;
}
}
?>
test.php
Code:
<html>
<body>
<?php
require_once('source_query.php');
$server = new Source_Query('63.144.102.125', 27015);
$info = $server->GetInfo();
print "<b>Network version:</b> ".$info['net_ver']." <br>\n";
print "<b>Server name:</b> ".$info['name']." <br>\n";
print "<b>Current map:</b> ".$info['map']." <br>\n";
print "<b>Game directory:</b> ".$info['dir']." <br>\n";
print "<b>Game description:</b> ".$info['desc']." <br>\n";
print "<b>Application ID:</b> ".$info['app_id']." <br>\n";
print "<b>Number of players:</b> ".$info['num_players']." <br>\n";
print "<b>Maximum players:</b> ".$info['max_players']." <br>\n";
print "<b>Number of bots:</b> ".$info['num_bots']." <br>\n";
print "<b>Type of server:</b> ".$info['dedicated']." <br>\n";
print "<b>Server OS:</b> ".$info['os']." <br>\n";
print "<b>Private:</b> ";
if ($info['private']) {
print "Yes <br>\n";
} else {
print "No <br>\n";
}
print "<b>VAC secured:</b> ";
if ($info['secure']) {
print "Yes <br>\n";
} else {
print "No <br>\n";
}
print "<b>Game version:</b> ".$info['game_ver']." <br><br><br>\n";
$players = $server->GetPlayers();
foreach ($players as $player) {
print "<b>Player name:</b> ".$player['name']." <br>\n";
print "<b>Kills current round:</b> ".$player['kills']." <br>\n";
print "<b>Time since joining:</b> ".$player['time']." <br><br>\n";
}
?>
</body>
</html>