├── LICENSE ├── README.md └── src ├── MinecraftPing.php ├── MinecraftPingException.php ├── MinecraftSkin.php ├── faces └── .gitkeep └── index.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 mgp25 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![dirt](http://i51.tinypic.com/2lx73o6.jpg)Minecraft Server Status 2 | 3 | This library can be used to query Minecraft servers for some basic information and player's skin. 4 | 5 | You will be able to retrieve: 6 | 7 | - Server's version 8 | - Number of players online 9 | - Server status 10 | - Server's description 11 | - Server's image 12 | - Mods installed in the server 13 | - Player's skin and face 14 | 15 | **Using:** `PHP-Minecraft-Query from xPaw` 16 | 17 | ### Examples 18 | ```php 19 | Query() ); 27 | } 28 | catch( MinecraftPingException $e ) 29 | { 30 | echo $e->getMessage(); 31 | } 32 | finally 33 | { 34 | $Query->Close(); 35 | } 36 | ``` 37 | 38 | ```php 39 | getFace('mgp25'); // image is downloaded and stored in /faces, path is returned 44 | ``` 45 | -------------------------------------------------------------------------------- /src/MinecraftPing.php: -------------------------------------------------------------------------------- 1 | Query(); 23 | * echo ''; 24 | * 25 | */ 26 | 27 | private $Socket; 28 | private $ServerAddress; 29 | private $ServerPort; 30 | private $Timeout; 31 | 32 | public function __construct( $Address, $Port = 25565, $Timeout = 2 ) 33 | { 34 | $this->ServerAddress = $Address; 35 | $this->ServerPort = (int)$Port; 36 | $this->Timeout = (int)$Timeout; 37 | 38 | $this->Connect( ); 39 | } 40 | 41 | public function __destruct( ) 42 | { 43 | $this->Close( ); 44 | } 45 | 46 | public function Close( ) 47 | { 48 | if( $this->Socket !== null ) 49 | { 50 | fclose( $this->Socket ); 51 | 52 | $this->Socket = null; 53 | } 54 | } 55 | 56 | public function Connect( ) 57 | { 58 | $connectTimeout = $this->Timeout; 59 | $this->Socket = @fsockopen( $this->ServerAddress, $this->ServerPort, $errno, $errstr, $connectTimeout ); 60 | 61 | if( !$this->Socket ) 62 | { 63 | throw new MinecraftPingException( "Failed to connect or create a socket: $errno ($errstr)" ); 64 | } 65 | 66 | // Set Read/Write timeout 67 | stream_set_timeout( $this->Socket, $this->Timeout ); 68 | } 69 | 70 | public function Query( ) 71 | { 72 | $TimeStart = microtime(true); // for read timeout purposes 73 | 74 | // See http://wiki.vg/Protocol (Status Ping) 75 | $Data = "\x00"; // packet ID = 0 (varint) 76 | 77 | $Data .= "\x04"; // Protocol version (varint) 78 | $Data .= Pack( 'c', StrLen( $this->ServerAddress ) ) . $this->ServerAddress; // Server (varint len + UTF-8 addr) 79 | $Data .= Pack( 'n', $this->ServerPort ); // Server port (unsigned short) 80 | $Data .= "\x01"; // Next state: status (varint) 81 | 82 | $Data = Pack( 'c', StrLen( $Data ) ) . $Data; // prepend length of packet ID + data 83 | 84 | fwrite( $this->Socket, $Data ); // handshake 85 | fwrite( $this->Socket, "\x01\x00" ); // status ping 86 | 87 | $Length = $this->ReadVarInt( ); // full packet length 88 | 89 | if( $Length < 10 ) 90 | { 91 | return FALSE; 92 | } 93 | 94 | fgetc( $this->Socket ); // packet type, in server ping it's 0 95 | 96 | $Length = $this->ReadVarInt( ); // string length 97 | 98 | $Data = ""; 99 | do 100 | { 101 | if (microtime(true) - $TimeStart > $this->Timeout) 102 | { 103 | throw new MinecraftPingException( 'Server read timed out' ); 104 | } 105 | 106 | $Remainder = $Length - StrLen( $Data ); 107 | $block = fread( $this->Socket, $Remainder ); // and finally the json string 108 | // abort if there is no progress 109 | if (!$block) 110 | { 111 | throw new MinecraftPingException( 'Server returned too few data' ); 112 | } 113 | 114 | $Data .= $block; 115 | } while( StrLen($Data) < $Length ); 116 | 117 | if( $Data === FALSE ) 118 | { 119 | throw new MinecraftPingException( 'Server didn\'t return any data' ); 120 | } 121 | 122 | $Data = JSON_Decode( $Data, true ); 123 | 124 | if( JSON_Last_Error( ) !== JSON_ERROR_NONE ) 125 | { 126 | if( Function_Exists( 'json_last_error_msg' ) ) 127 | { 128 | throw new MinecraftPingException( JSON_Last_Error_Msg( ) ); 129 | } 130 | else 131 | { 132 | throw new MinecraftPingException( 'JSON parsing failed' ); 133 | } 134 | 135 | return FALSE; 136 | } 137 | 138 | return $Data; 139 | } 140 | 141 | public function QueryOldPre17( ) 142 | { 143 | fwrite( $this->Socket, "\xFE\x01" ); 144 | $Data = fread( $this->Socket, 512 ); 145 | $Len = StrLen( $Data ); 146 | 147 | if( $Len < 4 || $Data[ 0 ] !== "\xFF" ) 148 | { 149 | return FALSE; 150 | } 151 | 152 | $Data = SubStr( $Data, 3 ); // Strip packet header (kick message packet and short length) 153 | $Data = iconv( 'UTF-16BE', 'UTF-8', $Data ); 154 | 155 | // Are we dealing with Minecraft 1.4+ server? 156 | if( $Data[ 1 ] === "\xA7" && $Data[ 2 ] === "\x31" ) 157 | { 158 | $Data = Explode( "\x00", $Data ); 159 | 160 | return Array( 161 | 'HostName' => $Data[ 3 ], 162 | 'Players' => IntVal( $Data[ 4 ] ), 163 | 'MaxPlayers' => IntVal( $Data[ 5 ] ), 164 | 'Protocol' => IntVal( $Data[ 1 ] ), 165 | 'Version' => $Data[ 2 ] 166 | ); 167 | } 168 | 169 | $Data = Explode( "\xA7", $Data ); 170 | 171 | return Array( 172 | 'HostName' => SubStr( $Data[ 0 ], 0, -1 ), 173 | 'Players' => isset( $Data[ 1 ] ) ? IntVal( $Data[ 1 ] ) : 0, 174 | 'MaxPlayers' => isset( $Data[ 2 ] ) ? IntVal( $Data[ 2 ] ) : 0, 175 | 'Protocol' => 0, 176 | 'Version' => '1.3' 177 | ); 178 | } 179 | 180 | private function ReadVarInt( ) 181 | { 182 | $i = 0; 183 | $j = 0; 184 | 185 | while( true ) 186 | { 187 | $k = @fgetc( $this->Socket ); 188 | 189 | if( $k === FALSE ) 190 | { 191 | return 0; 192 | } 193 | 194 | $k = Ord( $k ); 195 | 196 | $i |= ( $k & 0x7F ) << $j++ * 7; 197 | 198 | if( $j > 5 ) 199 | { 200 | throw new MinecraftPingException( 'VarInt too big' ); 201 | } 202 | 203 | if( ( $k & 0x80 ) != 128 ) 204 | { 205 | break; 206 | } 207 | } 208 | 209 | return $i; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/MinecraftPingException.php: -------------------------------------------------------------------------------- 1 | getSteveSkin(); 33 | } 34 | return $output; 35 | } 36 | 37 | public function getSteveSkin() 38 | { 39 | // Default Steve Skin: https://minecraft.net/images/steve.png 40 | $output = 'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAFDUlEQVR42u2a20sUURzH97G0LKMotPuWbVpslj1olJ'; 41 | $output .= 'XdjCgyisowsSjzgrB0gSKyC5UF1ZNQWEEQSBQ9dHsIe+zJ/+nXfM/sb/rN4ZwZ96LOrnPgyxzP/M7Z+X7OZc96JpE'; 42 | $output .= 'ISfWrFhK0YcU8knlozeJKunE4HahEqSc2nF6zSEkCgGCyb+82enyqybtCZQWAzdfVVFgBJJNJn1BWFgC49/VpwGVl'; 43 | $output .= 'D0CaxQiA5HSYEwBM5sMAdKTqygcAG9+8coHKY/XXAZhUNgDYuBSPjJL/GkzVVhAEU5tqK5XZ7cnFtHWtq/TahdSw2'; 44 | $output .= 'l0HUisr1UKIWJQBAMehDuqiDdzndsP2EZECAG1ZXaWMwOCODdXqysLf++uXUGv9MhUHIByDOijjdiSAoH3ErANQD7'; 45 | $output .= '3C7TXXuGOsFj1d4YH4OTJAEy8y9Hd0mCaeZ5z8dfp88zw1bVyiYhCLOg1ZeAqC0ybaDttHRGME1DhDeVWV26u17lR'; 46 | $output .= 'APr2+mj7dvULfHw2q65fhQRrLXKDfIxkau3ZMCTGIRR3URR5toU38HbaPiMwUcKfBAkoun09PzrbQ2KWD1JJaqswj'; 47 | $output .= 'deweoR93rirzyCMBCmIQizqoizZkm2H7iOgAcHrMHbbV9KijkUYv7qOn55sdc4fo250e+vUg4329/Xk6QB/6DtOws'; 48 | $output .= '+dHDGJRB3XRBve+XARt+4hIrAF4UAzbnrY0ve07QW8uHfB+0LzqanMM7qVb+3f69LJrD90/1axiEIs6qIs21BTITo'; 49 | $output .= 'ewfcSsA+Bfb2x67OoR1aPPzu2i60fSNHRwCw221Suz0O3jO+jh6V1KyCMGse9721XdN5ePutdsewxS30cwuMjtC86'; 50 | $output .= '0T5JUKpXyKbSByUn7psi5l+juDlZYGh9324GcPKbkycaN3jUSAGxb46IAYPNZzW0AzgiQ5tVnzLUpUDCAbakMQXXr'; 51 | $output .= 'OtX1UMtHn+Q9/X5L4wgl7t37r85OSrx+TYl379SCia9KXjxRpiTjIZTBFOvrV1f8ty2eY/T7XJ81FQAwmA8ASH1ob'; 52 | $output .= '68r5PnBsxA88/xAMh6SpqW4HRnLBrkOA9Xv5wPAZjAUgOkB+SHxgBgR0qSMh0zmZRsmwDJm1gFg2PMDIC8/nAHIMl'; 53 | $output .= 's8x8GgzOsG5WiaqREgYzDvpTwjLDy8NM15LpexDEA3LepjU8Z64my+8PtDCmUyRr+fFwA2J0eAFYA0AxgSgMmYBMZ'; 54 | $output .= 'TwFQnO9RNAEaHOj2DXF5UADmvAToA2ftyxZYA5BqgmZZApDkdAK4mAKo8GzPlr8G8AehzMAyA/i1girUA0HtYB2Ca'; 55 | $output .= 'IkUBEHQ/cBHSvwF0AKZFS5M0ZwMQtEaEAmhtbSUoDADH9ff3++QZ4o0I957e+zYAMt6wHkhzpjkuAcgpwNcpA7AZD'; 56 | $output .= 'LsvpwiuOkBvxygA6Bsvb0HlaeKIF2EbADZpGiGzBsA0gnwQHGOhW2snRpbpPexbAB2Z1oicAMQpTnGKU5ziFKc4xS'; 57 | $output .= 'lOcYpTnOIUpzgVmgo+XC324WfJAdDO/+ceADkCpuMFiFKbApEHkOv7BfzfXt+5gpT8V7rpfYJcDz+jAsB233r6yyB'; 58 | $output .= 'sJ0mlBCDofuBJkel4vOwBFPv8fyYAFPJ+wbSf/88UANNRVy4Awo6+Ig2gkCmgA5DHWjoA+X7AlM//owLANkX0w035'; 59 | $output .= '9od++pvX8fdMAcj3/QJ9iJsAFPQCxHSnQt8vMJ3v2wCYpkhkAOR7vG7q4aCXoMoSgG8hFAuc/grMdAD4B/kHl9da7'; 60 | $output .= 'Ne9AAAAAElFTkSuQmCC'; 61 | $output = base64_decode($output); 62 | 63 | return $output; 64 | } 65 | 66 | public function getFace($user = 'steve') 67 | { 68 | if (!file_exists(__DIR__ . '/faces/' . $user . '_face.png')) { 69 | $size = isset($_GET['s']) ? max(8, min(250, $_GET['s'])) : 48; 70 | $view = isset($_GET['v']) ? substr($_GET['v'], 0, 1) : 'f'; 71 | $view = in_array($view, array('f', 'l', 'r', 'b')) ? $view : 'f'; 72 | 73 | $skin = $this->get_skin($user); 74 | $im = imagecreatefromstring($skin); 75 | $av = imagecreatetruecolor($size, $size); 76 | 77 | $x = array('f' => 8, 'l' => 16, 'r' => 0, 'b' => 24); 78 | 79 | imagecopyresized($av, $im, 0, 0, $x[$view], 8, $size, $size, 8, 8); // Face 80 | imagecolortransparent($im, imagecolorat($im, 63, 0)); // Black Hat Issue 81 | imagecopyresized($av, $im, 0, 0, $x[$view] + 32, 8, $size, $size, 8, 8); // Accessories 82 | 83 | imagepng($av, __DIR__."/faces/" . $user . '_face.png'); 84 | 85 | imagedestroy($im); 86 | imagedestroy($av); 87 | } 88 | 89 | return "/faces/$user" . '_face.png'; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/faces/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mgp25/Minecraft-Server-Status/b1a8840dd78a61742cc6fef28d91e565cc370799/src/faces/.gitkeep -------------------------------------------------------------------------------- /src/index.php: -------------------------------------------------------------------------------- 1 | getFace($player); 19 | } 20 | try 21 | { 22 | $Query = new MinecraftPing( $SERVER_IP, $PORT ); 23 | 24 | $data = $Query->Query(); 25 | $status = 'online'; 26 | } 27 | catch( MinecraftPingException $e ) 28 | { 29 | echo $e->getMessage(); 30 | $status = 'offline'; 31 | } 32 | finally 33 | { 34 | $Query->Close(); 35 | } 36 | 37 | ?> 38 | 39 | 40 | 41 | 42 | Minecraft Server Status 43 | 44 | 45 | 46 | 47 | 48 | 49 | 54 | 60 | 61 | 62 |
63 |

Minecraft Server Status


64 |
65 |
66 |

General Information

67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 95 | 96 | 97 |
IP
Version
Players
Status Server is online"; } else { echo " Server is offline";}?>
Description
Server image'; ?>
Mods
98 |
99 |
100 |

Players

101 | 104 | "; 105 | } 106 | if($data['players'] > 0) { 107 | print_r($users); 108 | } 109 | else { 110 | echo "
There are no players online at the moment!
"; 111 | } 112 | ?> 113 |
114 |
115 |
116 | 117 | 118 | --------------------------------------------------------------------------------