206 lines
5.6 KiB
PHP
206 lines
5.6 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Namespace for Valve RCON related classes and functions.
|
|
* @package NAE\Valve\Rcon
|
|
*/
|
|
namespace NAE\Valve\Rcon;
|
|
|
|
/**
|
|
* A Class to interact with a Valve RCON server.
|
|
* @author "Marcel Naeve" <php@naeve.info>
|
|
* @license MIT License (http://www.opensource.org/licenses/mit)
|
|
*/
|
|
class ValveRcon {
|
|
|
|
/**
|
|
* @var string IP address of the RCON server
|
|
*/
|
|
private $ip;
|
|
|
|
/**
|
|
* @var int Port number of the RCON server
|
|
*/
|
|
private $port;
|
|
|
|
/**
|
|
* @var string Password to authenticate with the RCON server
|
|
*/
|
|
private $password;
|
|
|
|
/**
|
|
* @var int Timeout for socket operations in seconds
|
|
*/
|
|
private $timeout;
|
|
|
|
/**
|
|
* @var resource Socket resource for RCON communication
|
|
*/
|
|
private $socket;
|
|
|
|
/**
|
|
* @var array Error details if any occurs during communication
|
|
*/
|
|
private $err;
|
|
|
|
/**
|
|
* Constructor, setting variables.
|
|
* @param string $ip IP address of the RCON server
|
|
* @param int $port Port number of the RCON server
|
|
* @param string $password Password to authenticate with the RCON server
|
|
* @param int $timeout Timeout for socket operations in seconds (default: 2 seconds)
|
|
*/
|
|
public function __construct(string $ip, int $port, string $password, int $timeout=2) {
|
|
$this->ip = $ip;
|
|
$this->port = $port;
|
|
$this->password = $password;
|
|
$this->timeout = $timeout;
|
|
}
|
|
|
|
/**
|
|
* Establishes a connection to the RCON server.
|
|
* @return bool Connection established successfully?
|
|
*/
|
|
private function connect() : bool {
|
|
|
|
// prepare variables for error handling
|
|
$errno = null;
|
|
$errmsg = "";
|
|
|
|
$this->socket = fsockopen( // connect to RCON server
|
|
"udp://{$this->ip}",
|
|
$this->port,
|
|
$errno,
|
|
$errmsg,
|
|
$this->timeout
|
|
);
|
|
|
|
if(!$this->socket) { // connection failed?
|
|
$this->err = [
|
|
"code" => $errno,
|
|
"message" => "Socket connection failed: $errmsg"
|
|
];
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* Closes the connection to the RCON server
|
|
*/
|
|
private function disconnect() : void {
|
|
|
|
if ( $this->isConnected() ) {
|
|
fclose($this->socket);
|
|
$this->socket = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Checks if the connection to the RCON server is established.
|
|
*/
|
|
private function isConnected() : bool {
|
|
|
|
return ( get_resource_type($this->socket) !== null ); // check if socket is resource type, as it should be
|
|
|
|
}
|
|
|
|
/**
|
|
* Receives data from the RCON server.
|
|
* @return string|null Received data or null if no data is available
|
|
*/
|
|
private function read() : ?string {
|
|
|
|
if (!$this->isConnected()) {
|
|
return null;
|
|
}
|
|
|
|
stream_set_timeout($this->socket, 0, $this->timeout * 100000); // set timeout
|
|
|
|
$response = '';
|
|
while ($buffer = fread($this->socket, 4096)) { // loop while there's more data
|
|
list($header, $content) = explode("\n", $buffer, 2); // split into header and content
|
|
$response .= $content; // append content to response
|
|
}
|
|
|
|
$response = trim($response); // remove whitespaces around the response
|
|
|
|
if (empty($response)) // no data received, return null
|
|
return null;
|
|
|
|
return preg_replace("/\^./","", $response); // return response without prefix
|
|
|
|
}
|
|
|
|
/**
|
|
* Writes a command to the RCON server.
|
|
* @return bool $cmd Command writen successfully?
|
|
*/
|
|
private function write(string $cmd) : bool {
|
|
|
|
if (!$this->isConnected()) {
|
|
return false;
|
|
}
|
|
|
|
if( false === fwrite($this->socket, str_repeat(chr(255), 4) . "rcon {$this->password} {$cmd}\n") ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* Sends a command to the RCON server and optionally reads the response.
|
|
* @param string $cmd Command to send to the RCON server
|
|
* @param bool $read Should the response be read from the RCON server? (default: false)
|
|
* @return string|null Response from the RCON server or null if no response is expected or read fails
|
|
*/
|
|
public function sendCommand(string $cmd, bool $read=false) : ?string {
|
|
|
|
$this->connect();
|
|
if(!$this->write($cmd)) { // sending the command
|
|
$this->err = [
|
|
"code" => 0,
|
|
"message" => "Command could not be sent"
|
|
];
|
|
$this->disconnect();
|
|
return null;
|
|
}
|
|
|
|
if (!$read) {
|
|
$this->disconnect();
|
|
return null;
|
|
}
|
|
|
|
if(!$res = $this->read()) { // reading the response
|
|
$this->err = [
|
|
"code" => 0,
|
|
"message" => "Response could not be read"
|
|
];
|
|
}
|
|
$this->disconnect();
|
|
|
|
return $res;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the last error details if any occurred during communication.
|
|
* @return object|null Last error details or null if no error has occurred (error object contains code and message as properties)
|
|
*/
|
|
public function getError() :?object {
|
|
|
|
if($this->err === null) {
|
|
return null; // no error has occurred, return null
|
|
}
|
|
|
|
return json_decode(json_encode($this->err)); // return last error as object
|
|
|
|
}
|
|
|
|
|
|
} |