tronds

Minecraft Query

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