A while back (ok, so a long time ago) I wrote a Source RCON class in php for accessing the rcon on Source based dedicated servers.
I put it up here on PHP Classes.org and forgot about it until recently when someone came to me asking how to perform server query commands with it. Well in short you can’t.
I’m about to start work on another pair of classes that’ll let you do server queries and master server queries. To be consistent, I’ll design these classes around PHP4. Then a bit later, I’ll re-do them all specificly for PHP5.
But without further delay – here is the class (and below it is an example script)
This class implements a TCP based client that acts as a remote console – RCON – class for sending commands to control game variables of Counter-String Source and other games using a Source Dedicated Server – SRCDS .
<?php
/*
Basic CS:S Rcon class by Freman. (V1.00)
----------------------------------------------
Ok, it's a completely working class now with with multi-packet responses
Contact: printf("%s%s%s%s%s%s%s%s%s%d%s%s%s","rc","on",chr(46),"cl","ass",chr(64),"pri","ya",chr(46),2,"y",chr(46),"net")
Behaviour I've noticed:
rcon is not returning the packet id.
*/
define("SERVERDATA_EXECCOMMAND",2);
define("SERVERDATA_AUTH",3);
class RCon {
var $Password;
var $Host;
var $Port = 27015;
var $_Sock = null;
var $_Id = 0;
function RCon ($Host,$Port,$Password) {
$this->Password = $Password;
$this->Host = $Host;
$this->Port = $Port;
$this->_Sock = @fsockopen($this->Host,$this->Port, $errno, $errstr, 30) or
die("Unable to open socket: $errstr ($errno)\n");
$this->_Set_Timeout($this->_Sock,2,500);
}
function Auth () {
$PackID = $this->_Write(SERVERDATA_AUTH,$this->Password);
// Real response (id: -1 = failure)
$ret = $this->_PacketRead();
if ($ret[1]['id'] == -1) {
die("Authentication Failure\n");
}
}
function _Set_Timeout(&$res,$s,$m=0) {
if (version_compare(phpversion(),'4.3.0','<')) {
return socket_set_timeout($res,$s,$m);
}
return stream_set_timeout($res,$s,$m);
}
function _Write($cmd, $s1='', $s2='') {
// Get and increment the packet id
$id = ++$this->_Id;
// Put our packet together
$data = pack("VV",$id,$cmd).$s1.chr(0).$s2.chr(0);
// Prefix the packet size
$data = pack("V",strlen($data)).$data;
// Send packet
fwrite($this->_Sock,$data,strlen($data));
// In case we want it later we'll return the packet id
return $id;
}
function _PacketRead() {
//Declare the return array
$retarray = array();
//Fetch the packet size
while ($size = @fread($this->_Sock,4)) {
$size = unpack('V1Size',$size);
//Work around valve breaking the protocol
if ($size["Size"] > 4096) {
//pad with 8 nulls
$packet = "\x00\x00\x00\x00\x00\x00\x00\x00".fread($this->_Sock,4096);
} else {
//Read the packet back
$packet = fread($this->_Sock,$size["Size"]);
}
array_push($retarray,unpack("V1ID/V1Response/a*S1/a*S2",$packet));
}
return $retarray;
}
function Read() {
$Packets = $this->_PacketRead();
foreach($Packets as $pack) {
if (isset($ret[$pack['ID']])) {
$ret[$pack['ID']]['S1'] .= $pack['S1'];
$ret[$pack['ID']]['S2'] .= $pack['S1'];
} else {
$ret[$pack['ID']] = array(
'Response' => $pack['Response'],
'S1' => $pack['S1'],
'S2' =>$pack['S2'],
);
}
}
return $ret;
}
function sendCommand($Command) {
$Command = '"'.trim(str_replace(' ','" "', $Command)).'"';
$this->_Write(SERVERDATA_EXECCOMMAND,$Command,'');
}
function rconCommand($Command) {
$this->sendcommand($Command);
$ret = $this->Read();
//ATM: Source servers don't return the request id, but if they fix this the code below should read as
// return $ret[$this->_Id]['S1'];
return $ret[0]['S1'];
}
}
?>
<?php
include_once("rcon.class.php");
$r = new rcon("127.0.0.1",27015,"testme");
$r->Auth();
echo "Authenticated\n";
//Send a request
var_dump($r->rconCommand("cvarlist"));
?>