Skip to content

Instantly share code, notes, and snippets.

@emanh1
Created November 19, 2016 04:33
Show Gist options
  • Select an option

  • Save emanh1/e58629c4582c56997a5e0300c1f473b2 to your computer and use it in GitHub Desktop.

Select an option

Save emanh1/e58629c4582c56997a5e0300c1f473b2 to your computer and use it in GitHub Desktop.
<?php
/**
* Fast and easy way to access Mojang API
*
* Can be used to get Mojang status, UUID and username conversions, names history, and fetch skin.
* Usually, if NULL is returned, it means empty, and FALSE means failure
* Also, UUIDs returned are minified (without dashes)
*
* @see http://wiki.vg/Mojang_API
*/
class MojangAPI {
/**
* Get Mojang status
*
* @return array|bool Array with status, FALSE on failure
*/
public static function getStatus() {
$json = self::fetchJson('https://status.mojang.com/check');
if (is_array($json)) {
$status = array();
foreach ($json as $array) {
if (!empty($array)) {
list($key, $value) = each($array);
$status[$key] = $value;
}
}
return $status;
}
return false;
}
/**
* Get UUID from username, an optional time can be provided
*
* @param string $username
* @param int $time optional
* @return string|bool UUID (without dashes) on success, FALSE on failure
*/
public static function getUuid($username, $time = 0) {
$profile = self::getProfile($username, $time);
if (is_array($profile) and isset($profile['id'])) {
return $profile['id'];
}
return false;
}
/**
* Get username from UUID
*
* @param string $uuid
* @return string|bool Username on success, FALSE on failure
*/
public static function getUsername($uuid) {
$history = self::getNameHistory($uuid);
if (is_array($history)) {
$last = array_pop($history);
if (is_array($last) and array_key_exists('name', $last)) {
return $last['name'];
}
}
return false;
}
/**
* Get profile (username and UUID) from username, an optional time can be provided
*
* @param string $username
* @param int $time optional
* @return array|bool Array with id and name, FALSE on failure
*/
public static function getProfile($username, $time = 0) {
if (self::isValidUsername($username) and is_numeric($time)) {
return self::fetchJson('https://api.mojang.com/users/profiles/minecraft/' . $username . ($time > 0 ? '?at=' . $time : ''));
}
return false;
}
/**
* Get name history from UUID
*
* @param string $uuid
* @return array|bool Array with his username's history, FALSE on failure
*/
public static function getNameHistory($uuid) {
if (self::isValidUuid($uuid)) {
return self::fetchJson('https://api.mojang.com/user/profiles/' . self::minifyUuid($uuid) . '/names');
}
return false;
}
/**
* Check if string is a valid Minecraft username
*
* @param string $string to check
* @return bool Whether username is valid or not
*/
public static function isValidUsername($string) {
return is_string($string) and strlen($string) >= 2 and strlen($string) <= 16 and ctype_alnum(str_replace('_', '', $string));
}
/**
* Check if string is a valid UUID, with or without dashes
*
* @param string $string to check
* @return bool Whether UUID is valid or not
*/
public static function isValidUuid($string) {
return is_string(self::minifyUuid($string));
}
/**
* Remove dashes from UUID
*
* @param string $uuid
* @return string|bool UUID without dashes (32 chars), FALSE on failure
*/
public static function minifyUuid($uuid) {
if (is_string($uuid)) {
$minified = str_replace('-', '', $uuid);
if (strlen($minified) === 32) {
return $minified;
}
}
return false;
}
/**
* Add dashes to an UUID
*
* @param string $uuid
* @return string|bool UUID with dashes (36 chars), FALSE on failure
*/
public static function formatUuid($uuid) {
$uuid = self::minifyUuid($uuid);
if (is_string($uuid)) {
return substr($uuid, 0, 8) . '-' . substr($uuid, 8, 4) . '-' . substr($uuid, 12, 4) . '-' . substr($uuid, 16, 4) . '-' . substr($uuid, 20, 12);
}
return false;
}
/**
* Check if username is Alex or Steve is a valid UUID, with or without dashes
*
* @param string $uuid
* @return bool|null TRUE if Alex, FALSE if Steve, NULL on error
*/
public static function isAlex($uuid) {
$uuid = self::minifyUuid($uuid);
if (is_string($uuid)) {
$sub = array();
for ($i = 0; $i < 4; $i++) {
$sub[$i] = intval('0x' . substr($uuid, $i * 8, 8) + 0, 16);
}
return (bool) ((($sub[0] ^ $sub[1]) ^ ($sub[2] ^ $sub[3])) % 2);
}
return null;
}
/**
* Get profile (username and UUID) from UUID
*
* This has a rate limit to 1 per minute per profile
*
* @param string $uuid
* @return array|bool Array with profile and properties, FALSE on failure
*/
public static function getSessionProfile($uuid) {
if (self::isValidUuid($uuid)) {
return self::fetchJson('https://sessionserver.mojang.com/session/minecraft/profile/' . self::minifyUuid($uuid));
}
return false;
}
/**
* Get textures (usually skin and cape, or empty array) from UUID
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @return array|bool Array with profile and properties, FALSE on failure
*/
public static function getTextures($uuid) {
$profile = self::getSessionProfile($uuid);
if (is_array($profile) and array_key_exists('properties', $profile) and is_array($profile['properties']) and !empty($profile['properties']) and array_key_exists('value', $profile['properties'][0])) {
$json = base64_decode($profile['properties'][0]['value']);
if (!empty($json)) {
$textures = self::parseJson($json);
if (array_key_exists('textures', $textures) and is_array($textures['textures'])) {
return $textures['textures'];
}
}
}
return false;
}
/**
* Get skin url of player
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @return string|null|bool Skin url, NULL if he hasn't a skin, FALSE on failure
*/
public static function getSkinUrl($uuid) {
$textures = self::getTextures($uuid);
if (is_array($textures)) {
if (array_key_exists('SKIN', $textures) and array_key_exists('url', $textures['SKIN']) and !empty($textures['SKIN']['url']) and is_string($textures['SKIN']['url'])) {
return $textures['SKIN']['url'];
}
return null;
}
return false;
}
/**
* Get skin (in raw png) of player
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @return string|null|bool Skin picture, NULL if he hasn't a skin, FALSE on failure
*/
public static function getSkin($uuid) {
$skinUrl = self::getSkinUrl($uuid);
if (is_string($skinUrl)) {
return self::fetch($skinUrl);
}
return $skinUrl;
}
/**
* Get player head (in raw png) of player
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @param int $size in pixels
* @return string|null|bool Player head, NULL if he hasn't a skin, FALSE on failure
*/
public static function getPlayerHead($uuid, $size = 100) {
$skin = self::getSkin($uuid);
if (is_string($skin)) {
return self::getPlayerHeadFromSkin($skin, $size);
}
return $skin;
}
/**
* Get Steve skin (in raw png)
*
* @return string Steve skin
*/
public static function getSteveSkin() {
return base64_decode('iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAFDUlEQVR42u2a20sUURzH97G0LKMotPuWbVpslj1olJXdjCgyisow'
.'sSjzgrB0gSKyC5UF1ZNQWEEQSBQ9dHsIe+zJ/+nXfM/sb/rN4ZwZ96LOrnPgyxzP/M7Z+X7OZc96JpEISfWrFhK0YcU8knlozeJK'
.'unE4HahEqSc2nF6zSEkCgGCyb+82enyqybtCZQWAzdfVVFgBJJNJn1BWFgC49/VpwGVlD0CaxQiA5HSYEwBM5sMAdKTqygcAG9+8'
.'coHKY/XXAZhUNgDYuBSPjJL/GkzVVhAEU5tqK5XZ7cnFtHWtq/TahdSw2l0HUisr1UKIWJQBAMehDuqiDdzndsP2EZECAG1ZXaWM'
.'wOCODdXqysLf++uXUGv9MhUHIByDOijjdiSAoH3ErANQD73C7TXXuGOsFj1d4YH4OTJAEy8y9Hd0mCaeZ5z8dfp88zw1bVyiYhCL'
.'Og1ZeAqC0ybaDttHRGME1DhDeVWV26u17lRAPr2+mj7dvULfHw2q65fhQRrLXKDfIxkau3ZMCTGIRR3URR5toU38HbaPiMwUcKfB'
.'Akoun09PzrbQ2KWD1JJaqswjdeweoR93rirzyCMBCmIQizqoizZkm2H7iOgAcHrMHbbV9KijkUYv7qOn55sdc4fo250e+vUg4329'
.'/Xk6QB/6DtOws+dHDGJRB3XRBve+XARt+4hIrAF4UAzbnrY0ve07QW8uHfB+0LzqanMM7qVb+3f69LJrD90/1axiEIs6qIs21BTI'
.'ToewfcSsA+Bfb2x67OoR1aPPzu2i60fSNHRwCw221Suz0O3jO+jh6V1KyCMGse9721XdN5ePutdsewxS30cwuMjtC860T5JUKpXy'
.'KbSByUn7psi5l+juDlZYGh9324GcPKbkycaN3jUSAGxb46IAYPNZzW0AzgiQ5tVnzLUpUDCAbakMQXXrOtX1UMtHn+Q9/X5L4wgl'
.'7t37r85OSrx+TYl379SCia9KXjxRpiTjIZTBFOvrV1f8ty2eY/T7XJ81FQAwmA8ASH1ob68r5PnBsxA88/xAMh6SpqW4HRnLBrkO'
.'A9Xv5wPAZjAUgOkB+SHxgBgR0qSMh0zmZRsmwDJm1gFg2PMDIC8/nAHIMls8x8GgzOsG5WiaqREgYzDvpTwjLDy8NM15LpexDEA3'
.'LepjU8Z64my+8PtDCmUyRr+fFwA2J0eAFYA0AxgSgMmYBMZTwFQnO9RNAEaHOj2DXF5UADmvAToA2ftyxZYA5BqgmZZApDkdAK4m'
.'AKo8GzPlr8G8AehzMAyA/i1girUA0HtYB2CaIkUBEHQ/cBHSvwF0AKZFS5M0ZwMQtEaEAmhtbSUoDADH9ff3++QZ4o0I957e+zYA'
.'Mt6wHkhzpjkuAcgpwNcpA7AZDLsvpwiuOkBvxygA6Bsvb0HlaeKIF2EbADZpGiGzBsA0gnwQHGOhW2snRpbpPexbAB2Z1oicAMQp'
.'TnGKU5ziFKc4xSlOcYpTnOIUpzgVmgo+XC324WfJAdDO/+ceADkCpuMFiFKbApEHkOv7BfzfXt+5gpT8V7rpfYJcDz+jAsB233r6'
.'yyBsJ0mlBCDofuBJkel4vOwBFPv8fyYAFPJ+wbSf/88UANNRVy4Awo6+Ig2gkCmgA5DHWjoA+X7AlM//owLANkX0w0359od++pvX'
.'8fdMAcj3/QJ9iJsAFPQCxHSnQt8vMJ3v2wCYpkhkAOR7vG7q4aCXoMoSgG8hFAuc/grMdAD4B/kHl9da7Ne9AAAAAElFTkSuQmCC');
}
/**
* Get Alex skin (in raw png)
*
* @return string Alex skin
*/
public static function getAlexSkin() {
return base64_decode('iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAZiS0dEAIwAuACKS3UjegAAAAlwSFlzAAAL'
.'EwAACxMBAJqcGAAAAAd0SU1FB94IFA0kCPwApjEAAAAWaVRYdENvbW1lbnQAAAAAAG1vZGVsPXNsaW1TpLy5AAAIAElEQVR42uWa'
.'728URRjH968wanznj5DoC9BASIopJFKgHFIbCUmlNQVLGhoDoqRa0rQaAgJFDRoSEuGNJmITrVrPKIgmGo0EXlg1/CYkBIwoUOUt'
.'L9b7Pt3v9NnnZq/X2722XDf5ZmZndu/2+5lnZnd2Nggm2K4dXBaK9tWHRflCerb/8ZIKUm6XD+bGNLC0OF9IR/oWllTqTZslgIvv'
.'LpL0r0M5Z3Rwd1tMmQFQZgng9P4lkl451OyMfrS7PabsAESGYZYgBEAEhObf394UEyFkEQEwDLMEIQAiIDR/sKc5JkLIJAJiAFQ3'
.'QHnVAUSGHQDVDVA+JQAgAaC6A/YB4PLPH1QdACQAVHfAPgBc+unDKgMwg57u/1DVAZhBT/d/qOoAXH/XdwEVBQTgUxYAXH/XdwEV'
.'BQTgUyYA9ODHVrddoJTS/r8e/NjqtguUUtkhTqM0afu8Sz23Rd9xGpwv5e9rczq0bZ93qee26DtOg/Ol/P0g0UjChXMMsGaSoLly'
.'TxShLNFIwoVzDLBmkqC5ck8UoSwodeHWKMz/8/uwKNbSBGZa3oLRUcbfLHXh1ijMX/8tL4q1NIGZlrdgdJQ5ALZV2bdZxjzN3z5/'
.'NLx68mORHhTdb+hWNpB83cK2qr0D6A3G/z33bcljYq1sIPm6RRALadPKyMPonTt3wgfXPyTmb1/6QfIoQ51+Kiw1JmjAOh8L6cgM'
.'Wxnby40PBIX/CuZvX+BMIo8y1OnHZd9t044XOrKQd7c5try0csGkmC3o1umvwtFzx5x5SS8cD2+d+UbyBOU16ukWsf2CeJvjqC6t'
.'fPFHaWnb2iy3UeEgWqOebhHbly5g+jzDXMzScJSOHvtcRDiE5SAkjPR6ULVji+3zDHMxSxBRevP4sIhwCMtBSBjp9aBqx5bYkx5D'
.'X0eAky5TcIrMT3Rb5HjBByl1wQx9HQFOukzBKTI/0W2RY05UVrS9sOSxUGvOnDkxTfRc0ZPvDnfuai96L0AAnUvnltSEvz/UHVJn'
.'j74nDYD0mXkPB1DqLSsAWgDA8M8EQP4uAcDHYeSrAYDmkW5aPDeAZgwAN5gWpMeGLLtAJgAc0YgqLuK1NU+G/S2Lwxcbn5B9gGA6'
.'8N0eUe8XPSJ3bnQ+6iwA+zTonXeY2+jr+b5w79E3Q93i/H2Uy3+ba0c564OhoUBUTovzx2Fy69MLBACkzTO/OTdf4EA4Vl8AL3DP'
.'3o0xAIwCfSfg8XbuAeM07wBQ+W5nHv9j/5vnoG5SALRRGiSMpHoAEgCeFpIL3Nc5Pm9QzwDWEA1TbMEYABVlupVtBLB80gAo3Rdp'
.'cLIAuoe2uW4CCBDNx7pMdJ6+aG0cxyLtf6Ml3LV7vQh5dK+33+mStFRdRQAY4jvbGkTI23o7aNkW0gAYqmL+yx5vSPsAOIAFXT1x'
.'JCZGFVSqbrB3YwBNuNXV1YUUDb767CIRW14f09DQEFMsAgopTWtT2lwMwFC8T2sATK05/RiuQbCOadkAVqxYEVIw2NLSEra2toqQ'
.'R5k+ZtWqVeHatWtFyFsAtvWtIdtldIszz3MJQJslgOu/Dsn+jT++LsxKByXVj+oVAYDa29vDrq4uEfK2vghAPt4FeOHsCtpMDIA5'
.'Xh+rz4ExGkV6fWQcwui541J+7eQn4ej578fhjAxPHgBb2gdAR4EFcO/Ce8L76+9z6er+XAyAHhNQh2N8x8uxn70SOwd1MKglAKLJ'
.'GPZhnHVukjaZCIAJqLm52XWBDRs2iNgFUMfjGhsbw6amJhHyOvyRwhilzXJfD4CQrddwIJqDUWllmrz8SzGAQhnq/i7kD2x+LoAm'
.'BWDdunVFEYCyyQCwfZ63s9gY4Dnejhssp3GaFZORUdaxnuVQ2QDq6+tDCmbR8lu2bBEhjzJ9zPLly8NcLidC3vc4irvHtqaFMaFM'
.'7gL5ZABWqBPjUV93AFQX0FFQEYDp2jo6Oio7cWAgCAYHJcXkh9PgzKbDM36D+VOnJOUMcHYCKEQATRNEJu8DZvymuoA1X5sA2tqC'
.'YNOmce3YMQYB0uXQ/v1j6ukZ112/rVwZBJ2dY+rrG5vhHTkSBIcPj+1Dug4iCKgmIkADgHkanZUA0PIwOVsiYOtTjwYHnl8i2rWm'
.'LuAzPoR9iPUsf6tzjVNNAsATnqSqvGYBdNQ94gUgmm0AtEkLQHePmgRgW1kDsONDTY4BFoAeA2x09LbmnGoaQNJdoCYBUDCF0Eb6'
.'0rJ5Qe/qBU4sn1YAaRdXZfXIs/xd9mSHk6Voujzl7wvSAtBLaBUBgPkIxLS8L8gCANYS7fJ32QYIoJBOy/uC1ABg3vMBRNkApvp9'
.'gf1+AN8UcPFUL6vr5XW98KFfkLL1Sy2j4Rwc4yY+0/2+wK4ec2XZri4nfl+gDPL7gaKl8VLr/3hfwI0zQkYCZ4u6LuvZogUAU0hh'
.'kKvLZS2vm/V/3xcg3vV/RIAGgPcF2PR0udoA9NI5vzDxhb8PQOr1fwsAxrHpFyZTBaCS7wvSrv/jQYkb5wOMAD4pYqvabDHt9wVp'
.'1/+TAOi5QlUBpP2+IGn9n2b18rhv/R+zxRkDoJLvC7RBrAHq9X+WY/0/CcCVM58GN/88IUL+6oXh4L8bI5JyX+dZlzmASr8vmGj9'
.'P6nOFwFsZcmr9wU6OjKPgLTfFxSt8EYGYTapzkZAUiuXqststpj2+wK99u9MqvV/DcHVFeQbBHUE6PcFdgzIdLqc9vsCGGNft2v8'
.'3k9gPAB87wtQV877gv8BjY2wPg7jcKEAAAAASUVORK5CYII=');
}
/**
* Get Steve head (in raw png)
*
* @return string Steve head
*/
public static function getSteveHead($size = 100) {
return self::getPlayerHeadFromSkin(self::getSteveSkin(), $size);
}
/**
* Get Alex head (in raw png)
*
* @return string Alex head
*/
public static function getAlexHead($size = 100) {
return self::getPlayerHeadFromSkin(self::getAlexSkin(), $size);
}
/**
* Get player head (in raw png) from skin
*
* @param string $skin returned by getSkin($uuid)
* @param int $size in pixels
* @return string|bool Player head, FALSE on failure
*/
public static function getPlayerHeadFromSkin($skin, $size = 100) {
if (is_string($skin)) {
$im = @imagecreatefromstring($skin);
if (is_resource($im)) {
$av = imagecreatetruecolor($size, $size);
imagecopyresized($av, $im, 0, 0, 8, 8, $size, $size, 8, 8); // Face
imagecolortransparent($im, imagecolorat($im, 63, 0)); // Black Hat Issue
imagecopyresized($av, $im, 0, 0, 8 + 32, 8, $size, $size, 8, 8); // Accessories
ob_start();
imagepng($av);
$img = ob_get_clean();
imagedestroy($im);
imagedestroy($av);
return $img;
}
}
return false;
}
/**
* Print image from raw png
*
* Nothing should be displayed on the page other than this image
*
* @param string $img
* @param int $cache in seconds, 0 to disable
*/
public static function printImage($img, $cache = 86400) {
header('Content-type: image/png');
header('Pragma: public');
header('Cache-Control: max-age=' . $cache);
header('Expires: '. gmdate('D, d M Y H:i:s \G\M\T', time() + $cache));
echo $img;
}
/**
* Embed image for <img> tag
*
* @param string $img
* @return string embed image
*/
public static function embedImage($img) {
return substr($img, 0, strlen('data:image')) === 'data:image' ? $img : 'data:image/png;base64,' . base64_encode($img);
}
/**
* Authenticate with a Minecraft account
*
* After a few fails, Mojang server will deny all requests !
*
* @param string $id Minecraft username or Mojang email
* @param string $password Account's password
* @return array|bool Array with id and name, FALSE if authentication failed
*/
public static function authenticate($id, $password) {
if (!function_exists('curl_init') or !extension_loaded('curl')) {
return false;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://authserver.mojang.com/authenticate');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array(
'agent' => array(
'name' => 'Minecraft',
'version' => 1,
),
'username' => $id,
'password' => $password
)));
$output = curl_exec($ch);
curl_close($ch);
$json = self::parseJson($output);
if (is_array($json) and array_key_exists('selectedProfile', $json) and is_array($json['selectedProfile'])
and array_key_exists('id', $json['selectedProfile']) and array_key_exists('name', $json['selectedProfile'])) {
return array(
'id' => $json['selectedProfile']['id'],
'name' => $json['selectedProfile']['name']
);
}
return false;
}
/**
* Query a Minecraft server
*
* @see https://github.com/xPaw/PHP-Minecraft-Query/
*
* @param string $address Server's address
* @param int $port Server's port, default is 25565
* @param int $timeout Timeout (in seconds), default is 2
* @return array|bool Array with query result, FALSE if query failed
*/
public static function query($address, $port = 25565, $timeout = 2) {
// Check arguments and if functions exists
if (!is_numeric($timeout) or $timeout < 0 or !function_exists('fsockopen')) return false;
// Try to connect
$start = microtime(true);
$socket = @fsockopen('udp://' . $address, (int) $port, $errNo, $errStr, $timeout);
if ($errNo or $socket === false) return false;
// Set read/write timeout
stream_set_timeout($socket, $timeout);
stream_set_blocking($socket, true);
// Send handshake
$data = self::queryWriteData($socket, 0x09);
if ($data === false) return false;
// And query
$challenge = pack('N', $data);
$data = self::queryWriteData($socket, 0x00, $challenge . pack('c*', 0x00, 0x00, 0x00, 0x00));
if (!$data) return false;
$last = '';
$info = array();
// Extract data
$data = substr($data, 11);
$data = explode("\x00\x00\x01player_\x00\x00", $data);
if (count($data) !== 2) return false;
$players = substr($data[1], 0, -2);
$data = explode("\x00", $data[0]);
// Array with known keys in order to validate the result
$keys = array(
'hostname' => 'motd',
'gametype' => 'gametype',
'version' => 'version',
'plugins' => 'plugins',
'map' => 'map',
'numplayers' => 'players',
'maxplayers' => 'maxplayers',
'hostport' => 'hostport',
'hostip' => 'hostip',
'game_id' => 'gamename'
);
$mb_convert_encoding = function_exists('mb_convert_encoding');
foreach ($data as $key => $value) {
if (~$key & 1) {
if (!array_key_exists($value, $keys)) {
$last = false;
continue;
}
$last = $keys[$value];
$info[$last] = '';
} else if ($last !== false) {
if (strlen($value)) {
$info[$last] = $mb_convert_encoding ? mb_convert_encoding($value, 'UTF-8') : $value;
} else {
$info[$last] = null;
}
}
}
// Ints
$info['players'] = intval($info['players']);
$info['maxplayers'] = intval($info['maxplayers']);
$info['hostport'] = intval($info['hostport']);
// Parse "plugins" if any
if ($info['plugins']) {
$data = explode(": ", $info['plugins'], 2);
$info['rawplugins'] = $info['plugins'];
$info['software'] = $data[0];
if (count($data) == 2) {
$info['plugins'] = explode("; ", $data[1]);
}
}
if (empty($players)) {
$info['playerlist'] = null;
} else {
$info['playerlist'] = explode("\x00", $players);
}
$info['timeout'] = microtime(true) - $start;
fclose($socket);
return $info;
}
/**
* Ping a Minecraft server
*
* @see https://github.com/xPaw/PHP-Minecraft-Query/
*
* @param string $address Server's address
* @param int $port Server's port, default is 25565
* @param int $timeout Timeout (in seconds), default is 2
* @return array|bool Array with query result, FALSE if query failed
*/
public static function ping($address, $port = 25565, $timeout = 2) {
// Check arguments and if functions exists
if (!is_numeric($timeout) or $timeout < 0 or !function_exists('fsockopen')) return false;
// Try to connect
$start = microtime(true);
$socket = @fsockopen($address, (int) $port, $errNo, $errStr, $timeout);
if ($errNo or $socket === false) return false;
// Set read/write timeout
stream_set_timeout($socket, $timeout);
// See http://wiki.vg/Protocol (Status Ping)
$packet = "\x00\x04" . pack('c', strlen($address)) . $address . pack('n', $port) . "\x01";
$data = pack('c', strlen($packet)) . $packet;
// Send handshake and ping
fwrite($socket, $data);
fwrite($socket, "\x01\x00");
// Read response
$packetLength = self::pingReadVarInt($socket);
if ($packetLength === false or $packetLength < 10) return false;
fgetc($socket);
$length = self::pingReadVarInt($socket);
if ($length === false) return false;
$data = '';
do {
if (microtime(true) - $start > $timeout) return false;
$remainder = $length - strlen($data);
$block = fread($socket, $remainder);
if (!$block) return false;
$data .= $block;
} while (strlen($data) < $length);
$data = self::parseJson($data);
if (empty($data) or json_last_error() !== JSON_ERROR_NONE or !array_key_exists('players', $data) or !is_array($data['players'])
or !array_key_exists('online', $data['players'])) return false;
if (array_key_exists('description', $data) and is_array($data['description'])
and array_key_exists('extra', $data['description']) and empty($data['description']['text'])) {
$motd = '';
foreach ($data['description']['extra'] as $key => $value) {
$motd .= $value['text'];
}
$data['description']['text'] = $motd;
}
$data['timeout'] = microtime(true) - $start;
return $data;
}
/**
* Parse JSON
*
* @param string $json
* @return string|bool json data, FALSE on failure
*/
private static function parseJson($json) {
$data = json_decode($json, true);
return empty($data) ? false : $data;
}
/**
* Fetch url content
*
* @param string $url
* @return string|false content, FALSE on failure
*/
private static function fetch($url) {
if (function_exists('curl_init') and extension_loaded('curl')) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$output = curl_exec($ch);
curl_close($ch);
return $output;
} else {
return @file_get_contents($url);
}
}
/**
* Fetch url content as JSON
*
* @param string $url
* @return array|false json data, FALSE on failure
*/
private static function fetchJson($url) {
$output = self::fetch($url);
if (!empty($output)) {
$json = self::parseJson($output);
if (is_array($json) and !array_key_exists('error', $json)) {
return $json;
}
}
return false;
}
/**
* Write data in a socket for query
*
* @param socket $socket
* @param string $command
* @param string $append, default is ''
* @return string|false data, FALSE on failure
*/
private static function queryWriteData($socket, $command, $append = '') {
$command = pack('c*', 0xFE, 0xFD, $command, 0x01, 0x02, 0x03, 0x04) . $append;
$length = strlen($command);
if ($length !== fwrite($socket, $command, $length)) return false;
$data = fread($socket, 4096);
if ($data === false or strlen($data) < 5 or $data[0] != $command[2]) return false;
return substr($data, 5);
}
/**
* Read var int in a socket for ping
*
* @param socket $socket
* @return int|false var int, FALSE on failure
*/
private static function pingReadVarInt($socket) {
$i = $j = 0;
while (true) {
$k = @fgetc($socket);
if ($k === false) return false;
$k = ord($k);
$i |= ($k & 0x7F) << $j++ * 7;
if ($j > 5) return false;
if (($k & 0x80) != 128) break;
}
return $i;
}
}
<?php
/**
* Fast and easy way to access Mojang API
*
* This interface is NOT needed in your project
*
* @see http://wiki.vg/Mojang_API
*/
interface MojangAPI {
/**
* Get Mojang status
*
* @return array|bool Array with status, FALSE on failure
*/
public static function getStatus();
/**
* Get UUID from username, an optional time can be provided
*
* @param string $username
* @param int $time optional
* @return string|bool UUID (without dashes) on success, FALSE on failure
*/
public static function getUuid($username, $time = 0);
/**
* Get username from UUID
*
* @param string $uuid
* @return string|bool Username on success, FALSE on failure
*/
public static function getUsername($uuid);
/**
* Get profile (username and UUID) from username, an optional time can be provided
*
* @param string $username
* @param int $time optional
* @return array|bool Array with id and name, FALSE on failure
*/
public static function getProfile($username, $time = 0);
/**
* Get name history from UUID
*
* @param string $uuid
* @return array|bool Array with his username's history, FALSE on failure
*/
public static function getNameHistory($uuid);
/**
* Check if string is a valid Minecraft username
*
* @param string $string to check
* @return bool Whether username is valid or not
*/
public static function isValidUsername($string);
/**
* Check if string is a valid UUID, with or without dashes
*
* @param string $string to check
* @return bool Whether UUID is valid or not
*/
public static function isValidUuid($string);
/**
* Remove dashes from UUID
*
* @param string $uuid
* @return string|bool UUID without dashes (32 chars), FALSE on failure
*/
public static function minifyUuid($uuid);
/**
* Add dashes to an UUID
*
* @param string $uuid
* @return string|bool UUID with dashes (36 chars), FALSE on failure
*/
public static function formatUuid($uuid);
/**
* Check if username is Alex or Steve is a valid UUID, with or without dashes
*
* @param string $uuid
* @return bool|null TRUE if Alex, FALSE if Steve, NULL on error
*/
public static function isAlex($uuid);
/**
* Get profile (username and UUID) from UUID
*
* This has a rate limit to 1 per minute per profile
*
* @param string $uuid
* @return array|bool Array with profile and properties, FALSE on failure
*/
public static function getSessionProfile($uuid);
/**
* Get textures (usually skin and cape, or empty array) from UUID
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @return array|bool Array with profile and properties, FALSE on failure
*/
public static function getTextures($uuid);
/**
* Get skin url of player
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @return string|null|bool Skin url, NULL if he hasn't a skin, FALSE on failure
*/
public static function getSkinUrl($uuid);
/**
* Get skin (in raw png) of player
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @return string|null|bool Skin picture, NULL if he hasn't a skin, FALSE on failure
*/
public static function getSkin($uuid);
/**
* Get player head (in raw png) of player
*
* This has a rate limit to 1 per minute per profile
* @see getSessionProfile($uuid)
*
* @param string $uuid
* @param int $size in pixels
* @return string|null|bool Player head, NULL if he hasn't a skin, FALSE on failure
*/
public static function getPlayerHead($uuid, $size = 100);
/**
* Get Steve skin (in raw png)
*
* @return string Steve skin
*/
public static function getSteveSkin();
/**
* Get Alex skin (in raw png)
*
* @return string Alex skin
*/
public static function getAlexSkin();
/**
* Get Steve head (in raw png)
*
* @return string Steve head
*/
public static function getSteveHead($size = 100);
/**
* Get Alex head (in raw png)
*
* @return string Alex head
*/
public static function getAlexHead($size = 100);
/**
* Get player head (in raw png) from skin
*
* @param string $skin returned by getSkin($uuid)
* @param int $size in pixels
* @return string|bool Player head, FALSE on failure
*/
public static function getPlayerHeadFromSkin($skin, $size = 100);
/**
* Print image from raw png
*
* Nothing should be displayed on the page other than this image
*
* @param string $img
* @param int $cache in seconds, 0 to disable
*/
public static function printImage($img, $cache = 86400);
/**
* Embed image for <img> tag
*
* @param string $img
* @return string embed image
*/
public static function embedImage($img);
/**
* Authenticate with a Minecraft account
*
* After a few fails, Mojang server will deny all requests !
*
* @param string $id Minecraft username or Mojang email
* @param string $password Account's password
* @return array|bool Array with id and name, FALSE if authentication failed
*/
public static function authenticate($id, $password);
/**
* Query a Minecraft server
*
* @see https://github.com/xPaw/PHP-Minecraft-Query/
*
* @param string $address Server's address
* @param int $port Server's port, default is 25565
* @param int $timeout Timeout (in seconds), default is 2
* @return array|bool Array with query result, FALSE if query failed
*/
public static function query($address, $port = 25565, $timeout = 2);
/**
* Ping a Minecraft server
*
* @see https://github.com/xPaw/PHP-Minecraft-Query/
*
* @param string $address Server's address
* @param int $port Server's port, default is 25565
* @param int $timeout Timeout (in seconds), default is 2
* @return array|bool Array with query result, FALSE if query failed
*/
public static function ping($address, $port = 25565, $timeout = 2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment