Last active
December 28, 2015 12:28
-
-
Save mmis1000/7500394 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 | |
| /*by mmis1000 : All thanks to the original auther*/ | |
| /** | |
| * 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. | |
| */ | |
| /** | |
| * merged by mmis1000 | |
| */ | |
| 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); tag off due to unicode char*/ | |
| //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['ping'] = $ping; | |
| $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' => '', | |
| 'ping'=>''/*addtion ping code by mmis1000*/ | |
| ); | |
| /** | |
| * 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 */ | |
| $startTime = microtime(true);/*addtion ping code by mmis1000*/ | |
| $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)); | |
| $ping = round((microtime(true)-$startTime)*1000);/*addtion ping code by mmis1000*/ | |
| //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'), | |
| 'version' => $Fallback->Get('version'),/*modified by mmis1000; add hook for mc version*/ | |
| 'hostport' => (int)$Port, | |
| 'hostip' => $Host, | |
| 'ping' => $Fallback->Get('ping'),/*addtion ping code by mmis1000*/ | |
| 'online' => $Fallback->Get('online') | |
| ); fclose($this->Socket); return; | |
| } else { | |
| $this->Info['online'] = false; | |
| 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['ping'] = $ping;/*addtion ping code by mmis1000*/ | |
| $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. | |
| */ | |
| // Pretty print some JSON | |
| function json_format($json) | |
| { | |
| $tab = " "; | |
| $new_json = ""; | |
| $indent_level = 0; | |
| $in_string = false; | |
| $json_obj = json_decode($json); | |
| if($json_obj === false) | |
| return false; | |
| $json = json_encode($json_obj); | |
| $len = strlen($json); | |
| for($c = 0; $c < $len; $c++) | |
| { | |
| $char = $json[$c]; | |
| switch($char) | |
| { | |
| case '{': | |
| case '[': | |
| if(!$in_string) | |
| { | |
| $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); | |
| $indent_level++; | |
| } | |
| else | |
| { | |
| $new_json .= $char; | |
| } | |
| break; | |
| case '}': | |
| case ']': | |
| if(!$in_string) | |
| { | |
| $indent_level--; | |
| $new_json .= "\n" . str_repeat($tab, $indent_level) . $char; | |
| } | |
| else | |
| { | |
| $new_json .= $char; | |
| } | |
| break; | |
| case ',': | |
| if(!$in_string) | |
| { | |
| $new_json .= ",\n" . str_repeat($tab, $indent_level); | |
| } | |
| else | |
| { | |
| $new_json .= $char; | |
| } | |
| break; | |
| case ':': | |
| if(!$in_string) | |
| { | |
| $new_json .= ": "; | |
| } | |
| else | |
| { | |
| $new_json .= $char; | |
| } | |
| break; | |
| case '"': | |
| if($c > 0 && $json[$c-1] != '\\') | |
| { | |
| $in_string = !$in_string; | |
| } | |
| default: | |
| $new_json .= $char; | |
| break; | |
| } | |
| } | |
| return $new_json; | |
| } | |
| /** | |
| * http://stackoverflow.com/questions/7709593/php-json-decode-utf8 | |
| */ | |
| function ewchar_to_utf8($matches) { | |
| $ewchar = $matches[1]; | |
| $binwchar = hexdec($ewchar); | |
| $wchar = chr(($binwchar >> 8) & 0xFF) . chr(($binwchar) & 0xFF); | |
| return iconv("unicodebig", "utf-8", $wchar); | |
| } | |
| function special_unicode_to_utf8($str) { | |
| return preg_replace_callback("/\\\u([[:xdigit:]]{4})/i", "ewchar_to_utf8", $str); | |
| } | |
| /** | |
| * by mmis1000 | |
| * remove all slash and decode escaped unicode string | |
| */ | |
| function decodeAllSlash($input) { | |
| return stripslashes(special_unicode_to_utf8($input)); | |
| } | |
| /** | |
| * by mmis1000 | |
| * remove all minecraft format code | |
| */ | |
| function removeMinecraftFormatCode($input) { | |
| return preg_replace('/§[0-z]/', "", $input); | |
| } | |
| /*End of functions and classes*/ | |
| $Server = new MinecraftServerStatus('127.0.0.1', 25565); | |
| $state = $Server->Get(); | |
| echo '<pre>'; | |
| echo removeMinecraftFormatCode(decodeAllSlash(json_format(json_encode($state)))); | |
| echo '</pre>'; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment