jure98

sampquery

Oct 29th, 2025
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.93 KB | None | 0 0
  1. class QueryServer
  2. {
  3.   // Private variables used for the query-ing.
  4.   private $szServerIP;
  5.   private $iPort;
  6.   private $rSocketID;
  7.  
  8.   private $bStatus;
  9.  
  10.   // The __construct function gets called automatically
  11.   // by PHP once the class gets initialized.
  12.   function __construct( $szServerIP, $iPort )
  13.   {
  14.       $this->szServerIP = $this->VerifyAddress( $szServerIP );
  15.       $this->iPort = $iPort;
  16.  
  17.       if (empty( $this->szServerIP ) || !is_numeric( $iPort )) {
  18.           throw new QueryServerException( 'Either the ip-address or the port isn\'t filled in correctly.' );
  19.       }
  20.  
  21.       $this->rSocketID = @fsockopen( 'udp://' . $this->szServerIP, $iPort, $iErrorNo, $szErrorStr, 5 );
  22.       if (!$this->rSocketID) {
  23.           throw new QueryServerException( 'Cannot connect to the server: ' . $szErrorStr );
  24.       }
  25.  
  26.       socket_set_timeout( $this->rSocketID, 0, 500000 );
  27.       $this->bStatus = true;
  28.   }
  29.  
  30.   // The VerifyAddress function verifies the given hostname/
  31.   // IP address and returns the actual IP Address.
  32.   function VerifyAddress( $szServerIP )
  33.   {
  34.       if (ip2long( $szServerIP ) !== false &&
  35.         long2ip( ip2long( $szServerIP ) ) == $szServerIP ) {
  36.           return $szServerIP;
  37.       }
  38.  
  39.       $szAddress = gethostbyname( $szServerIP );
  40.       if ($szAddress == $szServerIP) {
  41.           return "";
  42.       }
  43.  
  44.       return $szAddress;
  45.   }
  46.  
  47.   // The SendPacket function sends a packet to the server which
  48.   // requests information, based on the type of packet send.
  49.   function SendPacket( $cPacket )
  50.   {
  51.       $szPacket = 'SAMP';
  52.       $aIpChunks = explode( '.', $this->szServerIP );
  53.  
  54.       foreach( $aIpChunks as $szChunk ) {
  55.           $szPacket .= chr( $szChunk );
  56.       }
  57.  
  58.       $szPacket .= chr( $this->iPort & 0xFF );
  59.       $szPacket .= chr( $this->iPort >> 8 & 0xFF );
  60.       $szPacket .= $cPacket;
  61.  
  62.       return fwrite( $this->rSocketID, $szPacket, strlen( $szPacket ) );
  63.   }
  64.  
  65.   // The GetPacket() function returns a specific number of bytes
  66.   // read from the socket. This uses a special way of getting stuff.
  67.   function GetPacket( $iBytes )
  68.   {
  69.       $iResponse = fread( $this->rSocketID, $iBytes );
  70.       if ($iResponse === false) {
  71.           throw new QueryServerException( 'Connection to ' . $this->szServerIP . ' failed or has dropped.' );
  72.       }
  73.  
  74.       $iLength = ord( $iResponse );
  75.       if ($iLength > 0)
  76.           return fread( $this->rSocketID, $iLength );
  77.  
  78.       return "";
  79.   }
  80.  
  81.   // After we're done, the connection needs to be closed using
  82.   // the Close() function. Otherwise stuff might go wrong.
  83.   function Close( )
  84.   {
  85.       if ($this->rSocketID !== false) {
  86.           fclose( $this->rSocketID );
  87.       }
  88.   }
  89.  
  90.   // A little function that's needed to properly convert the
  91.   // four bytes we're recieving to integers to an actual PHP
  92.   // integer. ord() can't handle value's higher then 255.
  93.   function toInteger( $szData )
  94.   {
  95.       $iInteger = 0;
  96.  
  97.       $iInteger += ( ord( @$szData[ 0 ] ) );
  98.       $iInteger += ( ord( @$szData[ 1 ] ) << 8 );
  99.       $iInteger += ( ord( @$szData[ 2 ] ) << 16 );
  100.       $iInteger += ( ord( @$szData[ 3 ] ) << 24 );
  101.  
  102.       if( $iInteger >= 4294967294 )
  103.           $iInteger -= 4294967296;
  104.  
  105.       return $iInteger;
  106.   }
  107.  
  108.   // The GetInfo() function returns basic information about the
  109.   // server, like the hostname, number of players online etc.
  110.   function GetInfo( )
  111.   {
  112.       if ($this->SendPacket('i') === false) {
  113.           throw new QueryServerException( 'Connection to ' . $this->szServerIP . ' failed or has dropped.' );
  114.       }
  115.  
  116.       $szFirstData = fread( $this->rSocketID, 4 );
  117.       if (empty( $szFirstData ) || $szFirstData != 'SAMP') {
  118.           throw new QueryServerException( 'The server at ' . $this->szServerIP . ' is not an SA-MP Server.' );
  119.       }
  120.  
  121.       // Pop the first seven characters returned.
  122.       fread( $this->rSocketID, 7 );
  123.  
  124.       return array (
  125.           'Password'  =>  ord( fread( $this->rSocketID, 1 ) ),
  126.           'Players'  =>  $this->toInteger( fread( $this->rSocketID, 2 ) ),
  127.           'MaxPlayers' =>  $this->toInteger( fread( $this->rSocketID, 2 ) ),
  128.           'Hostname'  =>  $this->GetPacket( 4 ),
  129.           'Gamemode'  =>  $this->GetPacket( 4 ),
  130.           'Map'    =>  $this->GetPacket( 4 )
  131.       );
  132.   }
  133.  
  134.   // The GetRules() function returns the rules which are set
  135.   // on the server, e.g. the gravity, version etcetera.
  136.   function GetRules( )
  137.   {
  138.       if ($this->SendPacket('r') === false) {
  139.           throw new QueryServerException( 'Connection to ' . $this->szServerIP . ' failed or has dropped.' );
  140.       }
  141.  
  142.       // Pop the first 11 bytes from the response;
  143.       fread( $this->rSocketID, 11 );
  144.  
  145.       $iRuleCount = ord( fread( $this->rSocketID, 2 ) );
  146.       $aReturnArray = array( );
  147.  
  148.       for( $i = 0; $i < $iRuleCount; $i ++ ) {
  149.           $szRuleName = $this->GetPacket( 1 );
  150.           $aReturnArray[ $szRuleName ] = $this->GetPacket( 1 );
  151.       }
  152.  
  153.       return $aReturnArray;
  154.   }
  155.  
  156.   // The GetPlayers() function is pretty much simelar to the
  157.   // detailed function, but faster and contains less information.
  158.   function GetPlayers( )
  159.   {
  160.       if ($this->SendPacket('c') === false) {
  161.           throw new QueryServerException( 'Connection to ' . $this->szServerIP . ' failed or has dropped.' );
  162.       }
  163.  
  164.       // Again, pop the first eleven bytes send;
  165.       fread( $this->rSocketID, 11 );
  166.  
  167.       $iPlayerCount = ord( fread( $this->rSocketID, 2 ) );
  168.       $aReturnArray = array( );
  169.  
  170.       for( $i = 0; $i < $iPlayerCount; $i ++ )
  171.       {
  172.           $aReturnArray[ ] = array (
  173.               'Nickname' => $this->GetPacket( 1 ),
  174.               'Score'  => $this->toInteger( fread( $this->rSocketID, 4 ) )
  175.           );
  176.       }
  177.  
  178.       return $aReturnArray;
  179.   }
  180.  
  181.   // The GetDetailedPlayers() function returns the player list,
  182.   // but in a detailed form inclusing the score and the ping.
  183.   function GetDetailedPlayers( )
  184.   {
  185.       if ($this->SendPacket('d') === false) {
  186.           throw new QueryServerException( 'Connection to ' . $this->szServerIP . ' failed or has dropped.' );
  187.       }
  188.  
  189.       // Skip the first 11 bytes of the response;
  190.       fread( $this->rSocketID, 11 );
  191.  
  192.       $iPlayerCount = ord( fread( $this->rSocketID, 2 ) );
  193.       $aReturnArray = array( );
  194.  
  195.       for( $i = 0; $i < $iPlayerCount; $i ++ ) {
  196.           $aReturnArray[ ] = array(
  197.               'PlayerID'  => $this->toInteger( fread( $this->rSocketID, 1 ) ),
  198.               'Nickname'  => $this->GetPacket( 1 ),
  199.               'Score'   => $this->toInteger( fread( $this->rSocketID, 4 ) ),
  200.               'Ping'    => $this->toInteger( fread( $this->rSocketID, 4 ) )
  201.           );
  202.       }
  203.  
  204.       return $aReturnArray;
  205.   }
  206.  
  207. function RCON($rcon, $command)
  208.   {
  209.       echo 'Password '.$rcon.' with '.$command;
  210.       if ($this->SendPacket('x '.$rcon.' '.$command) === false) {
  211.           throw new QueryServerException( 'Connection to ' . $this->szServerIP . ' failed or has dropped.' );
  212.       }
  213.  
  214.       // Pop the first 11 bytes from the response;
  215.       $aReturnArray = fread( $this->rSocketID, 11 );
  216.  
  217.       echo fread( $this->rSocketID, 11 );
  218.  
  219.       return $aReturnArray;
  220.   }
  221.  
  222. }
  223.  
  224. /*********************************************
  225. *
  226. * The QueryServerException is used to throw errors when querying
  227. * a specific server. That way we force the user to use proper
  228. * error-handling, and preferably even a try-/catch statement.
  229. *
  230. **********************************************/
  231.  
  232. class QueryServerException extends Exception
  233. {
  234.   // The actual error message is stored in this variable.
  235.   private $szMessage;
  236.  
  237.   // Again, the __construct function gets called as soon
  238.   // as the exception is being thrown, in here we copy the message.
  239.   function __construct( $szMessage )
  240.   {
  241.       $this->szMessage = $szMessage;
  242.   }
  243.  
  244.   // In order to read the exception being thrown, we have
  245.   // a .NET-like toString() function, which returns the message.
  246.   function toString( )
  247.   {
  248.       return $this->szMessage;
  249.   }
  250. }
  251.  
Advertisement
Add Comment
Please, Sign In to add comment