📄 Source: rcon.php
<?php
/**
* Minimal Minecraft (Source) RCON client in pure PHP.
* Implements just enough of the protocol to send commands and read replies.
*
* Protocol reference: https://wiki.vg/RCON
*/
class RconException extends Exception {}
class Rcon {
private $sock;
private int $reqId = 0;
public function __construct(string $host, int $port, string $password, int $timeout = 3) {
$errno = 0; $errstr = '';
$this->sock = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$this->sock) {
throw new RconException("Cannot connect to RCON at $host:$port ($errstr)");
}
stream_set_timeout($this->sock, $timeout);
// Auth packet: type 3
$this->sendPacket(3, $password);
$resp = $this->readPacket();
if ($resp === null) {
throw new RconException("No RCON auth response");
}
// Response id -1 means auth failed
if ($resp['id'] === -1) {
throw new RconException("RCON auth failed");
}
}
public function command(string $cmd): string {
$this->sendPacket(2, $cmd);
$resp = $this->readPacket();
return $resp['body'] ?? '';
}
public function close(): void {
if ($this->sock) {
fclose($this->sock);
$this->sock = null;
}
}
private function sendPacket(int $type, string $body): void {
$this->reqId++;
$id = $this->reqId;
$payload = pack('VV', $id, $type) . $body . "\x00\x00";
$packet = pack('V', strlen($payload)) . $payload;
fwrite($this->sock, $packet);
}
private function readPacket(): ?array {
$lenRaw = fread($this->sock, 4);
if (strlen($lenRaw) < 4) {
return null;
}
$len = unpack('V', $lenRaw)[1];
if ($len < 10) {
return null;
}
$data = '';
while (strlen($data) < $len) {
$chunk = fread($this->sock, $len - strlen($data));
if ($chunk === false || $chunk === '') {
break;
}
$data .= $chunk;
}
if (strlen($data) < 10) {
return null;
}
$id = unpack('V', substr($data, 0, 4))[1];
$type = unpack('V', substr($data, 4, 4))[1];
$body = substr($data, 8, -2); // strip trailing \x00\x00
return ['id' => $id, 'type' => $type, 'body' => $body];
}
}
/**
* Read RCON settings from a server.properties file, if present.
*/
function rcon_from_properties(string $serverDir, array $defaults): array {
$props = $serverDir . '/server.properties';
$conf = $defaults;
if (is_file($props)) {
foreach (file($props, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
if ($line === '' || $line[0] === '#') continue;
$pos = strpos($line, '=');
if ($pos === false) continue;
$k = trim(substr($line, 0, $pos));
$v = trim(substr($line, $pos + 1));
if ($k === 'rcon.port') $conf['port'] = (int)$v;
if ($k === 'rcon.password') $conf['password'] = $v;
}
}
return $conf;
}
← Back