Created
November 13, 2013 18:32
-
-
Save mmis1000/7453966 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Minecraft server status fallback class | |
| * Read the simple server info wich are actually for minecraft clients | |
| * @author Patrick K. - http://www.silexboard.org/ - https://github.com/NoxNebula | |
| * @license GNU Public Licence - Version 3 | |
| * @copyright c 2011-2013 Patrick K. | |
| */ | |
| /** | |
| * Minecraft Server Status Query | |
| * @author Julian Spravil <[email protected]> https://github.com/FunnyItsElmo | |
| * @license Free to use but dont remove the author, license and copyright | |
| * @copyright c 2013 Julian Spravil | |
| * edited by pcchou. | |
| */ | |
| class MinecraftServerStatusSimple { | |
| private $Socket; | |
| private $Info = array(); | |
| /** | |
| * Read the minecraft server info and parse it | |
| * @param string $Host | |
| * @param int $Port optional | |
| * @param int $Timeout optional | |
| */ | |
| public function __construct($Host, $Port = 25565, $Timeout = 3) { | |
| /* merge start*/ | |
| //Transform domain to ip address. | |
| if (substr_count($Host , '.') != 4) $Host = gethostbyname($Host); | |
| //Get timestamp for the ping | |
| $start = microtime(true); | |
| //Connect to the server | |
| if(!$socket = @stream_socket_client('tcp://'.$Host.':'.$Port, $errno, $errstr, $Timeout)) { | |
| $this->Info['online'] = false; | |
| } else { | |
| stream_set_timeout($socket, $Timeout); | |
| //Write and read data | |
| fwrite($socket, "\xFE\x01"); | |
| $data = fread($socket, 2048); | |
| fclose($socket); | |
| if($data == null) return false; | |
| //Calculate the ping | |
| $ping = round((microtime(true)-$start)*1000); | |
| //Evaluate the received data | |
| if (substr((String)$data, 3, 5) == "\x00\xa7\x00\x31\x00"){ | |
| $result = explode("\x00", mb_convert_encoding(substr((String)$data, 15), 'UTF-8', 'UCS-2')); | |
| $motd = preg_replace("/(§.)/", "",$result[1]); | |
| }else{ | |
| $result = explode('§', mb_convert_encoding(substr((String)$data, 3), 'UTF-8', 'UCS-2')); | |
| $motd = ""; | |
| foreach ($result as $key => $string) { | |
| if($key != sizeof($result)-1 && $key != sizeof($result)-2 && $key != 0) { | |
| $motd .= '§'.$string; | |
| } | |
| } | |
| $motd = preg_replace("/(§.)/", "", $motd); | |
| } | |
| //Remove all special characters from a string | |
| $motd = preg_replace("/[^[:alnum:][:punct:] ]/", "", $motd); | |
| //Set variables | |
| $this->Info['hostip'] = $Host; | |
| $this->Info['version'] = $result[0]; | |
| $this->Info['motd'] = $motd; | |
| $this->Info['numplayers'] = $result[sizeof($result)-2]; | |
| $this->Info['maxplayers'] = $result[sizeof($result)-1]; | |
| $this->Info['online'] = true; | |
| } | |
| /*merge end*/ | |
| } | |
| /** | |
| * Return the value of an key or the whole server info | |
| * @param string $Key optional | |
| * @return mixed | |
| */ | |
| public function Get($Key = '') { | |
| return $Key ? (array_key_exists($Key, $this->Info) ? $this->Info[$Key] : false) : $this->Info; | |
| } | |
| } | |
| /** | |
| * Minecraft server status class | |
| * Query minecraft server | |
| * @author Patrick K. - http://www.silexboard.org/ - https://github.com/NoxNebula | |
| * @license GNU Public Licence - Version 3 | |
| * @copyright c 2011-2013 Patrick K. | |
| */ | |
| class MinecraftServerStatus { | |
| // Get the server status | |
| const STATUS = 0x00; | |
| // Make the challenge (handshake) | |
| const HANDSHAKE = 0x09; | |
| // "Magic bytes" | |
| const B1 = 0xFE; | |
| const B2 = 0xFD; | |
| private $Socket; | |
| // Expected server info (Minecraft 1.3.2) | |
| // more keys may added while running the code | |
| private $Info = array( | |
| 'hostname' => '', | |
| 'gametype' => '', | |
| 'game_id' => '', | |
| 'version' => '', | |
| 'plugins' => '', | |
| 'map' => '', | |
| 'numplayers' => '', | |
| 'maxplayers' => '', | |
| 'hostport' => '', | |
| 'hostip' => '' | |
| ); | |
| /** | |
| * Query a minecraft server and parse the status | |
| * @param string $Host | |
| * @param int $Port optional | |
| * @param int $Timeout optional | |
| */ | |
| public function __construct($Host, $Port = 25565, $Timeout = 1) { | |
| /* Connect to the host and creat a socket */ | |
| $this->Socket = @stream_socket_client('udp://'.$Host.':'.(int)$Port, $ErrNo, $ErrStr, $Timeout); | |
| if($ErrNo || $this->Socket === false) { | |
| $this->Info['online'] = false; return; | |
| //throw new Exception('Failed to connect', 1); | |
| } | |
| stream_set_timeout($this->Socket, $Timeout); | |
| /* Make handshake and request server status */ | |
| $Data = $this->Send(self::STATUS, pack('N', $this->Send(self::HANDSHAKE)).pack('c*', 0x00, 0x00, 0x00, 0x00)); | |
| //set_time_limit($met); | |
| // Try fallback if query is not enabled on the server | |
| if(!$Data){ | |
| if(!class_exists('MinecraftServerStatusSimple') && file_exists('MinecraftServerStatusSimple.class.php')) | |
| require_once('MinecraftServerStatusSimple.class.php'); | |
| if(class_exists('MinecraftServerStatusSimple')) { | |
| $Fallback = new MinecraftServerStatusSimple($Host, $Port, $Timeout); | |
| $this->Info = array( | |
| 'hostname' => $Fallback->Get('motd'), | |
| 'numplayers' => $Fallback->Get('numplayers'), | |
| 'maxplayers' => $Fallback->Get('maxplayers'), | |
| 'hostport' => (int)$Port, | |
| 'hostip' => $Host, | |
| 'online' => $Fallback->Get('online') | |
| ); fclose($this->Socket); return; | |
| } | |
| } | |
| /* Prepare the data for parsing */ | |
| // Split the data string on the player position | |
| $Data = explode("\00\00\01player_\00\00", $Data); | |
| // Save the players | |
| $Players = ''; | |
| if($Data[1]) | |
| $Players = substr($Data[1], 0, -2); | |
| // Split the server infos (status) | |
| $Data = explode("\x00", $Data[0]); | |
| /* Parse server info */ | |
| for($i = 0; $i < sizeof($Data); $i += 2) { | |
| // Check if the server info is expected, if yes save the value | |
| if(array_key_exists($Data[$i], $this->Info) && array_key_exists($i+1, $Data)) | |
| $this->Info[$Data[$i]] = $Data[$i+1]; | |
| } | |
| // Parse plugins and try to determine the server software | |
| if($this->Info['plugins']) { | |
| $Data = explode(": ", $this->Info['plugins']); | |
| $this->Info['software'] = $Data[0]; | |
| if(isset($Data[1])) | |
| $this->Info['plugins'] = explode('; ', $Data[1]); | |
| else | |
| unset($this->Info['plugins']); | |
| } else { | |
| // It seems to be a vanilla server | |
| $this->Info['software'] = 'Vanilla'; | |
| unset($this->Info['plugins']); | |
| } | |
| // Parse players | |
| if($Players) | |
| $this->Info['players'] = explode("\00", $Players); | |
| // Cast types | |
| $this->Info['numplayers'] = (int)$this->Info['numplayers']; | |
| $this->Info['maxplayers'] = (int)$this->Info['maxplayers']; | |
| $this->Info['hostport'] = (int)$this->Info['hostport']; | |
| $this->Info['online'] = true; | |
| /* Close the connection */ | |
| fclose($this->Socket); | |
| } | |
| /** | |
| * Return the value of an key or the whole server info | |
| * @param string $Key optional | |
| * @return mixed | |
| */ | |
| public function Get($Key = '') { | |
| return $Key ? (array_key_exists($Key, $this->Info) ? $this->Info[$Key] : false) : $this->Info; | |
| } | |
| /** | |
| * Send a command to the server and get the answer | |
| * @param byte $Command | |
| * @param byte $Addition optional | |
| * @return string | |
| */ | |
| private function Send($Command, $Addition = '') { | |
| // pack the command into a binary string | |
| $Command = pack('c*', self::B1, self::B2, $Command, 0x01, 0x02, 0x03, 0x04).$Addition; | |
| // send the binary string to the server | |
| if(strlen($Command) !== @fwrite($this->Socket, $Command, strlen($Command))) | |
| throw new Exception('Failed to write on socket', 2); | |
| // listen what the server has to say now | |
| $Data = fread($this->Socket, 2048); | |
| if($Data === false) | |
| throw new Exception('Failed to read from socket', 3); | |
| // remove the first 5 unnecessary bytes (0x00, 0x01, 0x02, 0x03, 0x04) Status type and own ID token | |
| return substr($Data, 5); | |
| } | |
| } | |
| /** | |
| * Minecraft Server Status Query | |
| * @author Julian Spravil <[email protected]> https://github.com/FunnyItsElmo | |
| * @license Free to use but dont remove the author, license and copyright | |
| * @copyright c 2013 Julian Spravil | |
| * edited by pcchou. | |
| */ | |
| $Server = new MinecraftServerStatus('127.0.0.1', 25565); | |
| $state = $Server->Get(); | |
| header( "Content-type: image/png" ); | |
| $my_img = imagecreate( 120, 60 ); | |
| if($state['online']) { | |
| $background = imagecolorallocate( $my_img, 0, 255, 0 ); | |
| $text = ($state['numplayers']).'/'.($state['maxplayers']); | |
| $text_colour = imagecolorallocate( $my_img, 0, 0, 0); | |
| $line_colour = imagecolorallocate( $my_img, 128, 255, 0 ); | |
| imagestring( $my_img, 4, 45, 25, $text, $text_colour ); | |
| imagesetthickness ( $my_img, 5 ); | |
| imageline( $my_img, 10, 45, 110, 45, $line_colour ); | |
| imagepng( $my_img ); | |
| imagecolordeallocate( $line_color ); | |
| imagecolordeallocate( $text_color ); | |
| imagecolordeallocate( $background ); | |
| imagedestroy( $my_img ); | |
| } else { | |
| $background = imagecolorallocate( $my_img, 255, 0, 0 ); | |
| $text_colour = imagecolorallocate( $my_img, 255, 255, 255 ); | |
| $text = 'offline'; | |
| $line_colour = imagecolorallocate( $my_img, 128, 255, 0 ); | |
| imagestring( $my_img, 4, 30, 25, $text, $text_colour ); | |
| imagesetthickness ( $my_img, 5 ); | |
| imageline( $my_img, 10, 45, 110, 45, $line_colour ); | |
| imagepng( $my_img ); | |
| imagecolordeallocate( $line_color ); | |
| imagecolordeallocate( $text_color ); | |
| imagecolordeallocate( $background ); | |
| imagedestroy( $my_img ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment