Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- class ServerQueryException extends Exception {}
- class ServerQuery {
- const TYPE_CHALLENGE = 0x09;
- const TYPE_QUERY = 0x00;
- private $socket;
- private $players;
- private $info;
- public function connect($addr, $port = 25565, $timeout = 3) {
- if(!is_int($timeout) || $timeout < 0) {
- throw new InvalidArgumentException('Timeout must be a positive integer.');
- }
- if(!is_int($port) || $port < 1 || $port > 65535) {
- throw new InvalidArgumentException('Port must be an integer between 1 and 65535.');
- }
- $this -> socket = @fsockopen('udp://' . $addr, (int)$port, $errno, $errstr, $timeout);
- if($errno || $this -> socket === false) {
- throw new ServerQueryException('Could not create socket: ' . $errstr);
- }
- stream_set_timeout($this -> socket, $timeout);
- stream_set_blocking($this -> socket, true);
- try {
- $challenge = $this -> getChallenge();
- $this -> getStatus($challenge);
- } catch(ServerQueryException $ex) {
- // Dispose of socket and close the connection
- fclose($this -> socket);
- throw new ServerQueryException($ex -> getMessage());
- }
- fclose($this -> socket);
- }
- public function getServerInfo() {
- return isset($this -> info) ? $this -> info : false;
- }
- public function getPlayerList() {
- return isset($this -> players) ? $this -> players : false;
- }
- private function getChallenge() {
- $data = $this -> query(self::TYPE_CHALLENGE);
- if($data === false) {
- throw new ServerQueryException('Failed to receive challenge token.');
- }
- return pack('N', $data);
- }
- private function getStatus($challenge) {
- $data = $this -> query(self::TYPE_QUERY, $challenge . pack('c*', 0x00, 0x00, 0x00, 0x00));
- if(!$data) {
- throw new ServerQueryException('Failed to get server status.');
- }
- $lastKey = '';
- $info = array();
- $data = substr($data, 11);
- $data = explode("\x00\x00\x01player_\x00\x00", $data);
- if(count($data) !== 2) {
- throw new ServerQueryException('Failed to parse server status.');
- }
- $players = substr($data[1], 0, -2);
- $data = explode("\x00", $data[0]);
- $validKeys = array(
- 'game_id' => 'Game',
- 'hostname' => 'Server Name',
- 'gametype' => 'Game Type',
- 'version' => 'Version',
- 'plugins' => 'Plugins', //This will never actually display anything; it's hardcoded to ""
- 'map' => 'Default World',
- 'numplayers' => 'Players',
- 'maxplayers' => 'Max Players',
- 'hostport' => 'Server Port',
- 'hostip' => 'Server Address'
- );
- foreach($data as $key => $value) {
- if(~$key & 1) {
- if(!array_key_exists($value, $validKeys)) {
- $lastKey = false;
- continue;
- }
- $lastKey = $validKeys[$value];
- $info[$lastKey] = '';
- } else if($lastKey != false) {
- $info[$lastKey] = $value;
- }
- }
- // Check integers
- $info['Players'] = intval($info['Players']);
- $info['Max Players'] = intval($info['Max Players']);
- $info['Server Port'] = intval($info['Server Port']);
- // Parse installed plugins, if any
- if($info['Plugins']) {
- $data = explode(": ", $info['Plugins'], 2);
- $info['Raw_Plugins'] = $info['Plugins'];
- $info['Software'] = $data[0];
- if(count($data) == 2) {
- $info['Plugins'] = explode(", ", $data[1]);
- }
- } else {
- $info['Software'] = 'Vanilla Server';
- }
- $this -> info = $info;
- if($players) {
- $this -> players = explode("\x00", $players);
- }
- }
- private function query($command, $append = "") {
- $command = pack('c*', 0xFE, 0xFD, $command, 0x01, 0x02, 0x03, 0x04) . $append;
- $length = strlen($command);
- // Compare number of bytes sent with number of bytes returned by fwrite
- // If there is a mismatch, then we have a problem
- if($length !== fwrite($this -> socket, $command, $length)) {
- throw new ServerQueryException('Failed to write to socket.');
- }
- $data = fread($this -> socket, 2048);
- if($data === false) {
- throw new ServerQueryException('Failed to read from socket.');
- }
- if(strlen($data) < 5 || $data[0] != $command[2]) {
- return false;
- }
- return substr($data, 5);
- }
- }
- ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement