Dateien nach "rcon" hochladen
This commit is contained in:
102
rcon/Rcon.php
Normal file
102
rcon/Rcon.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* Compact RCON client for Minecraft with improved read loop & timeouts.
|
||||
*/
|
||||
class Rcon {
|
||||
private $host;
|
||||
private $port;
|
||||
private $password;
|
||||
private $socket;
|
||||
private $req_id;
|
||||
private $timeout;
|
||||
|
||||
const SERVERDATA_AUTH = 3;
|
||||
const SERVERDATA_EXECCOMMAND = 2;
|
||||
|
||||
public function __construct($host, $port, $password, $timeout = 3) {
|
||||
$this->host = $host;
|
||||
$this->port = intval($port);
|
||||
$this->password = $password;
|
||||
$this->timeout = $timeout;
|
||||
$this->socket = null;
|
||||
$this->req_id = random_int(1, 0x7fffffff);
|
||||
}
|
||||
|
||||
public function connect() {
|
||||
$this->socket = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout);
|
||||
if (!$this->socket) return false;
|
||||
stream_set_timeout($this->socket, $this->timeout);
|
||||
stream_set_blocking($this->socket, true);
|
||||
|
||||
$pkt = $this->buildPacket($this->req_id, self::SERVERDATA_AUTH, $this->password);
|
||||
fwrite($this->socket, $pkt);
|
||||
|
||||
$resp = $this->readPacket();
|
||||
if ($resp === false) { $this->disconnect(); return false; }
|
||||
if (isset($resp['id']) && intval($resp['id']) == -1) { $this->disconnect(); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
public function disconnect() {
|
||||
if ($this->socket) { fclose($this->socket); $this->socket = null; }
|
||||
}
|
||||
|
||||
public function sendCommand($command) {
|
||||
if (!$this->socket) return false;
|
||||
$this->req_id = ($this->req_id + 1) & 0x7fffffff;
|
||||
$pkt = $this->buildPacket($this->req_id, self::SERVERDATA_EXECCOMMAND, $command);
|
||||
fwrite($this->socket, $pkt);
|
||||
|
||||
$out = '';
|
||||
$start = time();
|
||||
while (true) {
|
||||
$resp = $this->readPacket();
|
||||
if ($resp === false) break;
|
||||
if (isset($resp['body'])) $out .= $resp['body'];
|
||||
$meta = stream_get_meta_data($this->socket);
|
||||
if ($meta['timed_out']) break;
|
||||
if (time() - $start > $this->timeout) break;
|
||||
if (strlen($resp['body']) < 4096) break;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function buildPacket($id, $type, $body) {
|
||||
$payload = pack('V', $id) . pack('V', $type) . $body . "\x00\x00";
|
||||
$size = strlen($payload);
|
||||
return pack('V', $size) . $payload;
|
||||
}
|
||||
|
||||
private function readPacket() {
|
||||
$sizeData = $this->fread_all(4);
|
||||
if ($sizeData === false) return false;
|
||||
$sizeArr = unpack('Vsize', $sizeData);
|
||||
$size = $sizeArr['size'];
|
||||
if ($size <= 0) return false;
|
||||
$payload = $this->fread_all($size);
|
||||
if ($payload === false) return false;
|
||||
$id = unpack('Vid', substr($payload, 0, 4))['id'] ?? 0;
|
||||
$type = unpack('Vtype', substr($payload, 4, 4))['type'] ?? 0;
|
||||
$body = substr($payload, 8, $size - 10);
|
||||
return ['id' => $id, 'type' => $type, 'body' => $body];
|
||||
}
|
||||
|
||||
private function fread_all($len) {
|
||||
$data = '';
|
||||
$read = 0;
|
||||
$start = time();
|
||||
while ($read < $len) {
|
||||
$chunk = @fread($this->socket, $len - $read);
|
||||
if ($chunk === false) return false;
|
||||
if ($chunk === '') {
|
||||
$meta = stream_get_meta_data($this->socket);
|
||||
if ($meta['timed_out'] || (time() - $start) > $this->timeout) return false;
|
||||
usleep(10000);
|
||||
continue;
|
||||
}
|
||||
$data .= $chunk;
|
||||
$read += strlen($chunk);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user