Guest User

Untitled

a guest
Sep 13th, 2020
48
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.36 KB | None | 0 0
  1. <?php
  2.  
  3. namespace xPaw;
  4.  
  5. defined('BASEPATH') OR exit('No direct script access allowed');
  6.  
  7. class MinecraftPing
  8. {
  9. /*
  10. * Queries Minecraft server
  11. * Returns array on success, false on failure.
  12. *
  13. * WARNING: This method was added in snapshot 13w41a (Minecraft 1.7)
  14. *
  15. * Written by xPaw
  16. *
  17. * Website: http://xpaw.me
  18. * GitHub: https://github.com/xPaw/PHP-Minecraft-Query
  19. *
  20. * ---------
  21. *
  22. * This method can be used to get server-icon.png too.
  23. * Something like this:
  24. *
  25. * $Server = new MinecraftPing( 'localhost' );
  26. * $Info = $Server->Query();
  27. * echo '<img width="64" height="64" src="' . Str_Replace( "\n", "", $Info[ 'favicon' ] ) . '">';
  28. *
  29. */
  30.  
  31. private $Socket;
  32. private $ServerAddress;
  33. private $ServerPort;
  34. private $Timeout;
  35.  
  36. public function __construct( $Address, $Port = 25565, $Timeout = 2, $ResolveSRV = true )
  37. {
  38. $this->ServerAddress = $Address;
  39. $this->ServerPort = (int)$Port;
  40. $this->Timeout = (int)$Timeout;
  41.  
  42. if( $ResolveSRV )
  43. {
  44. $this->ResolveSRV();
  45. }
  46.  
  47. $this->Connect( );
  48. }
  49.  
  50. public function __destruct( )
  51. {
  52. $this->Close( );
  53. }
  54.  
  55. public function Close( )
  56. {
  57. if( $this->Socket !== null )
  58. {
  59. fclose( $this->Socket );
  60.  
  61. $this->Socket = null;
  62. }
  63. }
  64.  
  65. public function Connect( )
  66. {
  67. $connectTimeout = $this->Timeout;
  68. $this->Socket = @fsockopen( $this->ServerAddress, $this->ServerPort, $errno, $errstr, $connectTimeout );
  69.  
  70. if( !$this->Socket )
  71. {
  72. $this->Socket = null;
  73.  
  74. throw new MinecraftPingException( "Failed to connect or create a socket: $errno ($errstr)" );
  75. }
  76.  
  77. // Set Read/Write timeout
  78. stream_set_timeout( $this->Socket, $this->Timeout );
  79. }
  80.  
  81. public function Query( )
  82. {
  83. $TimeStart = microtime(true); // for read timeout purposes
  84.  
  85. // See http://wiki.vg/Protocol (Status Ping)
  86. $Data = "\x00"; // packet ID = 0 (varint)
  87.  
  88. $Data .= "\x04"; // Protocol version (varint)
  89. $Data .= Pack( 'c', StrLen( $this->ServerAddress ) ) . $this->ServerAddress; // Server (varint len + UTF-8 addr)
  90. $Data .= Pack( 'n', $this->ServerPort ); // Server port (unsigned short)
  91. $Data .= "\x01"; // Next state: status (varint)
  92.  
  93. $Data = Pack( 'c', StrLen( $Data ) ) . $Data; // prepend length of packet ID + data
  94.  
  95. fwrite( $this->Socket, $Data ); // handshake
  96. fwrite( $this->Socket, "\x01\x00" ); // status ping
  97.  
  98. $Length = $this->ReadVarInt( ); // full packet length
  99.  
  100. if( $Length < 10 )
  101. {
  102. return FALSE;
  103. }
  104.  
  105. $this->ReadVarInt( ); // packet type, in server ping it's 0
  106.  
  107. $Length = $this->ReadVarInt( ); // string length
  108.  
  109. $Data = "";
  110. do
  111. {
  112. if (microtime(true) - $TimeStart > $this->Timeout)
  113. {
  114. throw new MinecraftPingException( 'Server read timed out' );
  115. }
  116.  
  117. $Remainder = $Length - StrLen( $Data );
  118. $block = fread( $this->Socket, $Remainder ); // and finally the json string
  119. // abort if there is no progress
  120. if (!$block)
  121. {
  122. throw new MinecraftPingException( 'Server returned too few data' );
  123. }
  124.  
  125. $Data .= $block;
  126. } while( StrLen($Data) < $Length );
  127.  
  128. if( $Data === FALSE )
  129. {
  130. throw new MinecraftPingException( 'Server didn\'t return any data' );
  131. }
  132.  
  133. $Data = JSON_Decode( $Data, true );
  134.  
  135. if( JSON_Last_Error( ) !== JSON_ERROR_NONE )
  136. {
  137. if( Function_Exists( 'json_last_error_msg' ) )
  138. {
  139. throw new MinecraftPingException( JSON_Last_Error_Msg( ) );
  140. }
  141. else
  142. {
  143. throw new MinecraftPingException( 'JSON parsing failed' );
  144. }
  145.  
  146. return FALSE;
  147. }
  148.  
  149. return $Data;
  150. }
  151.  
  152. public function QueryOldPre17( )
  153. {
  154. fwrite( $this->Socket, "\xFE\x01" );
  155. $Data = fread( $this->Socket, 512 );
  156. $Len = StrLen( $Data );
  157.  
  158. if( $Len < 4 || $Data[ 0 ] !== "\xFF" )
  159. {
  160. return FALSE;
  161. }
  162.  
  163. $Data = SubStr( $Data, 3 ); // Strip packet header (kick message packet and short length)
  164. $Data = iconv( 'UTF-16BE', 'UTF-8', $Data );
  165.  
  166. // Are we dealing with Minecraft 1.4+ server?
  167. if( $Data[ 1 ] === "\xA7" && $Data[ 2 ] === "\x31" )
  168. {
  169. $Data = Explode( "\x00", $Data );
  170.  
  171. return Array(
  172. 'HostName' => $Data[ 3 ],
  173. 'Players' => IntVal( $Data[ 4 ] ),
  174. 'MaxPlayers' => IntVal( $Data[ 5 ] ),
  175. 'Protocol' => IntVal( $Data[ 1 ] ),
  176. 'Version' => $Data[ 2 ]
  177. );
  178. }
  179.  
  180. $Data = Explode( "\xA7", $Data );
  181.  
  182. return Array(
  183. 'HostName' => SubStr( $Data[ 0 ], 0, -1 ),
  184. 'Players' => isset( $Data[ 1 ] ) ? IntVal( $Data[ 1 ] ) : 0,
  185. 'MaxPlayers' => isset( $Data[ 2 ] ) ? IntVal( $Data[ 2 ] ) : 0,
  186. 'Protocol' => 0,
  187. 'Version' => '1.3'
  188. );
  189. }
  190.  
  191. private function ReadVarInt( )
  192. {
  193. $i = 0;
  194. $j = 0;
  195.  
  196. while( true )
  197. {
  198. $k = @fgetc( $this->Socket );
  199.  
  200. if( $k === FALSE )
  201. {
  202. return 0;
  203. }
  204.  
  205. $k = Ord( $k );
  206.  
  207. $i |= ( $k & 0x7F ) << $j++ * 7;
  208.  
  209. if( $j > 5 )
  210. {
  211. throw new MinecraftPingException( 'VarInt too big' );
  212. }
  213.  
  214. if( ( $k & 0x80 ) != 128 )
  215. {
  216. break;
  217. }
  218. }
  219.  
  220. return $i;
  221. }
  222.  
  223. private function ResolveSRV()
  224. {
  225. if( ip2long( $this->ServerAddress ) !== false )
  226. {
  227. return;
  228. }
  229.  
  230. $Record = @dns_get_record( '_minecraft._tcp.' . $this->ServerAddress, DNS_SRV );
  231.  
  232. if( empty( $Record ) )
  233. {
  234. return;
  235. }
  236.  
  237. if( isset( $Record[ 0 ][ 'target' ] ) )
  238. {
  239. $this->ServerAddress = $Record[ 0 ][ 'target' ];
  240. }
  241.  
  242. if( isset( $Record[ 0 ][ 'port' ] ) )
  243. {
  244. $this->ServerPort = $Record[ 0 ][ 'port' ];
  245. }
  246. }
  247. }
Add Comment
Please, Sign In to add comment