Skip to content

Instantly share code, notes, and snippets.

@mmis1000
Last active December 28, 2015 12:28
Show Gist options
  • Select an option

  • Save mmis1000/7500394 to your computer and use it in GitHub Desktop.

Select an option

Save mmis1000/7500394 to your computer and use it in GitHub Desktop.
<?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