Net_GameServerQuery
[ class tree: Net_GameServerQuery ] [ index: Net_GameServerQuery ] [ all elements ]

Source for file GameServerQuery.php

Documentation is available at GameServerQuery.php

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP Version 4                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1997-2004 The PHP Group                                |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 2 of the PHP license,         |
  8. // | that is bundled with this package in the file LICENSE, and is        |
  9. // | available at through the world-wide-web at                           |
  10. // | http://au.php.net/license/3_0.txt.                                    |
  11. // | If you did not receive a copy of the PHP license and are unable to   |
  12. // | obtain it through the world-wide-web, please send a note to          |
  13. // | license@php.net so we can mail you a copy immediately.               |
  14. // +----------------------------------------------------------------------+
  15. // | Author: Aidan Lister <aidan@virtualexplorer.com.au>                  |
  16. // +----------------------------------------------------------------------+
  17.  
  18. require_once ('PEAR.php');
  19. require_once ('function.microtime_str.php');
  20.  
  21.  
  22. /**
  23. * Error Constants
  24. */
  25. define('NET_GAMESERVERQUERY_ERROR_INVALIDPROTOCOL',      0);
  26. define('NET_GAMESERVERQUERY_ERROR_COULDNOTCONNECT',      1);
  27. define('NET_GAMESERVERQUERY_ERROR_NOREPLY',              2);
  28. define('NET_GAMESERVERQUERY_ERROR_COULDNOTSEND',         3);
  29.  
  30.  
  31. /**
  32. * Net_GameServerQuery
  33. *
  34. * Communicate with a Half-Life server.
  35. *
  36. @version        0.1
  37. @package        Net_GameServerQuery
  38. */
  39. {
  40.     /**
  41.     * The resource ID of the connection
  42.     *
  43.     * @var          resource 
  44.     * @access       private
  45.     */
  46.     private $_socket;
  47.  
  48.     /**
  49.     * True if we have a udp "connection" open.
  50.     *
  51.     * @var          bool 
  52.     * @access       private
  53.     */
  54.     private $_connected;
  55.  
  56.      /**
  57.     * The IP we're connecting to
  58.     *
  59.     * @var          string 
  60.     * @access       private
  61.     */
  62.     private $_ip;
  63.  
  64.     /**
  65.     * The port we wish to query
  66.     *
  67.     * @var          string 
  68.     * @access       private
  69.     */
  70.     private $_port;
  71.  
  72.     /**
  73.     * Max time in seconds to send data for
  74.     *
  75.     * @var          int 
  76.     * @access       private
  77.     */
  78.     private $_timeout_stream;
  79.  
  80.     /**
  81.     * Max time in seconds to attempt connection
  82.     *
  83.     * @var          int 
  84.     * @access       private
  85.     */
  86.     private $_timeout_connect;
  87.  
  88.     /**
  89.     * What packet protocol we are using
  90.     *
  91.     * @var          string 
  92.     * @access       private
  93.     */
  94.     private $_protocol;
  95.  
  96.     /**
  97.     * Ping from the server
  98.     *
  99.     * @var          string 
  100.     * @access       private
  101.     */
  102.     private $_ping;
  103.  
  104.     /**
  105.     * Constructor
  106.     *
  107.     * @version      0.1
  108.     * @access       public
  109.     */
  110.     public function __construct ()
  111.     {
  112.         // Set some defaults
  113.         $this->_timeout_stream = 1;
  114.         $this->_timeout_connect = 1;
  115.     }
  116.  
  117.  
  118.     /**
  119.     * Query a server
  120.     *
  121.     * @version      0.1
  122.     * @param        string $protocol The protocol to use (name of the game)
  123.     * @param        string $ip The address to connect to
  124.     * @param        int $port The information port of the server
  125.     * @return       mixed true if query was successful, or a PEAR_Error if not.
  126.     * @access       public
  127.     */
  128.     public function query ($ip$port$protocol)
  129.     {
  130.         // Load the protocols ($games, $protcols)
  131.         include_once ('GameServerQuery/protocols.php');
  132.         
  133.         // Check the protocol
  134.         if (array_key_exists($protocol$games)) {
  135.             $this->_protocol $protocols[$games[$protocol]['protocol']]}
  136.  
  137.         else {
  138.             // Throw error
  139.             return new Net_GameServerQuery_Error (NET_GAMESERVERQUERY_ERROR_INVALIDPROTOCOL)}
  140.  
  141.         // Save IP/Port
  142.         $this->_ip $ip;
  143.         $this->_port $port;
  144.  
  145.         // Perform the actual query
  146.         // $data is now the raw response from the server or an error
  147.         $data $this->_proceduralquery();
  148.         
  149.         // Check if there was an error
  150.         // If there was, return the error obj
  151.         if ($data instanceof Net_GameServerQuery_Error{
  152.             return $data}
  153.  
  154.         // If there wasn't, return the result object
  155.         else {
  156.             // We parse a formatted associative array to the result object
  157.             $info $this->_format($data);
  158.  
  159.             return new Net_GameServerQuery_Result ($info$this->_ping$this->_protocol)}
  160.     }
  161.  
  162.  
  163.     /**
  164.     * Connect, Query, Receive and Disconnect
  165.     *
  166.     * @version      0.1
  167.     * @return       mixed true if query was successful, or a PEAR_Error if not.
  168.     * @access       public
  169.     */
  170.     private function _proceduralquery ()
  171.     {
  172.         // Connect
  173.         if (!$this->_connect($this->_ip$this->_port)) {
  174.             // Throw Error
  175.         }
  176.  
  177.         // Build Packet
  178.         $packet $this->_build();
  179.  
  180.         // Start the timer
  181.         $start = microtime_str();
  182.  
  183.         // Send Packet
  184.         if (!$this->_send($packet)) {
  185.             // Throw Error
  186.         }
  187.  
  188.         // Receive
  189.         $data $this->_receive();
  190.         if (empty($data)) {
  191.             // Throw Error
  192.             return new Net_GameServerQuery_Error(NET_GAMESERVERQUERY_ERROR_NOREPLY);
  193.         }
  194.  
  195.         // When we recieve the reply, end the timer
  196.         $stop = microtime_str();
  197.         $this->_ping round((($stop $start* 1000));
  198.  
  199.         // Disconnect
  200.         $this->_disconnect();
  201.  
  202.         return $data;
  203.     }
  204.  
  205.  
  206.     /**
  207.     * Select the packet to use
  208.     *
  209.     * @version      0.1
  210.     * @return       string The packet
  211.     * @access       private
  212.     */
  213.     private function _build ()
  214.     {
  215.         $packet $this->_protocol['packet'];
  216.         return $packet;
  217.     }
  218.  
  219.  
  220.     /**
  221.     * Take the raw server response and extract
  222.     *     the information into an assoc array
  223.     *
  224.     * @version      0.1
  225.     * @param        string $data The string from which to parse the data
  226.     * @access       private
  227.     * @return       array Associative array of server data
  228.     */  
  229.     private function _format($data)
  230.     {
  231.         // Some games return the information in the second chunk
  232.         // so we need to define the delimiters for the chunk segments
  233.         $delimiter $this->_protocol['delim'];
  234.  
  235.         // Get the second chunk if need be
  236.         if ($delimiter !== null{
  237.             list($dataexplode($delimiter$data)}
  238.         
  239.         // The data chunk is backslash delimited
  240.         // Remove the first backslash, then explode
  241.         $data substr($data1);
  242.         $data_arr explode ('\\'$data);
  243.  
  244.         // Sort the data into an assoc array
  245.         for ($i = 0$x count($data_arr)$i $x$i += 2{
  246.             $info[$data_arr[$i]] $data_arr[$i+1]}
  247.  
  248.         return $info;
  249.     }
  250.  
  251.  
  252.     /**
  253.     * Connect
  254.     *
  255.     * @version      0.1
  256.     * @param        string $ip IP of the server
  257.     * @param        string $port Port of the server
  258.     * @return       bool True if the connection is established
  259.     * @access       private
  260.     */
  261.     private function _connect ($ip$port)
  262.     {
  263.         // "Connect"
  264.         $fp fsockopen('udp://' $ip$port$errno$errstr$this->_timeout_connect);
  265.  
  266.         // Check if connection was successful
  267.         if (false !== $fp)
  268.         {
  269.             // Save the connection
  270.             $this->_socket $fp;
  271.             $this->_connected = true;
  272.  
  273.             // Set stream options
  274.             socket_set_timeout($this->_socket$this->_timeout_stream0);
  275.             socket_set_blocking($this->_sockettrue);
  276.  
  277.             return true;
  278.         }
  279.         
  280.         return false;
  281.     }
  282.  
  283.  
  284.     /**
  285.     * Disconnect
  286.     *
  287.     * @version      0.1
  288.     * @return       bool True if the connection was closed
  289.     * @access       private
  290.     */
  291.     private function _disconnect ()
  292.     {
  293.         // Check if we're connected
  294.         if ($this->_connected !== true{
  295.             return false; }
  296.  
  297.         fclose($this->_socket);
  298.         return true;
  299.     }
  300.  
  301.  
  302.  
  303.     /**
  304.     * Send packet
  305.     *
  306.     * @version      0.1
  307.     * @param        string $packet The packet to send
  308.     * @return       bool True if the packet was sent
  309.     * @access       private
  310.     */
  311.     private function _send ($packet)
  312.     {
  313.         // Check if we're connected
  314.         if ($this->_connected !== true{
  315.             return false; }
  316.         
  317.         // If we have multiple packets
  318.         if (is_array($packet))
  319.         {
  320.             foreach ($packet as $pack)
  321.             {
  322.                 if (!fwrite($this->_socket$pack)) {
  323.                     return false; }
  324.             }
  325.         }
  326.         
  327.         // If we have a single packet
  328.         else
  329.         {
  330.             if (!fwrite($this->_socket$packet)) {
  331.                 return false; }
  332.         }
  333.  
  334.         return true;
  335.     }
  336.  
  337.  
  338.     /**
  339.     * Receive packet
  340.     *
  341.     * @version      0.1
  342.     * @access       private
  343.     */    
  344.     private function _receive()
  345.     {
  346.         // Read the first bit, this will tell us if we have a valid packet
  347.         $data fread($this->_socket1);
  348.         $metadata socket_get_status($this->_socket);
  349.         $bytesleft $metadata['unread_bytes'];
  350.  
  351.         // If we have a valid packet, read the rest of it
  352.         while ($bytesleft > 0)
  353.         {
  354.             $data .= fread($this->_socket$bytesleft);
  355.             $metadata socket_get_status($this->_socket);
  356.             $bytesleft $metadata['unread_bytes'];
  357.         }
  358.  
  359.         return $data;
  360.     }
  361.  
  362.  
  363.     /**
  364.     * Return microtime as a single string.
  365.     *
  366.     * @version        0.1
  367.     */
  368.     private function microtime_str()
  369.     
  370.         list ($msec$secexplode (" "microtime())
  371.         $microtime = (float)$msec + (float)$sec;
  372.         return $microtime
  373.     }
  374.  
  375. }
  376.  
  377.  
  378. /**
  379. * Net_GameServerQuery_Result
  380. *
  381. * Result object for GameServerQuery.
  382. *
  383. @version        0.1
  384. @package        Net_GameServerQuery
  385. */
  386. {
  387.     /**
  388.     * Hold the assoc. array from a query request
  389.     *
  390.     * @var          string 
  391.     * @access       private
  392.     */
  393.     private $_data;
  394.  
  395.     /**
  396.     * Ping
  397.     *
  398.     * @var          string 
  399.     * @access       private
  400.     */
  401.     private $_ping;
  402.  
  403.  
  404.     /**
  405.     * Constructor
  406.     *
  407.     * @version      0.1
  408.     * @param        string $data The raw array of packet data
  409.     * @param        int $ping The ping in milliseconds
  410.     * @param        array $prot The query protocol information
  411.     * @access       public
  412.     */
  413.     public function __construct ($data$ping$prot)
  414.     {
  415.         $this->_data      $data;
  416.         $this->_ping      $ping;
  417.         $this->_protocol  $prot;
  418.     }
  419.  
  420.  
  421.     /**
  422.     * Basic "status" information
  423.     *
  424.     * @version      0.1
  425.     * @return       array The associative array of server data
  426.     * @access       public
  427.     */
  428.     public function status ()
  429.     {
  430.         // Get a list of the raw packet status keys
  431.         $keys $this->_protocol['keys'];
  432.  
  433.         // Normalise the raw packet
  434.         // numplayers is not supported for all game protocols, yet
  435.         $normal = array (
  436.             'maxplayers'    => $this->_data[$keys[0]],
  437.             'numplayers'    => @$this->_data[$keys[1]],
  438.             'mapname'       => $this->_data[$keys[2]],
  439.             'hostname'      => $this->_data[$keys[3]],
  440.             );
  441.  
  442.         return $normal;
  443.     }
  444.  
  445.  
  446.     /**
  447.     * Raw packet data
  448.     *
  449.     * @version      0.1
  450.     * @return       array The associative array of server data
  451.     * @access       public
  452.     */
  453.     public function raw ()
  454.     {
  455.         return $this->_data;
  456.     }
  457.  
  458.  
  459.     /**
  460.     * Information about players on the server
  461.     *
  462.     * This function is not complete. It will return a dummy array of player information.
  463.     *
  464.     * @version      0.1
  465.     * @return       array The associative array of server data
  466.     * @access       public
  467.     */
  468.     public function players ()
  469.     {
  470.         return false;
  471.     }
  472.  
  473.  
  474.     /**
  475.     * Rules listing
  476.     * 
  477.     * This function is not complete. It will return false.
  478.     *
  479.     * @version      0.1
  480.     * @return       array The associative array of server data
  481.     * @access       public
  482.     */
  483.     public function rules ()
  484.     {
  485.         return false;
  486.     }
  487.  
  488.  
  489.     /**
  490.     * The time it took the server to respond to a request
  491.     *
  492.     * @version      0.1
  493.     * @return       float Ping in milliseconds
  494.     * @access       public
  495.     */
  496.     public function ping ()
  497.     {
  498.         return $this->_ping;
  499.     }
  500.  
  501. }
  502.  
  503.  
  504. /**
  505. * Net_GameServerQuery_Error
  506. *
  507. * Error object for GameServerQuery.
  508. *
  509. @version        0.1
  510. @package        Net_GameServerQuery
  511. */
  512. {
  513.     /**
  514.     * Error Number
  515.     *
  516.     * @var          string 
  517.     * @access       private
  518.     */
  519.     private $_errno;
  520.  
  521.  
  522.     /**
  523.     * Constructor
  524.     *
  525.     * @version      0.1
  526.     * @return       constant The error code
  527.     * @access       public
  528.     */
  529.     function __construct($errno)
  530.     {
  531.         $this->_errno $errno;
  532.     }
  533.  
  534.  
  535.     /**
  536.     * Print an error
  537.     *
  538.     * @version      0.1
  539.     * @return       constant The error code
  540.     * @access       public
  541.     */
  542.     function printError()
  543.     {
  544.         $errors = array (
  545.             NET_GAMESERVERQUERY_ERROR_INVALIDPROTOCOL        => 'Invalid Protocol',
  546.             NET_GAMESERVERQUERY_ERROR_COULDNOTCONNECT        => 'Could not connect to specified server',
  547.             NET_GAMESERVERQUERY_ERROR_NOREPLY                => 'Server did not reply to request',
  548.             NET_GAMESERVERQUERY_ERROR_COULDNOTSEND           => 'Could not send packet'
  549.         );
  550.  
  551.         print $errors[$this->_errno];
  552.     }
  553.  
  554. }
  555.  
  556. ?>

Documentation generated on Mon, 11 Mar 2019 10:14:59 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.