Advertisement
Guest User

Untitled

a guest
May 26th, 2014
266
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.11 KB | None | 0 0
  1. <?php
  2. class MinecraftQueryException extends Exception
  3. {
  4. // Exception thrown by MinecraftQuery class
  5. }
  6.  
  7. class MinecraftQueryD
  8. {
  9. /*
  10. * Class written by xPaw
  11. *
  12. * Website: http://xpaw.ru
  13. * GitHub: https://github.com/xPaw/PHP-Minecraft-Query
  14. */
  15.  
  16. const STATISTIC = 0x00;
  17. const HANDSHAKE = 0x09;
  18.  
  19. private $Socket;
  20. private $Players;
  21. private $Info;
  22.  
  23. public function Connect( $Ip, $Port = 25565, $Timeout = 3 )
  24. {
  25. if( !is_int( $Timeout ) || $Timeout < 0 )
  26. {
  27. throw new InvalidArgumentException( 'Timeout must be an integer.' );
  28. }
  29.  
  30. $this->Socket = @FSockOpen( 'udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, $Timeout );
  31.  
  32. if( $ErrNo || $this->Socket === false )
  33. {
  34. throw new MinecraftQueryException( 'Could not create socket: ' . $ErrStr );
  35. }
  36.  
  37. Stream_Set_Timeout( $this->Socket, $Timeout );
  38. Stream_Set_Blocking( $this->Socket, true );
  39.  
  40. try
  41. {
  42. $Challenge = $this->GetChallenge( );
  43.  
  44. $this->GetStatus( $Challenge );
  45. }
  46. // We catch this because we want to close the socket, not very elegant
  47. catch( MinecraftQueryException $e )
  48. {
  49. FClose( $this->Socket );
  50.  
  51. throw new MinecraftQueryException( $e->getMessage( ) );
  52. }
  53.  
  54. FClose( $this->Socket );
  55. }
  56.  
  57. public function GetInfo( )
  58. {
  59. return isset( $this->Info ) ? $this->Info : false;
  60. }
  61.  
  62. public function GetPlayers( )
  63. {
  64. return isset( $this->Players ) ? $this->Players : false;
  65. }
  66.  
  67. private function GetChallenge( )
  68. {
  69. $Data = $this->WriteData( self :: HANDSHAKE );
  70.  
  71. if( $Data === false )
  72. {
  73. throw new MinecraftQueryException( "Failed to receive challenge." );
  74. }
  75.  
  76. return Pack( 'N', $Data );
  77. }
  78.  
  79. private function GetStatus( $Challenge )
  80. {
  81. $Data = $this->WriteData( self :: STATISTIC, $Challenge . Pack( 'c*', 0x00, 0x00, 0x00, 0x00 ) );
  82.  
  83. if( !$Data )
  84. {
  85. throw new MinecraftQueryException( "Failed to receive status." );
  86. }
  87.  
  88. $Last = "";
  89. $Info = Array( );
  90.  
  91. $Data = SubStr( $Data, 11 ); // splitnum + 2 int
  92. $Data = Explode( "\x00\x00\x01player_\x00\x00", $Data );
  93. $Players = SubStr( $Data[ 1 ], 0, -2 );
  94. $Data = Explode( "\x00", $Data[ 0 ] );
  95.  
  96. // Array with known keys in order to validate the result
  97. // It can happen that server sends custom strings containing bad things (who can know!)
  98. $Keys = Array(
  99. 'hostname' => 'HostName',
  100. 'gametype' => 'GameType',
  101. 'version' => 'Version',
  102. 'plugins' => 'Plugins',
  103. 'map' => 'Map',
  104. 'numplayers' => 'Players',
  105. 'maxplayers' => 'MaxPlayers',
  106. 'hostport' => 'HostPort',
  107. 'hostip' => 'HostIp'
  108.  
  109. );
  110.  
  111. foreach( $Data as $Key => $Value )
  112. {
  113. if( ~$Key & 1 )
  114. {
  115. if( !Array_Key_Exists( $Value, $Keys ) )
  116. {
  117. $Last = false;
  118. continue;
  119. }
  120.  
  121. $Last = $Keys[ $Value ];
  122. $Info[ $Last ] = "";
  123. }
  124. else if( $Last != false )
  125. {
  126. $Info[ $Last ] = $Value;
  127. }
  128. }
  129.  
  130. // Ints
  131. $Info[ 'Players' ] = IntVal( $Info[ 'Players' ] );
  132. $Info[ 'MaxPlayers' ] = IntVal( $Info[ 'MaxPlayers' ] );
  133. $Info[ 'HostPort' ] = IntVal( $Info[ 'HostPort' ] );
  134.  
  135. // Parse "plugins", if any
  136. if( $Info[ 'Plugins' ] )
  137. {
  138. $Data = Explode( ": ", $Info[ 'Plugins' ], 2 );
  139.  
  140. $Info[ 'RawPlugins' ] = $Info[ 'Plugins' ];
  141. $Info[ 'Software' ] = $Data[ 0 ];
  142.  
  143. if( Count( $Data ) == 2 )
  144. {
  145. $Info[ 'Plugins' ] = Explode( "; ", $Data[ 1 ] );
  146. }
  147. }
  148. else
  149. {
  150. $Info[ 'Software' ] = 'Vanilla';
  151. }
  152.  
  153. $this->Info = $Info;
  154.  
  155. if( $Players )
  156. {
  157. $this->Players = Explode( "\x00", $Players );
  158. }
  159. }
  160.  
  161. private function WriteData( $Command, $Append = "" )
  162. {
  163. $Command = Pack( 'c*', 0xFE, 0xFD, $Command, 0x01, 0x02, 0x03, 0x04 ) . $Append;
  164. $Length = StrLen( $Command );
  165.  
  166. if( $Length !== FWrite( $this->Socket, $Command, $Length ) )
  167. {
  168. throw new MinecraftQueryException( "Failed to write on socket." );
  169. }
  170.  
  171. $Data = FRead( $this->Socket, 2048 );
  172.  
  173. if( $Data === false )
  174. {
  175. throw new MinecraftQueryException( "Failed to read from socket." );
  176. }
  177.  
  178. if( StrLen( $Data ) < 5 || $Data[ 0 ] != $Command[ 2 ] )
  179. {
  180. return false;
  181. }
  182.  
  183. return SubStr( $Data, 5 );
  184. }
  185. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement