Advertisement
Initionmc

Untitled

Jun 19th, 2017
55
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 4.17 KB | None | 0 0
  1. <?php
  2. class MinecraftQueryException extends Exception
  3. {
  4.     // Exception thrown by MinecraftQuery class
  5. }
  6.  
  7. class MinecraftQuery
  8. {
  9.    
  10.    
  11.     const STATISTIC = 0x00;
  12.     const HANDSHAKE = 0x09;
  13.    
  14.     private $Socket;
  15.     private $Players;
  16.     private $Info;
  17.    
  18.     public function Connect( $Ip, $Port = 25565, $Timeout = 3 )
  19.     {
  20.         if( !is_int( $Timeout ) || $Timeout < 0 )
  21.         {
  22.             throw new InvalidArgumentException( 'Timeout must be an integer.' );
  23.         }
  24.        
  25.         $this->Socket = @FSockOpen( 'udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, $Timeout );
  26.        
  27.         if( $ErrNo || $this->Socket === false )
  28.         {
  29.             throw new MinecraftQueryException( 'Could not create socket: ' . $ErrStr );
  30.         }
  31.        
  32.         Stream_Set_Timeout( $this->Socket, $Timeout );
  33.         Stream_Set_Blocking( $this->Socket, true );
  34.        
  35.         try
  36.         {
  37.             $Challenge = $this->GetChallenge( );
  38.            
  39.             $this->GetStatus( $Challenge );
  40.         }
  41.         // We catch this because we want to close the socket, not very elegant
  42.         catch( MinecraftQueryException $e )
  43.         {
  44.             FClose( $this->Socket );
  45.            
  46.             throw new MinecraftQueryException( $e->getMessage( ) );
  47.         }
  48.        
  49.         FClose( $this->Socket );
  50.     }
  51.    
  52.     public function GetInfo( )
  53.     {
  54.         return isset( $this->Info ) ? $this->Info : false;
  55.     }
  56.    
  57.     public function GetPlayers( )
  58.     {
  59.         return isset( $this->Players ) ? $this->Players : false;
  60.     }
  61.    
  62.     private function GetChallenge( )
  63.     {
  64.         $Data = $this->WriteData( self :: HANDSHAKE );
  65.        
  66.         if( $Data === false )
  67.         {
  68.             throw new MinecraftQueryException( 'Failed to receive challenge.' );
  69.         }
  70.        
  71.         return Pack( 'N', $Data );
  72.     }
  73.    
  74.     private function GetStatus( $Challenge )
  75.     {
  76.         $Data = $this->WriteData( self :: STATISTIC, $Challenge . Pack( 'c*', 0x00, 0x00, 0x00, 0x00 ) );
  77.        
  78.         if( !$Data )
  79.         {
  80.             throw new MinecraftQueryException( 'Failed to receive status.' );
  81.         }
  82.        
  83.         $Last = '';
  84.         $Info = Array( );
  85.        
  86.         $Data    = SubStr( $Data, 11 ); // splitnum + 2 int
  87.         $Data    = Explode( "\x00\x00\x01player_\x00\x00", $Data );
  88.        
  89.         if( Count( $Data ) !== 2 )
  90.         {
  91.             throw new MinecraftQueryException( 'Failed to parse server\'s response.' );
  92.         }
  93.        
  94.         $Players = SubStr( $Data[ 1 ], 0, -2 );
  95.         $Data    = Explode( "\x00", $Data[ 0 ] );
  96.        
  97.         // Array with known keys in order to validate the result
  98.         // It can happen that server sends custom strings containing bad things (who can know!)
  99.         $Keys = Array(
  100.             'hostname'   => 'HostName',
  101.             'gametype'   => 'GameType',
  102.             'version'    => 'Version',
  103.             'plugins'    => 'Plugins',
  104.             'map'        => 'Map',
  105.             'numplayers' => 'Players',
  106.             'maxplayers' => 'MaxPlayers',
  107.             'hostport'   => 'HostPort',
  108.             'hostip'     => 'HostIp'
  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