* @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 } }