Skip to content

Instantly share code, notes, and snippets.

@mmis1000
Created November 13, 2013 18:32
Show Gist options
  • Select an option

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

Select an option

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