Download | Plain Text | No Line Numbers


  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2 foldmethod=marker: */
  4.  
  5. /*
  6.  * Gameserver Query Protocol Class
  7.  * Copyright (C) 2004 Manuel Mausz ( manuel @ clanserver.eu )
  8.  * ClanServer - Just Gaming!
  9.  * http://www.clanserver.eu
  10.  *
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2, or (at your option)
  14.  * any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  */
  25.  
  26. /* {{{ Description
  27.  * Gameserver Query Protocol Class
  28.  *
  29.  * Queries different gameservers and returns the current
  30.  * status information. supports several query protocols.
  31.  *
  32.  * Supported Protocols:
  33.  * For further information: http://www.int64.org/protocols/
  34.  * - Half-Life/Half-Life: Source ("HalfLife")
  35.  * - All Seeing Eye ("AllSeeingEye")
  36.  * - Quake 1/Quake World ("Quake1")
  37.  * - Quake 2 ("Quake2")
  38.  * - Quake 3 ("Quake3")
  39.  * - Doom 3/Quake 4/Enemy Territory: Quake Wars ("Doom3")
  40.  * - GameSpy/UT ("GameSpy")
  41.  * - GameSpy 2 ("GameSpy2")
  42.  * - GameSpy 3 ("GameSpy3")
  43.  * - GameSpy 4 ("GameSpy4")
  44.  *
  45.  * Users:
  46.  * $this->SetProtocol("protocol")
  47.  * ... protocol to use for query
  48.  *
  49.  * $this->SetIpPort("ip:port:queryport")
  50.  * ... ip, port and queryport to use for query
  51.  * the queryport is optional
  52.  *
  53.  * $this->SetLocalQuery(0 or 1)
  54.  * ... set localhost query (used for halflife query)
  55.  *
  56.  * $this->SetRequestData(array("infotype1", "infotype2", ...))
  57.  * ... data to request from server. must be type array
  58.  * use the alias "FullInfo" for full information
  59.  *
  60.  * array = $this->GetData()
  61.  * ... request the data from gameserver
  62.  * data will be returned as type array
  63.  *
  64.  * bool = $this->ERROR
  65.  * ... TRUE if an error occur, FALSE otherwise
  66.  *
  67.  * string = $this->ERRSTR
  68.  * ... contains the error message as string
  69.  *
  70.  * Advanced Users:
  71.  * $this->SetSocketTimeOut(int seconds, int microseconds)
  72.  * ... sets socket timeout. default: 0s, 50000ms
  73.  *
  74.  * $this->SetSocketLoopTimeOut(int microseconds)
  75.  * ... sets loop timeout in microseconds. default: 1000
  76.  *
  77.  * Debug Modus:
  78.  * define("DEBUG", TRUE)
  79.  * ... enables debug modus
  80.  *
  81.  * AutoDetection:
  82.  * $this->SetProtocol("AutoDetect")
  83.  * ... must be set to "AutoDetect"
  84.  * $this->SetProtocols(array("protocol1", "protocol2", ...))
  85.  * ... protocols to use for autodetection
  86.  * ... NOTE: GameSpyPortPlus10 is an alias for GameSpy but uses QueryPort+10
  87.  *
  88.  * Developers:
  89.  * This class contains an autodetect-query-protocol-engine
  90.  * If you want to add your own query, you will have to name your methodes like:
  91.  * _YourNameMain() ... main methode. will be called first from engine
  92.  * _YourNameString1() ... sub methode. must be called from main methode
  93.  * will request string1 from gameserver (eg: _YourNameDetails)
  94.  * _YourNameAutoDetect() ... will be called from autodetection engine before _YourNameMain
  95.  * normally used for setting correct query port
  96.  * if you don't want autodetection, don't define this methode
  97.  * _YourNameFullInfo() ... wrapper for requestdatatype "FullInfo"
  98.  *
  99.  }}} */
  100.  
  101. /* {{{ ToDo:
  102.  * - HalfLife/HalfLifeSource
  103.  * - Sometimes query comes in fragments prefixed by an id
  104.  * syntax: "<id>2" -> 02, 12, 22, ... (not sure about that)
  105.  * this behavior is only recognized in _HalfLifeRules()
  106.  * -> append fragments depending on their queryid
  107.  * - GameSpy
  108.  * - If one player name contains the string deliminater "\", the query will probably stop working
  109.  * - Protocol is fragmental and will send a queryid followed by a number at the end
  110.  * -> append fragments depending on their queryid
  111.  * - Better Error Handling!
  112.  }}} */
  113.  
  114. class GSQuery
  115. {
  116. // {{{ global variable definitions
  117. var $ERROR;
  118. var $ERRSTR;
  119. var $DEBUG;
  120.  
  121. var $_ip;
  122. var $_port;
  123. var $_queryport;
  124. var $_fullinfo;
  125. var $_localquery;
  126. var $_socket;
  127. var $_stimeout;
  128. var $_mstimeout;
  129. var $_looptimeout;
  130. var $_protocol;
  131. var $_gettypes;
  132. var $_protocols;
  133. // }}}
  134.  
  135. // {{{ GSQuery() - main class constructor
  136. function GSQuery()
  137. {
  138. $this->ERROR = FALSE;
  139. $this->ERRSTR = "";
  140. $this->DEBUG = FALSE;
  141.  
  142. $this->_ip = 0;
  143. $this->_port = 0;
  144. $this->_queryport = 0;
  145. $this->_fullinfo = 0;
  146. $this->_localquery = 0;
  147. $this->_socket = 0;
  148. $this->_stimeout = 0;
  149. $this->_mstimeout = 50000;
  150. $this->_looptimeout = 1000;
  151. $this->_protocol = "";
  152. $this->_gettypes = array();
  153. $this->_protocols = array();
  154. $this->_globalvars = array();
  155.  
  156. if (defined('DEBUG') && DEBUG)
  157. $this->DEBUG = TRUE;
  158.  
  159. set_error_handler(array(&$this, "_ErrorHandler"));
  160. return TRUE;
  161. }
  162. // }}}
  163.  
  164. // {{{ SetProtocol() - sets query protocol to use
  165. // @param string $arg1 protocol to use for gameserver query
  166. // @return bool always TRUE
  167. // @access public
  168. function SetProtocol($string)
  169. {
  170. $this->_protocol = $string;
  171. trigger_error("<b>Set Protocol</b>: ".$string);
  172. return TRUE;
  173. }
  174. // }}}
  175.  
  176. // {{{ SetIpPort() - sets ip, port and optional queryport
  177. // @param string $arg1 format: "ip:port" or "ip:port:queryport"
  178. // @return bool always TRUE
  179. // @access public
  180. function SetIpPort($string)
  181. {
  182. if (substr_count($string, ":") == 2)
  183. list($this->_ip, $this->_port, $this->_queryport) = explode(":", $string);
  184. else
  185. {
  186. list($this->_ip, $this->_port) = explode(":", $string);
  187. $this->_queryport = 0;
  188. }
  189. $this->_port = intval($this->_port);
  190. $this->_queryport = intval($this->_queryport);
  191.  
  192. trigger_error("<b>Set Gameserver IP</b>: ".$this->_ip);
  193. trigger_error("<b>Set Gameserver Port</b>: ".$this->_port);
  194. if ($this->_queryport != 0)
  195. trigger_error("<b>Set Gameserver QueryPort</b>: ".$this->_queryport);
  196. return TRUE;
  197. }
  198. // }}}
  199.  
  200. // {{{ SetRequestData() - sets data which query will request from gameserver
  201. // @param array $arg1 data to request from gameserver
  202. // @return bool always TRUE
  203. // @access public
  204. function SetRequestData($gettypes)
  205. {
  206. if (!is_array($gettypes))
  207. {
  208. $this->_SoftError("SetRequestData(): argument 1 is not an array");
  209. return FALSE;
  210. }
  211.  
  212. $this->_gettypes = $gettypes;
  213.  
  214. if ($this->_gettypes[0] == "FullInfo")
  215. $this->_fullinfo = 1;
  216.  
  217. foreach ($this->_gettypes as $type)
  218. trigger_error("<b>Set RequestData</b>: ".$type);
  219. return TRUE;
  220. }
  221. // }}}
  222.  
  223. // {{{ SetProtocols() - set autodetect protocols
  224. // @param array $arg1 protocols to autodetect
  225. // @return bool always TRUE
  226. // @access public
  227. function SetProtocols($protocols)
  228. {
  229. if (!is_array($protocols))
  230. {
  231. $this->_SoftError("SetProtocols(): argument 1 is not an array");
  232. return FALSE;
  233. }
  234.  
  235. $this->_protocols = $protocols;
  236. foreach ($this->_protocols as $type)
  237. trigger_error("<b>Set AutoDetect Protocols</b>: ".$type);
  238. return TRUE;
  239. }
  240. // }}}
  241.  
  242. // {{{ SetLocalQuery() - set local query (used for halflife query)
  243. // @param int 0 or 1
  244. // @return bool always TRUE
  245. // @access public
  246. function SetLocalQuery($enable)
  247. {
  248. $this->_localquery = $enable;
  249. trigger_error("<b>Set Local Query</b>: ".$this->_localquery);
  250. return TRUE;
  251. }
  252. // }}}
  253.  
  254. // {{{ SetSocketTimeOut() - sets an optional socket timeout
  255. // @param int $arg1 socket timeout in seconds
  256. // int $arg2 socket timeout in microseconds
  257. // @return bool always TRUE
  258. // @access public
  259. function SetSocketTimeOut($stimeout, $mstimeout)
  260. {
  261. $this->_stimeout = $stimeout;
  262. $this->_mstimeout = $mstimeout;
  263. trigger_error("<b>Set Socket Timeout Seconds</b>: ".$this->_stimeout);
  264. trigger_error("<b>Set Socket Timeout MicroSeconds</b>: ".$this->_mstimeout);
  265. return TRUE;
  266. }
  267. // }}}
  268.  
  269. // {{{ SetSocketLoopTimeOut() - sets an optional socket loop timeout
  270. // @param int $arg1 socket loop timeout in microseconds
  271. // @return bool always TRUE
  272. // @access public
  273. function SetSocketLoopTimeOut($timeout)
  274. {
  275. $this->_looptimeout = $timeout;
  276. trigger_error("<b>Set Socket Loop Timeout</b>: ".$this->_looptimeout);
  277. return TRUE;
  278. }
  279. // }}}
  280.  
  281. // {{{ GetData() - requests data from gameserver and returns an array
  282. // @return array requested data as array in format: $array["data1"][...]
  283. // bool FALSE if an error occur
  284. // @access public
  285. function GetData()
  286. {
  287. $recvdata = array();
  288.  
  289. $this->_CheckSettings();
  290. if ($this->ERROR)
  291. return FALSE;
  292. if ($this->_fullinfo && $this->_protocol != "AutoDetect")
  293. {
  294. $recvdata = call_user_func(array(&$this, "_".$this->_protocol."FullInfo"));
  295. $recvdata["Protocol"] = $this->_protocol;
  296. }
  297. else
  298. $recvdata = call_user_func(array(&$this, "_".$this->_protocol."Main"));
  299. $this->_CleanUP();
  300.  
  301. return $recvdata;
  302. }
  303. // }}}
  304.  
  305. // {{{ _GetMicroTime() - returns current timestamp as microseconds
  306. // @return float timestamp as micrososeconds
  307. // @access protected
  308. function _GetMicroTime()
  309. {
  310. list($usec, $sec) = explode(" ",microtime());
  311. return ((float)$usec + (float)$sec);
  312. }
  313. // }}}
  314.  
  315. // {{{ _CleanUp() - class clean up
  316. // @return bool always TRUE
  317. // @access protected
  318. function _CleanUp()
  319. {
  320. restore_error_handler();
  321. return TRUE;
  322. }
  323. // }}}
  324.  
  325. // {{{ _CheckSettings() - check for valid settings
  326. // @return bool TRUE if no error occur
  327. // FALSE otherwise
  328. // @access protected
  329. function _CheckSettings()
  330. {
  331. $type = "";
  332.  
  333. // check available protocol
  334. if ($this->_protocol == "")
  335. {
  336. $this->_SoftError("No protocol set");
  337. return FALSE;
  338. }
  339. if (!is_callable(array(&$this, "_".$this->_protocol."Main")))
  340. {
  341. $this->_SoftError("\"".$this->_protocol."\" protocol not available");
  342. return FALSE;
  343. }
  344.  
  345. // check available methodes for this protocol
  346. if (empty($this->_gettypes))
  347. {
  348. $this->_SoftError("No requesttypes set");
  349. return FALSE;
  350. }
  351. if ($this->_protocol != "AutoDetect")
  352. {
  353. foreach ($this->_gettypes as $type)
  354. {
  355. if ($type != "RCon")
  356. {
  357. if (!is_callable(array(&$this, "_".$this->_protocol.$type)))
  358. {
  359. $this->_SoftError("SetRequestData() Type: \"".$type."\" now known in Protocol: \"".$this->_protocol."\"");
  360. return FALSE;
  361. }
  362. }
  363. }
  364. }
  365.  
  366. // check ip and port
  367. if ($this->_ip == "" || $this->_port == "")
  368. {
  369. $this->_SoftError("No IP or Port set");
  370. return FALSE;
  371. }
  372. if (!preg_match("/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/", gethostbyname($this->_ip)))
  373. {
  374. $this->_SoftError("Wrong Gameserver IP Format");
  375. return FALSE;
  376. }
  377. if (!is_integer($this->_port) || $this->_port <= 0 || 65536 < $this->_port)
  378. {
  379. $this->_SoftError("Wrong Gameserver Port Format");
  380. return FALSE;
  381. }
  382. if ($this->_queryport != 0 && (!is_integer($this->_queryport) || $this->_queryport <= 0 || 65536 < $this->_queryport))
  383. {
  384. $this->_SoftError("Wrong Gameserver QueryPort Format");
  385. return FALSE;
  386. }
  387.  
  388. // check and calculate socket loop timeout
  389. if ($this->_looptimeout < 500) $this->_looptimeout = 500;
  390. if ($this->_looptimeout > 2000) $this->_looptimeout = 2000;
  391. $this->_looptimeout = doubleval($this->_looptimeout / 1000.0);
  392.  
  393. return TRUE;
  394. }
  395. // }}}
  396.  
  397. // {{{ _ErrorHandler() - main php error handler
  398. // @param -> reference to php documentation
  399. // @return bool always TRUE
  400. // stops script if error number is E_USER_ERROR
  401. // @access protected
  402. function _ErrorHandler($errno, $errstr, $errfile, $errline)
  403. {
  404. switch ($errno) {
  405. case E_USER_ERROR:
  406. echo "<b>[FATAL]</b> ".$errstr." in <b>".$errfile."</b> on line <b>".$errline."</b><br />\n";
  407. exit(1);
  408. break;
  409. case E_USER_WARNING:
  410. echo "<b>[ERROR]</b> ".$errstr." in <b>".$errfile."</b> on line <b>".$errline."</b><br />\n";
  411. break;
  412. case E_USER_NOTICE:
  413. if ($this->DEBUG)
  414. echo "<b>[DEBUG]</b> ".$errstr."</b>\n";
  415. break;
  416. case E_NOTICE:
  417. echo "<b>[NOTICE]</b> ".$errstr." in <b>".$errfile."</b> on line <b>".$errline."</b><br />\n";
  418. break;
  419. default:
  420. echo "<b>[UNKNOWN]</b> ".$errstr." in <b>".$errfile."</b> on line <b>".$errline."</b><br />\n";
  421. break;
  422. }
  423. return TRUE;
  424. }
  425. // }}}
  426.  
  427. // {{{ _SoftError() - soft error handler
  428. // @param string $arg1 error message
  429. // @return bool always TRUE
  430. // @access proteced
  431. function _SoftError($errstr)
  432. {
  433. $this->ERROR = ($errstr === false || strlen($errstr) === 0) ? FALSE : TRUE;
  434. $this->ERRSTR = $errstr;
  435. return TRUE;
  436. }
  437. // }}}
  438.  
  439. // {{{ _CreateUDPSocket() - creates an udp socket
  440. // @return bool always TRUE
  441. // stops script if an error occur
  442. // @access protected
  443. function _CreateUDPSocket()
  444. {
  445. if ($this->_queryport == 0)
  446. $this->_queryport = $this->_port;
  447. if (!$this->_socket = fsockopen("udp://".$this->_ip, $this->_queryport, $errnr, $errstr))
  448. {
  449. trigger_error("Could not create socket (".$errstr.")", E_USER_ERROR);
  450. }
  451. socket_set_blocking($this->_socket, TRUE);
  452. socket_set_timeout($this->_socket, $this->_stimeout, $this->_mstimeout);
  453. return TRUE;
  454. }
  455. // }}}
  456.  
  457. // {{{ _SendSocket() - write data to socket
  458. // @param string $arg1 string to write to socket
  459. // @return bool always TRUE
  460. // stops script if an error occur
  461. // @access protected
  462. function _SendSocket($string)
  463. {
  464. if (!fwrite($this->_socket, $string, strlen($string)))
  465. trigger_error("Could not send data", E_USER_ERROR);
  466. return TRUE;
  467. }
  468. // }}}
  469.  
  470. // {{{ _GetSocketData() - read data from socket
  471. // @return string data received from socket buffer
  472. // bool FALSE and triggers _SoftError() if an error occur
  473. // @access protected
  474. function _GetSocketData()
  475. {
  476. $recv = "";
  477. $socketstatus = array();
  478. $start = $this->_GetMicroTime();
  479. do
  480. {
  481. $recv .= fgetc($this->_socket);
  482. $socketstatus = socket_get_status($this->_socket);
  483. if ($this->_GetMicroTime() > ($start + $this->_looptimeout))
  484. {
  485. $this->_CloseUDPSocket();
  486. $this->_SoftError("Connection to server timeout out");
  487. return FALSE;
  488. }
  489. }
  490. while ($socketstatus["unread_bytes"]);
  491.  
  492. if ($recv == "")
  493. $this->_SoftError("Nothing received from server");
  494.  
  495. return $recv;
  496. }
  497. // }}}
  498.  
  499. // {{{ _GetSocketDataNr() - read multiple times from socket
  500. // @param int $arg1 number how often _GetSocketData() will be called
  501. // @return array one array element for every received data
  502. // triggers _SoftError() if no data will be received
  503. // @access protected
  504. function _GetSocketDataNr($nr)
  505. {
  506. $recv = "";
  507. $data = array();
  508. for ($i = 0; $i < $nr; $i++)
  509. {
  510. $recv = $this->_GetSocketData();
  511. if ($recv != "")
  512. array_push($data, $recv);
  513. }
  514. if (count($data))
  515. $this->_SoftError(FALSE);
  516.  
  517. return $data;
  518. }
  519. // }}}
  520.  
  521. // {{{ _CloseUDPSocket() - close an udp socket
  522. // @return bool always TRUE
  523. // stops script if socket cannot be closed
  524. // @access protected
  525. function _CloseUDPSocket()
  526. {
  527. if (!fclose($this->_socket))
  528. trigger_error("Could not close socket", E_USER_ERROR);
  529. return TRUE;
  530. }
  531. // }}}
  532.  
  533. // {{{ _CheckQueryHeader() - checks for query header
  534. // @param string $arg1 string to search in. string will be shortened automatically
  535. // string $arg2 string to search for
  536. // string $arg3 will be set to the snippet-string off $arg1
  537. // @return bool TRUE if $arg2 was found
  538. // FALSE otherwise
  539. // @access protected
  540. function _CheckQueryHeader(&$data, $header, &$snippet)
  541. {
  542. $offset = 0;
  543. $snippet = "";
  544.  
  545. if ($this->ERROR)
  546. return FALSE;
  547.  
  548. if (($offset = strpos($data, $header)) !== FALSE)
  549. {
  550. $snippet = substr($data, 0, $offset);
  551. $data = substr($data, $offset + strlen($header));
  552. return TRUE;
  553. }
  554. $this->_SoftError("No query header found in received data");
  555. return FALSE;
  556. }
  557. // }}}
  558.  
  559. // {{{ _CheckQueryFooter() - checks for query footer
  560. // @param string $arg1 string to search in. string will be shortened automatically
  561. // string $arg2 string to search for
  562. // string $arg3 will be set to the snippet-string off $arg1
  563. // @return bool TRUE if $arg2 was found
  564. // FALSE otherwise
  565. // @access protected
  566. function _CheckQueryFooter(&$data, $footer, &$snippet)
  567. {
  568. $offset = 0;
  569. $snippet = "";
  570.  
  571. if ($this->ERROR)
  572. return FALSE;
  573.  
  574. if (($offset = strpos($data, $footer)) !== FALSE)
  575. {
  576. $snippet = substr($data, $offset + strlen($footer));
  577. $data = substr($data, 0, $offset);
  578. return TRUE;
  579. }
  580. $this->_SoftError("No query footer found in received data");
  581. return FALSE;
  582. }
  583. // }}}
  584.  
  585. // {{{ _GetCharacterTerminatedString() - get a character-terminated-string
  586. // @param string $arg1 string to search in. string will be shortened automatically
  587. // @param char $arg2 character to search for
  588. // @return string first character-terminated string
  589. // @access protected
  590. function _GetCharacterTerminatedString(&$data, $chr)
  591. {
  592. $str = "";
  593. $counter = 0;
  594. while ((strlen($data) > $counter) && ($data{$counter++} != $chr))
  595. $str .= $data{$counter-1};
  596. $data = substr($data, strlen($str) + 1);
  597. return $str;
  598. }
  599. // }}}
  600.  
  601. // {{{ _GetDelimitedVariables() - splits a string delimated by a character
  602. // @param string $arg1 string to search in
  603. // @return array splitted array. format: $data["before_character"] = after_character
  604. // @access protected
  605. function _GetDelimitedVariables(&$data, $delimiter)
  606. {
  607. $name = "";
  608. $value = "";
  609. $vars = array();
  610.  
  611. $name = strtok($data, $delimiter);
  612. $value = strtok($delimiter);
  613. while (strlen($name))
  614. {
  615. $vars[$name] = $value;
  616. $name = strtok($delimiter);
  617. $value = strtok($delimiter);
  618. }
  619. return $vars;
  620. }
  621. // }}}
  622.  
  623. // {{{ _GetByteAsChr() - get one byte as character
  624. // @param string $arg1 string to search in. string will be shortened automatically
  625. // @return string first byte of string as ascii character
  626. // bool FALSE if length of $arg1 is zero
  627. // @access protected
  628. function _GetByteAsChr(&$data)
  629. {
  630. $str = "";
  631. if (!strlen($data))
  632. return FALSE;
  633. $str = $data{0};
  634. $data = substr($data, 1);
  635. return $str;
  636. }
  637. // }}}
  638.  
  639. // {{{ _GetByteAsAscii() - get one byte as ascii value
  640. // @param string $arg1 string to search in. string will be shortened automatically
  641. // @return string first byte of string as type ascii value
  642. // @access protected
  643. function _GetByteAsAscii(&$data)
  644. {
  645. $str = "";
  646. $str = ord($this->_GetByteAsChr($data));
  647. return $str;
  648. }
  649. // }}}
  650.  
  651. // {{{ _GetStringByLength() - get a string by length
  652. // @param string $arg1 string to snip. string will be shortened automatically
  653. // int $arg2 length to snip off
  654. // @return string snippet string
  655. // @access protected
  656. function _GetStringByLength(&$data, $length)
  657. {
  658. $str = "";
  659. $str = substr($data, 0, $length);
  660. $data = substr($data, strlen($str));
  661. return $str;
  662. }
  663. // }}}
  664.  
  665. // {{{ _GetInt16AsInt() - get int16 value
  666. // @param string $arg1 string to search in. string will be shortened automatically
  667. // @return int corresponding int16 value
  668. // bool FALSE if length of $arg1 is too short
  669. // @access proteced
  670. function _GetInt16AsInt(&$data)
  671. {
  672. $str = "";
  673. if (strlen($data) < 2)
  674. return FALSE;
  675. $str = $this->_GetByteAsChr($data).$this->_GetByteAsChr($data);
  676. $str = unpack('sint', $str);
  677. return $str["int"];
  678. }
  679. // }}}
  680.  
  681. // {{{ _GetInt32AsInt() - get int32 value
  682. // @param string $arg1 string to search in. string will be shortened automatically
  683. // @return int corresponding int32 value
  684. // bool FALSE if length of $arg1 is too short
  685. // @access proteced
  686. function _GetInt32AsInt(&$data)
  687. {
  688. $str = "";
  689. if (strlen($data) < 4)
  690. return FALSE;
  691. $str = $this->_GetByteAsChr($data).$this->_GetByteAsChr($data).$this->_GetByteAsChr($data).$this->_GetByteAsChr($data);
  692. $str = unpack('iint', $str);
  693. return $str["int"];
  694. }
  695. // }}}
  696.  
  697. // {{{ _GetFloat32AsFloat() - get float32 value
  698. // @param string $arg1 string to search in. string will be shortened automatically
  699. // @return float corresponding float32 value
  700. // bool FALSE if length of $arg1 is too short
  701. // @access proteced
  702. function _GetFloat32AsFloat(&$data)
  703. {
  704. $str = "";
  705. if (strlen($data) < 4)
  706. return FALSE;
  707. $str = $this->_GetByteAsChr($data).$this->_GetByteAsChr($data).$this->_GetByteAsChr($data).$this->_GetByteAsChr($data);
  708. $str = unpack('fint', $str);
  709. return $str["int"];
  710. }
  711. // }}}
  712.  
  713. // {{{ _GetLongAsLong() - get long value
  714. // @param string $arg1 string to search in. string will be shortened automatically
  715. // @return long corresponding long value
  716. // bool FALSE if length of $arg1 is too short
  717. // @access proteced
  718. function _GetLongAsLong(&$data)
  719. {
  720. $str = "";
  721. if (strlen($data) < 4)
  722. return FALSE;
  723. $str = $this->_GetByteAsChr($data).$this->_GetByteAsChr($data).$this->_GetByteAsChr($data).$this->_GetByteAsChr($data);
  724. $str = unpack('llong', $str);
  725. return $str["long"];
  726. }
  727. // }}}
  728.  
  729.  
  730. // {{{ _HexDump() - dump bytes as hex
  731. // @param string $arg1 data to dump
  732. // @return bool TRUE
  733. // @access proteced
  734. function _HexDump($data)
  735. {
  736. echo "Length: ".strlen($data)."\n";
  737.  
  738. $cache = "";
  739. for ($i = 0; $i < strlen($data); $i++)
  740. {
  741. if ($i % 16 == 0)
  742. printf("%08x ", $i);
  743. elseif ($i % 8 == 0)
  744. echo " ";
  745. else
  746. echo " ";
  747.  
  748. $cache .= $data{$i};
  749. printf("%02x", ord($data{$i}));
  750.  
  751. if (strlen($cache) == 16 || $i == strlen($data) - 1)
  752. {
  753. if (strlen($cache) < 16)
  754. {
  755. $shift = "";
  756. for ($j = strlen($cache); $j < 16; $j++)
  757. {
  758. if ($j % 8 == 0)
  759. $shift .= " ";
  760. $shift .= " ";
  761. }
  762. echo substr($shift, 0, strlen($shift) - 1);
  763. if (strlen($shift) < 3*8)
  764. echo " ";
  765. }
  766.  
  767. echo " |";
  768. for ($j = 0; $j < strlen($cache); $j++)
  769. {
  770. $chr = $cache{$j};
  771. if (ord($chr) < ord("\x20") || ord($chr) > ord("\x7E"))
  772. $chr = ".";
  773. echo htmlentities($chr);
  774. }
  775. echo "|";
  776. echo "\n";
  777.  
  778. $cache = "";
  779. }
  780. }
  781.  
  782. return TRUE;
  783. }
  784. // }}}
  785.  
  786.  
  787. // {{{ _AutoDetectMain() - main methode of the autodetect routine
  788. // @return array decoded data received from gameserver
  789. // bool FALSE if an error occur
  790. // @access protected
  791. function _AutoDetectMain()
  792. {
  793. $protocol = "";
  794. $subproto = "";
  795. $data = array();
  796.  
  797. // first some checks
  798. if (empty($this->_protocols))
  799. {
  800. $this->_SoftError("No autodetect protocols set");
  801. return FALSE;
  802. }
  803. foreach ($this->_protocols as $protocol)
  804. {
  805. if (!is_callable(array(&$this, "_".$protocol."Main")))
  806. {
  807. $this->_SoftError("\"".$protocol."\" protocol not available");
  808. return FALSE;
  809. }
  810. if (!is_callable(array(&$this, "_".$protocol."AutoDetect")))
  811. {
  812. $this->_SoftError("No Autodetection method available for Protocol: \"".$protocol."\"");
  813. return FALSE;
  814. }
  815. foreach ($this->_gettypes as $subproto)
  816. {
  817. if (!is_callable(array(&$this, "_".$protocol.$subproto)))
  818. {
  819. $this->_SoftError("SetRequestData() Type: \"".$subproto."\" now known in Protocol: \"".$protocol."\"");
  820. return FALSE;
  821. }
  822. }
  823. }
  824. if ($this->_queryport != 0)
  825. {
  826. $this->_SoftError("Never set a queryport if you use autodetection");
  827. return FALSE;
  828. }
  829.  
  830. trigger_error("<b>Starting AutoDetection</b>");
  831. foreach ($this->_protocols as $type)
  832. {
  833. call_user_func(array(&$this, "_".$type."AutoDetect"));
  834. $autodetect = new GSQuery();
  835. $autodetect->SetProtocol($type);
  836. $autodetect->SetIpPort($this->_ip.":".$this->_port.":".$this->_queryport);
  837. $autodetect->SetRequestData($this->_gettypes);
  838. $autodetect->SetSocketTimeOut($this->_stimeout, $this->_mstimeout);
  839. $autodetect->SetSocketLoopTimeOut($this->_looptimeout);
  840. $data = $autodetect->GetData();
  841. $this->ERROR = $autodetect->ERROR;
  842. $this->ERRSTR = $autodetect->ERRSTR;
  843. if ($autodetect->ERROR == FALSE)
  844. break;
  845. }
  846.  
  847. return $data;
  848. }
  849. // }}}
  850.  
  851. // {{{ Query Protocol definitions
  852. // {{{ Query Protocol: HalfLife
  853. // {{{ _HalfLifeMain() - Query Protocol: HalfLife - Type: Main Methode
  854. // @return array decoded data received from gameserver
  855. // bool FALSE if an error occur
  856. // @access protected
  857. function _HalfLifeMain()
  858. {
  859. $challenge = "\xFF\xFF\xFF\xFF";
  860. $data = array();
  861. $this->_CreateUDPSocket();
  862.  
  863. if (!$this->_localquery)
  864. $challenge = call_user_func(array(&$this, "_".$this->_protocol."Challenge"));
  865.  
  866. foreach ($this->_gettypes as $type)
  867. {
  868. $data[$type] = call_user_func(array(&$this, "_".$this->_protocol.$type), $challenge);
  869. if ($this->ERROR)
  870. return FALSE;
  871. }
  872. $this->_CloseUDPSocket();
  873.  
  874. return $data;
  875. }
  876. /// }}}
  877.  
  878. // {{{ Query Protocol definitions
  879. // {{{ Query Protocol: HalfLife
  880. // {{{ _HalfLifeRecv() - Query Protocol: HalfLife - Type: Recv
  881. // @access protected
  882. function _HalfLifeRecv($query)
  883. {
  884. $this->_SendSocket($query);
  885.  
  886. $recv = "";
  887. $packets = array();
  888. $packetcnt = 1;
  889. $compressed = false;
  890. for ($i = 0; $i < $packetcnt; $i++)
  891. {
  892. $recv = implode("", $this->_GetSocketDataNr(1));
  893. # split packet
  894. if (substr($recv, 0, 4) == "\xFE\xFF\xFF\xFF")
  895. {
  896. $recv = substr($recv, 4);
  897. $requestid = $this->_GetLongAsLong($recv); # maybe check too?
  898. $compressed = (($requestid & (int)(1 << 31)) == (int)(1 << 31)) ? true : false;
  899. if (isset($this->_globalvars["ProtocolVersion"]) && $this->_globalvars["ProtocolVersion"] == 48)
  900. {
  901. $tmp = $this->_GetByteAsAscii($recv);
  902. $packetcnt = $tmp & 0xF;
  903. $packetnum = ($tmp >> 4) & 0xF;
  904. }
  905. else
  906. {
  907. $packetcnt = $this->_GetByteAsAscii($recv);
  908. $packetnum = $this->_GetByteAsAscii($recv);
  909. }
  910. # only in tf2 and newer source engines. should be harcoded?!
  911. if (substr($recv, 0, 2) == "\xE0\x04")
  912. $this->_GetInt16AsInt($recv);
  913. if ($compressed)
  914. {
  915. $realsize = $this->_GetInt32AsInt($recv);
  916. $realcrc32 = $this->_GetInt32AsInt($recv);
  917. }
  918. $packets[$packetnum] = $recv;
  919. }
  920. # single packet
  921. elseif (substr($recv, 0, 4) == "\xFF\xFF\xFF\xFF")
  922. {
  923. $packets[0] = $recv;
  924. }
  925. # unknown
  926. else
  927. {
  928. $this->_SoftError("Unknown header in half-life packet");
  929. return FALSE;
  930. }
  931. }
  932.  
  933. $recv = implode("", $packets);
  934. if ($compressed)
  935. $recv = bzdecompress($recv);
  936.  
  937. return $recv;
  938. }
  939. /// }}}
  940.  
  941.  
  942. // {{{ _HalfLifeDetails() - Query Protocol: HalfLife - Type: Server Details
  943. // @return array decoded data received from gameserver
  944. // bool FALSE if an error occur
  945. // @access protected
  946. function _HalfLifeDetails()
  947. {
  948. $recv = "";
  949. $prefix = "";
  950. $data = array();
  951. $tmp = array();
  952.  
  953. $this->_SendSocket("\xFF\xFF\xFF\xFFTSource Engine Query\x00");
  954. $recv = implode("", $this->_GetSocketDataNr(1));
  955.  
  956. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFF", $prefix))
  957. return FALSE;
  958.  
  959. # Half-Life reply
  960. if ($recv{0} == "m")
  961. {
  962. $recv = substr($recv, 1);
  963. $tmp = explode(":", $this->_GetCharacterTerminatedString($recv, "\x00"));
  964. $data["Ip"] = $tmp[0];
  965. $data["Port"] = $tmp[1];
  966. $data["Hostname"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  967. $data["Map"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  968. $data["GameDir"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  969. $data["GameDesc"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  970. $data["PlayerCount"] = $this->_GetByteAsAscii($recv);
  971. $data["MaxPlayers"] = $this->_GetByteAsAscii($recv);
  972. $data["ProtocolVersion"] = $this->_GetByteAsAscii($recv);
  973. $data["ServerType"] = $this->_GetByteAsChr($recv);
  974. $data["ServerOS"] = $this->_GetByteAsChr($recv);
  975. $data["Password"] = $this->_GetByteAsAscii($recv);
  976. $data["Modded"] = $this->_GetByteAsAscii($recv);
  977. if ($data["Modded"])
  978. {
  979. $data["ModWebsite"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  980. $data["ModDownloadServer"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  981. $this->_GetCharacterTerminatedString($recv, "\x00");
  982. $data["ModVersion"] = $this->_GetInt32AsInt($recv);
  983. $data["ModSize"] = $this->_GetInt32AsInt($recv);
  984. $data["ModServerSideOnly"] = $this->_GetByteAsAscii($recv);
  985. $data["ModCustomDLL"] = $this->_GetByteAsAscii($recv);
  986. }
  987. $data["Secure"] = $this->_GetByteAsAscii($recv);
  988. }
  989. # Half-Life Source reply
  990. elseif ($recv{0} == "I")
  991. {
  992. $recv = substr($recv, 1);
  993. $data["ProtocolVersion"] = $this->_GetByteAsAscii($recv);
  994. $this->_globalvars["ProtocolVersion"] = $data["ProtocolVersion"];
  995. $data["Hostname"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  996. $data["Map"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  997. $data["GameDir"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  998. $data["GameDesc"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  999. $data["SteamAppID"] = $this->_GetInt16AsInt($recv);
  1000. $data["PlayerCount"] = $this->_GetByteAsAscii($recv);
  1001. $data["MaxPlayers"] = $this->_GetByteAsAscii($recv);
  1002. $data["BotCount"] = $this->_GetByteAsAscii($recv);
  1003. $data["ServerType"] = $this->_GetByteAsChr($recv);
  1004. $data["ServerOS"] = $this->_GetByteAsChr($recv);
  1005. $data["Password"] = $this->_GetByteAsAscii($recv);
  1006. $data["Secure"] = $this->_GetByteAsAscii($recv);
  1007. }
  1008. else
  1009. $this->_SoftError("Unknown reply from HalfLife");
  1010.  
  1011. return $data;
  1012. }
  1013. // }}}
  1014.  
  1015. // {{{ _HalfLifeChallenge() - Query Protocol: HalfLife - get challenge string for quering
  1016. // @return string challenge string
  1017. // @access protected
  1018. function _HalfLifeChallenge()
  1019. {
  1020. $recv = "";
  1021. $prefix = "";
  1022. $challenge = "";
  1023.  
  1024. // there's a bug in new hlds protocol which fucks up the challenge query.
  1025. // so fallback to A2S_PLAYER with -1 as challenge id */
  1026. //$this->_SendSocket("\xFF\xFF\xFF\xFFW");
  1027. $this->_SendSocket("\xFF\xFF\xFF\xFFV\xFF\xFF\xFF\xFF");
  1028. $recv = implode("", $this->_GetSocketDataNr(1));
  1029.  
  1030. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFFA", $prefix))
  1031. return FALSE;
  1032.  
  1033. $challenge = substr($recv, 0, 4);
  1034.  
  1035. return $challenge;
  1036. }
  1037. // }}}
  1038.  
  1039. // {{{ _HalfLifeRules() - Query Protocol: HalfLife - Type: Server Rules
  1040. // @param string challenge string
  1041. // @return array decoded data received from gameserver
  1042. // bool FALSE if an error occur
  1043. // @access protected
  1044. function _HalfLifeRules($challenge)
  1045. {
  1046. $prefix = "";
  1047. $recv = $this->_HalfLifeRecv("\xFF\xFF\xFF\xFFV".$challenge);
  1048.  
  1049. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFFE", $prefix))
  1050. return FALSE;
  1051.  
  1052. $data["RuleCount"] = $this->_GetInt16AsInt($recv);
  1053. for ($i = 0; $i < $data["RuleCount"]; $i++)
  1054. $data[$this->_GetCharacterTerminatedString($recv, "\x00")] = $this->_GetCharacterTerminatedString($recv, "\x00");
  1055.  
  1056. return $data;
  1057. }
  1058. // }}}
  1059.  
  1060. // {{{ _HalfLifePlayers() - Query Protocol: HalfLife - Type: Players
  1061. // @param string challenge string
  1062. // @return array decoded data received from gameserver
  1063. // bool FALSE if an error occur
  1064. // @access protected
  1065. function _HalfLifePlayers($challenge)
  1066. {
  1067. $prefix = "";
  1068. $recv = $this->_HalfLifeRecv("\xFF\xFF\xFF\xFFU".$challenge);
  1069.  
  1070. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFFD", $prefix))
  1071. return FALSE;
  1072.  
  1073. $data["PlayerCount"] = $this->_GetByteAsAscii($recv);
  1074. $data["Players"] = array();
  1075. for ($i = 0; $i < $data["PlayerCount"]; $i++)
  1076. {
  1077. $player = array();
  1078. $player["Number"] = $this->_GetByteAsAscii($recv);
  1079. $player["Name"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  1080. $player["Score"] = $this->_GetInt32AsInt($recv);
  1081. $player["Time"] = round($this->_GetFloat32AsFloat($recv), 0) + 82800;
  1082. if ($player["Name"] == "" && $player["Score"] == 0 && $player["Time"] == 82800)
  1083. $player["Number"] = $player["Time"] = 0;
  1084. array_push($data["Players"], $player);
  1085. }
  1086.  
  1087. return $data;
  1088. }
  1089. // }}}
  1090.  
  1091. // {{{ _HalfLifeAutoDetect() - Query Protocol: HalfLife - Type: AutoDetect Methode
  1092. // @return bool always TRUE
  1093. // @access protected
  1094. function _HalfLifeAutoDetect()
  1095. {
  1096. $this->_queryport = $this->_port;
  1097. return TRUE;
  1098. }
  1099. // }}}
  1100.  
  1101. // {{{ _HalfLifeFullInfo() - Query Protocol: HalfLife - Type: Request Full Server Info
  1102. // @return array decoded data received from gameserver
  1103. // @access protected
  1104. function _HalfLifeFullInfo()
  1105. {
  1106. $this->SetRequestData(array("Details", "Rules", "Players"));
  1107. return $this->_HalfLifeMain();
  1108. }
  1109. // }}}
  1110. // }}}
  1111.  
  1112. // {{{ Query Protocol: AllSeeingEye
  1113. // {{{ _AllSeeingEyeMain() - Query Protocol: AllSeeingEye - Type: Main Methode
  1114. // @return array decoded data received from gameserver
  1115. // bool FALSE if an error occur
  1116. // @access protected
  1117. function _AllSeeingEyeMain()
  1118. {
  1119. $recv = "";
  1120. $prefix = "";
  1121. $tmp = array();
  1122. $data = array();
  1123.  
  1124. $this->_CreateUDPSocket();
  1125. $this->_SendSocket("s");
  1126. $recv = implode("", $this->_GetSocketDataNr(1));
  1127.  
  1128. if (!$this->_CheckQueryHeader($recv, "EYE1", $prefix))
  1129. return FALSE;
  1130.  
  1131. $tmp["Details"] = $this->_AllSeeingEyeDetails($recv);
  1132. $tmp["Players"] = $this->_AllSeeingEyePlayers($recv);
  1133. if ($this->ERROR)
  1134. return FALSE;
  1135. $this->_CloseUDPSocket();
  1136.  
  1137.  
  1138. foreach ($this->_gettypes as $type)
  1139. $data[$type] = $tmp[$type];
  1140.  
  1141. return $data;
  1142. }
  1143. // }}}
  1144.  
  1145. // {{{ _AllSeeingEyeDetails() - Query Protocol: AllSeeingEye - Type: Server Details
  1146. // @return array decoded data received from gameserver
  1147. // @access protected
  1148. function _AllSeeingEyeDetails(&$recv)
  1149. {
  1150. $tmp = "";
  1151. $data = array();
  1152.  
  1153. $data["GameName"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1154. $data["Port"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1155. $data["HostName"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1156. $data["GameTyp"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1157. $data["Map"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1158. $data["Version"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1159. $data["Password"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1160. $data["PlayerCount"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1161. $data["MaxPlayers"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1162.  
  1163. while (($tmp = $this->_GetByteAsAscii($recv)) != "1")
  1164. $data[$this->_GetStringByLength($recv, $tmp - 1)] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1165.  
  1166. return $data;
  1167. }
  1168. // }}}
  1169.  
  1170. // {{{ _AllSeeingEyePlayers() - Query Protocol: AllSeeingEye - Type: Players
  1171. // @return array decoded data received from gameserver
  1172. // @access protected
  1173. function _AllSeeingEyePlayers(&$recv)
  1174. {
  1175. $tmp = "";
  1176. $counter = 0;
  1177. $players = array();
  1178.  
  1179. if (strlen($recv) == 0)
  1180. return $players;
  1181.  
  1182. while(strlen($recv) > 0)
  1183. {
  1184. $tmp = $this->_GetByteAsAscii($recv);
  1185. if ($tmp & 1)
  1186. $players[$counter]["Name"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1187. if ($tmp & 2)
  1188. $players[$counter]["Team"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1189. if ($tmp & 4)
  1190. $players[$counter]["Skin"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1191. if ($tmp & 8)
  1192. $players[$counter]["Score"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1193. if ($tmp & 16)
  1194. $players[$counter]["Ping"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1195. if ($tmp & 32)
  1196. $players[$counter]["Time"] = $this->_GetStringByLength($recv, $this->_GetByteAsAscii($recv) - 1);
  1197. $counter++;
  1198. }
  1199.  
  1200. return $players;
  1201. }
  1202. // }}}
  1203.  
  1204. // {{{ _AllSeeingEyeAutoDetect() - Query Protocol: AllSeeingEye - Type: AutoDetect Methode
  1205. // @return bool always TRUE
  1206. // @access protected
  1207. function _AllSeeingEyeAutoDetect()
  1208. {
  1209. $this->_queryport = $this->_port + 123;
  1210. return TRUE;
  1211. }
  1212. // }}}
  1213.  
  1214. // {{{ _AllSeeingEyeFullInfo() - Query Protocol: AllSeeingEye - Type: Request Full Server Info
  1215. // @return array decoded data received from gameserver
  1216. // @access protected
  1217. function _AllSeeingEyeFullInfo()
  1218. {
  1219. $this->SetRequestData(array("Details", "Players"));
  1220. return $this->_AllSeeingEyeMain();
  1221. }
  1222. // }}}
  1223. // }}}
  1224.  
  1225. // {{{ Query Protocol: Quake1
  1226. // {{{ _Quake1Main() - Query Protocol: Quake1 - Type: Main Methode
  1227. // @return array decoded data received from gameserver
  1228. // bool FALSE if an error occur
  1229. // @access protected
  1230. function _Quake1Main()
  1231. {
  1232. $recv = "";
  1233. $prefix = "";
  1234. $tmp = array();
  1235. $data = array();
  1236.  
  1237. $this->_CreateUDPSocket();
  1238. $this->_SendSocket("\xFF\xFF\xFF\xFFstatus");
  1239. $recv = implode("", $this->_GetSocketDataNr(1));
  1240.  
  1241. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFFn\\", $prefix))
  1242. return FALSE;
  1243.  
  1244. $tmp["Details"] = substr($recv, 0, strpos($recv, "\n"))."\\";
  1245. $tmp["Players"] = substr($recv, strpos($recv,"\n") + 1);
  1246. $tmp["Players"] = substr($tmp["Players"], 0, strlen($tmp["Players"]) - 1);
  1247.  
  1248. foreach ($this->_gettypes as $type)
  1249. {
  1250. $data[$type] = call_user_func(array(&$this, "_".$this->_protocol.$type), $tmp[$type]);
  1251. if ($this->ERROR)
  1252. return FALSE;
  1253. }
  1254. $this->_CloseUDPSocket();
  1255.  
  1256. return $data;
  1257. }
  1258. // }}}
  1259.  
  1260. // {{{ _Quake1Details() - Query Protocol: Quake1 - Type: Server Details
  1261. // @return array decoded data received from gameserver
  1262. // @access protected
  1263. function _Quake1Details($recv)
  1264. {
  1265. $data = array();
  1266. $data = $this->_GetDelimitedVariables($recv, "\\");
  1267. return $data;
  1268. }
  1269. // }}}
  1270.  
  1271. // {{{ _Quake1Players() - Query Protocol: Quake1 - Type: Players
  1272. // @return array decoded data received from gameserver
  1273. // @access protected
  1274. function _Quake1Players($recv)
  1275. {
  1276. $counter = 0;
  1277. $data = array();
  1278. $player = array();
  1279. $players = array();
  1280.  
  1281. if (strlen($recv) == 0)
  1282. return $players;
  1283.  
  1284. $data = explode("\n", $recv);
  1285. foreach ($data as $line)
  1286. {
  1287. if (strlen($line) == 0)
  1288. continue;
  1289. if (preg_match("/^([-0-9]+) ([-0-9]+) ([-0-9]+) ([-0-9]+) \"(.*)\" \"(.*)\" ([-0-9]+) ([-0-9]+)$/i", $line, $player))
  1290. {
  1291. $players[$counter]["Number"] = $player[1];
  1292. $players[$counter]["Score"] = $player[2];
  1293. $players[$counter]["Time"] = $player[3];
  1294. $players[$counter]["Ping"] = $player[4];
  1295. $players[$counter]["Name"] = $player[5];
  1296. $players[$counter]["Skin"] = $player[6];
  1297. $players[$counter]["Score1"] = $player[7];
  1298. $players[$counter]["Score2"] = $player[8];
  1299. $counter++;
  1300. }
  1301. else
  1302. $this->_SoftError("Unknown players data format received in Protocol: \"".$this->_protocol."\"");
  1303. }
  1304. return $players;
  1305. }
  1306. // }}}
  1307.  
  1308. // {{{ _Quake1AutoDetect() - Query Protocol: Quake1 - Type: AutoDetect Methode
  1309. // @return bool always TRUE
  1310. // @access protected
  1311. function _Quake1AutoDetect()
  1312. {
  1313. $this->_queryport = $this->_port;
  1314. return TRUE;
  1315. }
  1316. // }}}
  1317.  
  1318. // {{{ _Quake1FullInfo() - Query Protocol: Quake1 - Type: Request Full Server Info
  1319. // @return array decoded data received from gameserver
  1320. function _Quake1FullInfo()
  1321. {
  1322. $this->SetRequestData(array("Details", "Players"));
  1323. return $this->_Quake1Main();
  1324. }
  1325. // }}}
  1326. // }}}
  1327.  
  1328. // {{{ Query Protocol: Quake2
  1329. // {{{ _Quake2Main() - Query Protocol: Quake2 - Type: Main Methode
  1330. // @return array decoded data received from gameserver
  1331. // bool FALSE if an error occur
  1332. // @access protected
  1333. function _Quake2Main()
  1334. {
  1335. $recv = "";
  1336. $prefix = "";
  1337. $tmp = array();
  1338. $data = array();
  1339.  
  1340. $this->_CreateUDPSocket();
  1341. $this->_SendSocket("\xFF\xFF\xFF\xFFstatus");
  1342. $recv = implode("", $this->_GetSocketDataNr(1));
  1343.  
  1344. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFFprint\x0A", $prefix))
  1345. return FALSE;
  1346.  
  1347. $tmp["Details"] = substr($recv, 0, strpos($recv, "\n"))."\\";
  1348. $tmp["Players"] = substr($recv, strpos($recv,"\n") + 1);
  1349. $tmp["Players"] = substr($tmp["Players"], 0, strlen($tmp["Players"]) - 1);
  1350.  
  1351. foreach ($this->_gettypes as $type)
  1352. {
  1353. $data[$type] = call_user_func(array(&$this, "_".$this->_protocol.$type), $tmp[$type]);
  1354. if ($this->ERROR)
  1355. return FALSE;
  1356. }
  1357. $this->_CloseUDPSocket();
  1358.  
  1359. return $data;
  1360. }
  1361. // }}}
  1362.  
  1363. // {{{ _Quake2Details() - Query Protocol: Quake2 - Type: Server Details
  1364. // @return array decoded data received from gameserver
  1365. // @access protected
  1366. function _Quake2Details($recv)
  1367. {
  1368. $data = array();
  1369. $data = $this->_GetDelimitedVariables($recv, "\\");
  1370. return $data;
  1371. }
  1372. // }}}
  1373.  
  1374. // {{{ _Quake2Players() - Query Protocol: Quake2 - Type: Players
  1375. // @return array decoded data received from gameserver
  1376. // @access protected
  1377. function _Quake2Players($recv)
  1378. {
  1379. $counter = 0;
  1380. $data = array();
  1381. $player = array();
  1382. $players = array();
  1383.  
  1384. if (strlen($recv) == 0)
  1385. return $players;
  1386.  
  1387. $data = explode("\n", $recv);
  1388. foreach ($data as $line)
  1389. {
  1390. if (strlen($line) == 0)
  1391. continue;
  1392. if (preg_match("/^([-0-9]+) ([-0-9]+) \"(.*)\"$/i", $line, $player))
  1393. {
  1394. $players[$counter]["Score"] = $player[1];
  1395. $players[$counter]["Ping"] = $player[2];
  1396. $players[$counter]["Name"] = $player[3];
  1397. $counter++;
  1398. }
  1399. else
  1400. $this->_SoftError("Unknown players data format received in Protocol: \"".$this->_protocol."\"");
  1401. }
  1402. return $players;
  1403. }
  1404. // }}}
  1405.  
  1406. // {{{ _Quake2AutoDetect() - Query Protocol: Quake2 - Type: AutoDetect Methode
  1407. // @return bool always TRUE
  1408. // @access protected
  1409. function _Quake2AutoDetect()
  1410. {
  1411. $this->_queryport = $this->_port;
  1412. return TRUE;
  1413. }
  1414. // }}}
  1415.  
  1416. // {{{ _Quake2FullInfo() - Query Protocol: Quake2 - Type: Request Full Server Info
  1417. // @return array decoded data received from gameserver
  1418. function _Quake2FullInfo()
  1419. {
  1420. $this->SetRequestData(array("Details", "Players"));
  1421. return $this->_Quake2Main();
  1422. }
  1423. // }}}
  1424. // }}}
  1425.  
  1426. // {{{ Query Protocol: Quake3
  1427. // {{{ _Quake3Main() - Query Protocol: Quake3 - Type: Main Methode
  1428. // @return array decoded data received from gameserver
  1429. // bool FALSE if an error occur
  1430. // @access protected
  1431. function _Quake3Main()
  1432. {
  1433. $recv = "";
  1434. $prefix = "";
  1435. $tmp = array();
  1436. $data = array();
  1437.  
  1438. $this->_CreateUDPSocket();
  1439. $this->_SendSocket("\xFF\xFF\xFF\xFFgetstatus\x0A");
  1440. $recv = implode("", $this->_GetSocketDataNr(1));
  1441.  
  1442. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFFstatusResponse\x0A\\", $prefix))
  1443. return FALSE;
  1444.  
  1445. $tmp["Details"] = substr($recv, 0, strpos($recv, "\n"))."\\";
  1446. $tmp["Players"] = substr($recv, strpos($recv,"\n") + 1);
  1447. $tmp["Players"] = substr($tmp["Players"], 0, strlen($tmp["Players"]) - 1);
  1448.  
  1449. foreach ($this->_gettypes as $type)
  1450. {
  1451. $data[$type] = call_user_func(array(&$this, "_".$this->_protocol.$type), $tmp[$type]);
  1452. if ($this->ERROR)
  1453. return FALSE;
  1454. }
  1455. $this->_CloseUDPSocket();
  1456.  
  1457. return $data;
  1458. }
  1459. // }}}
  1460.  
  1461. // {{{ _Quake3Details() - Query Protocol: Quake3 - Type: Server Details
  1462. // @return array decoded data received from gameserver
  1463. // @access protected
  1464. function _Quake3Details($recv)
  1465. {
  1466. $data = array();
  1467. $data = $this->_GetDelimitedVariables($recv, "\\");
  1468. return $data;
  1469. }
  1470. // }}}
  1471.  
  1472. // {{{ _Quake3Players() - Query Protocol: Quake3 - Type: Players
  1473. // @return array decoded data received from gameserver
  1474. // @access protected
  1475. function _Quake3Players($recv)
  1476. {
  1477. $data = array();
  1478. $player = array();
  1479. $players = array();
  1480.  
  1481. if (strlen($recv) == 0)
  1482. return $players;
  1483.  
  1484. $data = explode("\n", $recv);
  1485. foreach ($data as $line)
  1486. {
  1487. if (strlen($line) == 0)
  1488. continue;
  1489.  
  1490. if (preg_match("/^([-0-9]+) ([-0-9]+) \"(.*)\"$/i", $line, $player))
  1491. array_push($players, array("frags" => $player[1], "ping" => $player[2], "name" => $player[3]));
  1492. elseif (preg_match("/^([-0-9]+) ([-0-9]+) ([-0-9]+) \"(.*)\"$/i", $line, $player))
  1493. array_push($players, array("frags" => $player[1], "ping" => $player[2], "deaths" => $player[3], "name" => $player[4]));
  1494. else
  1495. $this->_SoftError("Unknown players data format received in Protocol: \"".$this->_protocol."\"");
  1496. }
  1497. return $players;
  1498. }
  1499. // }}}
  1500.  
  1501. // {{{ _Quake3AutoDetect() - Query Protocol: Quake3 - Type: AutoDetect Methode
  1502. // @return bool always TRUE
  1503. // @access protected
  1504. function _Quake3AutoDetect()
  1505. {
  1506. $this->_queryport = $this->_port;
  1507. return TRUE;
  1508. }
  1509. // }}}
  1510.  
  1511. // {{{ _Quake3FullInfo() - Query Protocol: Quake3 - Type: Request Full Server Info
  1512. // @return array decoded data received from gameserver
  1513. // @access protected
  1514. function _Quake3FullInfo()
  1515. {
  1516. $this->SetRequestData(array("Details", "Players"));
  1517. return $this->_Quake3Main();
  1518. }
  1519. // }}}
  1520. // }}}
  1521.  
  1522. // {{{ Query Protocol: Doom3
  1523. // {{{ _Doom3Main() - Query Protocol: Doom3 - Type: Main Methode
  1524. // @return array decoded data received from gameserver
  1525. // bool FALSE if an error occur
  1526. // @access protected
  1527. function _Doom3Main($protocol = 1)
  1528. {
  1529. $recv = "";
  1530. $prefix = "";
  1531. $tmp = array();
  1532. $data = array();
  1533.  
  1534. $this->_CreateUDPSocket();
  1535. $this->_SendSocket("\xFF\xFFgetInfo\x00\x00\x00\x00\x00");
  1536. $recv = implode("", $this->_GetSocketDataNr(5));
  1537.  
  1538. if (!$this->_CheckQueryHeader($recv, "\xFF\xFFinfoResponse\x00", $prefix))
  1539. return FALSE;
  1540.  
  1541. /* dirty hack to determine protocol version */
  1542. if ($tmp2 = strstr($recv, "si_version\x00"))
  1543. {
  1544. /* strip off si_version */
  1545. $this->_GetCharacterTerminatedString($tmp2, "\x00");
  1546. $version = $this->_GetCharacterTerminatedString($tmp2, "\x00");
  1547. if (strpos(strtolower($version), "doom") !== false)
  1548. $protocol = 1;
  1549. elseif (strpos(strtolower($version), "pray") !== false)
  1550. $protocol = 2;
  1551. elseif (strpos(strtolower($version), "quake4") !== false)
  1552. $protocol = 3;
  1553. elseif (strpos(strtolower($version), "etqw") !== false)
  1554. $protocol = 4;
  1555. }
  1556.  
  1557. if ($protocol == 4) /* ETQW */
  1558. $taskid = $this->_GetLongAsLong($recv);
  1559. $challenge = $this->_GetLongAsLong($recv);
  1560. $this->_protoversion = $this->_GetLongAsLong($recv);
  1561. $this->_protoversion = sprintf("%u.%u", $this->_protoversion >> 16, $this->_protoversion & 0xFFFF);
  1562. if ($protocol == 4) /* ETQW */
  1563. $this->_infosize = $this->_GetLongAsLong($recv);
  1564.  
  1565. $tmp["Details"] = $this->_Doom3Details($recv, $protocol);
  1566. if (!isset($tmp["Details"]["queryprotocol"]))
  1567. $tmp["Details"]["queryprotocol"] = $this->_protoversion;
  1568.  
  1569. $tmp["Players"] = $this->_Doom3Players($recv, $protocol);
  1570.  
  1571. $tmp["Details"]["osmask"] = $this->_GetLongAsLong($recv);
  1572. if ($protocol == 4) /* ETQW */
  1573. {
  1574. $tmp["Details"]["ranked"] = $this->_GetByteAsAscii($recv);
  1575. $tmp["Details"]["timeleft"] = $this->_GetInt32AsInt($recv);
  1576. $tmp["Details"]["gamestate"] = $this->_GetByteAsAscii($recv);
  1577. $tmp["Details"]["servertype"] = $this->_GetByteAsAscii($recv);
  1578. if ($tmp["Details"]["servertype"] == 0) /* regular server */
  1579. $tmp["Details"]["interested_clients"] = $this->_GetByteAsAscii($recv);
  1580. elseif ($tmp["Details"]["servertype"] == 0) /* tv server */
  1581. {
  1582. $tmp["Details"]["tv_numClients"] = $this->_GetByteAsAscii($recv);
  1583. $tmp["Details"]["tv_maxClients"] = $this->_GetByteAsAscii($recv);
  1584. }
  1585. }
  1586. if ($this->ERROR)
  1587. return FALSE;
  1588. $this->_CloseUDPSocket();
  1589.  
  1590. foreach ($this->_gettypes as $type)
  1591. $data[$type] = $tmp[$type];
  1592.  
  1593. return $data;
  1594. }
  1595. // }}}
  1596.  
  1597. // {{{ _Doom3Details() - Query Protocol: Doom3 - Type: Server Details
  1598. // @return array decoded data received from gameserver
  1599. // @access protected
  1600. function _Doom3Details(&$recv, $protocol = 1)
  1601. {
  1602. $key = "";
  1603. $data = array();
  1604.  
  1605. while (($key = $this->_GetCharacterTerminatedString($recv, "\x00")) !== FALSE)
  1606. {
  1607. $value = $this->_GetCharacterTerminatedString($recv, "\x00");
  1608. if ($key == "" && $value == "")
  1609. break;
  1610. $data[$key] = $value;
  1611. }
  1612.  
  1613. if ($protocol == 4) /* ETQW */
  1614. {
  1615. if (isset($data["si_map"]) && strpos($data["si_map"], ".entities") !== FALSE)
  1616. $data["si_map"] = substr($data["si_map"], 0, -9);
  1617. }
  1618.  
  1619. return $data;
  1620. }
  1621. // }}}
  1622.  
  1623. // {{{ _Doom3Players() - Query Protocol: Doom3 - Type: Players
  1624. // @return array decoded data received from gameserver
  1625. // @access protected
  1626. function _Doom3Players(&$recv, $protocol = 1)
  1627. {
  1628. $tmp = "";
  1629. $players = array();
  1630.  
  1631. if (strlen($recv) == 0)
  1632. return $players;
  1633.  
  1634. while (($number = $this->_GetByteAsAscii($recv)) != 32)
  1635. {
  1636. $players[$number]["Ping"] = $this->_GetByteAsAscii($recv) + $this->_GetByteAsAscii($recv) * 256;
  1637. if ($protocol != 4) /* ETQW */
  1638. $players[$number]["Rate"] = $this->_GetLongAsLong($recv);
  1639. $players[$number]["Name"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  1640. if ($protocol == 4) /* ETQW */
  1641. {
  1642. $players[$number]["ClanTagPos"] = ($this->_GetByteAsAscii($recv) == 1) ? 1 : 0;
  1643. $players[$number]["ClanTag"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  1644. }
  1645.  
  1646. if ($protocol == 3) /* Quake4 */
  1647. $players[$number]["Clantag"] = $this->_GetCharacterTerminatedString($recv, "\x00");
  1648. elseif ($protocol == 4) /* ETQW */
  1649. $players[$number]["Bot"] = ($this->_GetByteAsAscii($recv) == 1) ? 1 : 0;
  1650. }
  1651.  
  1652. return $players;
  1653. }
  1654. // }}}
  1655.  
  1656. // {{{ _Doom3AutoDetect() - Query Protocol: Doom3 - Type: AutoDetect Methode
  1657. // @return bool always TRUE
  1658. // @access protected
  1659. function _Doom3AutoDetect()
  1660. {
  1661. $this->_queryport = $this->_port;
  1662. return TRUE;
  1663. }
  1664. // }}}
  1665.  
  1666. // {{{ _Doom3FullInfo() - Query Protocol: Doom3 - Type: Request Full Server Info
  1667. // @return array decoded data received from gameserver
  1668. // @access protected
  1669. function _Doom3FullInfo($protocol = 1)
  1670. {
  1671. $this->SetRequestData(array("Details", "Players"));
  1672. return $this->_Doom3Main($protocol);
  1673. }
  1674. // }}}
  1675. // }}}
  1676.  
  1677. // {{{ Query Protocol: GameSpy
  1678. // {{{ _GameSpyMain() - Query Protocol: GameSpy - Type: Main Methode
  1679. // @return array decoded data received from gameserver
  1680. // bool FALSE if an error occur
  1681. // @access protected
  1682. function _GameSpyMain()
  1683. {
  1684. $data = array();
  1685. $this->_CreateUDPSocket();
  1686. foreach ($this->_gettypes as $type)
  1687. {
  1688. $data[$type] = call_user_func(array(&$this, "_".$this->_protocol.$type));
  1689. if ($this->ERROR)
  1690. return FALSE;
  1691. }
  1692. $this->_CloseUDPSocket();
  1693.  
  1694. return $data;
  1695. }
  1696. // }}}
  1697.  
  1698. // {{{ _GameSpyDetails() - Query Protocol: GameSpy - Type: Server Details
  1699. // @return array decoded data received from gameserver
  1700. // bool FALSE if an error occur
  1701. // @access protected
  1702. function _GameSpyDetails()
  1703. {
  1704. $recv = "";
  1705. $suffix = "";
  1706. $data = array();
  1707.  
  1708. $this->_SendSocket("\\info\\");
  1709. $recv = implode("", $this->_GetSocketDataNr(1));
  1710.  
  1711. if (!$this->_CheckQueryFooter($recv, "final\\", $suffix))
  1712. return FALSE;
  1713.  
  1714. $data = $this->_GetDelimitedVariables($recv, "\\");
  1715. return $data;
  1716. }
  1717. // }}}
  1718.  
  1719. // {{{ _GameSpyRules() - Query Protocol: GameSpy - Type: Server Rules
  1720. // @return array decoded data received from gameserver
  1721. // bool FALSE if an error occur
  1722. // @access protected
  1723. function _GameSpyRules()
  1724. {
  1725. $recv = "";
  1726. $suffix = "";
  1727. $data = array();
  1728.  
  1729. $this->_SendSocket("\\rules\\");
  1730. $recv = implode("", $this->_GetSocketDataNr(1));
  1731.  
  1732. if (!$this->_CheckQueryFooter($recv, "final\\", $suffix))
  1733. return FALSE;
  1734.  
  1735. $data = $this->_GetDelimitedVariables($recv, "\\");
  1736. return $data;
  1737. }
  1738. // }}}
  1739.  
  1740. // {{{ _GameSpyPlayers() - Query Protocol: GameSpy - Type: Players
  1741. // @return array decoded data received from gameserver
  1742. // bool FALSE if an error occur
  1743. // @access protected
  1744. function _GameSpyPlayers()
  1745. {
  1746. $recv = "";
  1747. $suffix = "";
  1748. $type = "";
  1749. $tmp = "";
  1750. $counter = 0;
  1751. $key = 0;
  1752. $data = array();
  1753.  
  1754. $this->_SendSocket("\\players\\");
  1755. $recv = implode("", $this->_GetSocketDataNr(1));
  1756. while ($counter < 4)
  1757. {
  1758. if (!$this->_CheckQueryFooter($recv, "final\\", $suffix))
  1759. $recv .= implode("", $this->_GetSocketDataNr(1));
  1760. else
  1761. {
  1762. $recv .= "final\\";
  1763. break;
  1764. }
  1765. $counter++;
  1766. }
  1767. $recv = substr($recv, 1);
  1768.  
  1769. if (!$this->_CheckQueryFooter($recv, "final\\", $suffix))
  1770. return FALSE;
  1771.  
  1772. while(strlen($recv) > 0)
  1773. {
  1774. $tmp = $this->_GetCharacterTerminatedString($recv, "\\");
  1775. if (($counter = strrpos($tmp, "_")) !== FALSE)
  1776. {
  1777. $type = substr($tmp, 0, $counter);
  1778. $key = intval(substr($tmp, $counter+1));
  1779. $data[$key][$type] = $this->_GetCharacterTerminatedString($recv, "\\");
  1780. }
  1781. }
  1782.  
  1783. return $data;
  1784. }
  1785. // }}}
  1786.  
  1787. // {{{ _GameSpyAutoDetect() - Query Protocol: GameSpy - Type: AutoDetect Methode
  1788. // @return bool always TRUE
  1789. // @access protected
  1790. function _GameSpyAutoDetect($arg = 1)
  1791. {
  1792. $this->_queryport = $this->_port + 1;
  1793. return TRUE;
  1794. }
  1795. // }}}
  1796.  
  1797. // {{{ _GameSpyFullInfo() - Query Protocol: GameSpy - Type: Request Full Server Info
  1798. // @return array decoded data received from gameserver
  1799. // @access protected
  1800. function _GameSpyFullInfo()
  1801. {
  1802. $this->SetRequestData(array("Details", "Rules", "Players"));
  1803. return $this->_GameSpyMain();
  1804. }
  1805. // }}}
  1806.  
  1807. // {{{ _GameSpyPortPlus10Main() - Query Protocol: GameSpy Port+10 - Type: Main Methode
  1808. // @return array decoded data received from gameserver
  1809. // bool FALSE if an error occur
  1810. // @access protected
  1811. function _GameSpyPortPlus10Main()
  1812. {
  1813. return $this->_GameSpyMain();
  1814. }
  1815. // }}}
  1816.  
  1817. // {{{ _GameSpyPortPlus10Details() - Query Protocol: GameSpy Port+10 - Type: Server Details
  1818. // @return array decoded data received from gameserver
  1819. // bool FALSE if an error occur
  1820. // @access protected
  1821. function _GameSpyPortPlus10Details()
  1822. {
  1823. return $this->_GameSpyDetails();
  1824. }
  1825. // }}}
  1826.  
  1827. // {{{ _GameSpyPortPlus10Rules() - Query Protocol: GameSpy Port+10 - Type: Server Rules
  1828. // @return array decoded data received from gameserver
  1829. // bool FALSE if an error occur
  1830. // @access protected
  1831. function _GameSpyPortPlus10Rules()
  1832. {
  1833. return $this->_GameSpyRules();
  1834. }
  1835. // }}}
  1836.  
  1837. // {{{ _GameSpyPortPlus10Players() - Query Protocol: GameSpy Port+10 - Type: Players
  1838. // @return array decoded data received from gameserver
  1839. // bool FALSE if an error occur
  1840. // @access protected
  1841. function _GameSpyPortPlus10Players()
  1842. {
  1843. return $this->_GameSpyPlayers();
  1844. }
  1845. // }}}
  1846.  
  1847. // {{{ _GameSpyPortPlus10AutoDetect() - Query Protocol: GameSpy Port+10 - Type: AutoDetect Methode
  1848. // @return bool always TRUE
  1849. // @access protected
  1850. function _GameSpyPortPlus10AutoDetect()
  1851. {
  1852. $this->_queryport = $this->_port + 10;
  1853. return TRUE;
  1854. }
  1855. // }}}
  1856.  
  1857. // {{{ _GameSpyPortPlus10FullInfo() - Query Protocol: GameSpy Port+10 - Type: Request Full Server Info
  1858. // @return array decoded data received from gameserver
  1859. // @access protected
  1860. function _GameSpyPortPlus10FullInfo()
  1861. {
  1862. return $this->_GameSpyFullInfo();
  1863. }
  1864. // }}}
  1865. // }}}
  1866.  
  1867. // {{{ Query Protocol: GameSpy2
  1868. // {{{ _GameSpy2Main() - Query Protocol: GameSpy2 - Type: Main Methode
  1869. // @return array decoded data received from gameserver
  1870. // bool FALSE if an error occur
  1871. // @access protected
  1872. function _GameSpy2Main()
  1873. {
  1874. $recv = "";
  1875. $prefix = "";
  1876. $tosend = "";
  1877. $data = array();
  1878.  
  1879. $tosend = "\xFE\xFD\x00\x04\x05\x06\x07";
  1880. $tosend .= (array_search("Details", $this->_gettypes) !== FALSE) ? "\xFF" : "\x00";
  1881. $tosend .= (array_search("Players", $this->_gettypes) !== FALSE) ? "\xFF" : "\x00";
  1882. $tosend .= (array_search("Teams", $this->_gettypes) !== FALSE) ? "\xFF" : "\x00";
  1883. $tosend .= "\x00\x00\x00";
  1884.  
  1885. $this->_CreateUDPSocket();
  1886. $this->_SendSocket($tosend);
  1887. $recv = implode("", $this->_GetSocketDataNr(1));
  1888.  
  1889. if (!$this->_CheckQueryHeader($recv, "\x00\x04\x05\x06\x07", $prefix))
  1890. return FALSE;
  1891.  
  1892. if (substr($recv, 0, 8) == "splitnum")
  1893. $recv = substr($recv, 11);
  1894.  
  1895. foreach ($this->_gettypes as $type)
  1896. {
  1897. $data[$type] = call_user_func_array(array(&$this, "_".$this->_protocol.$type), array(&$recv));
  1898. if ($this->ERROR)
  1899. return FALSE;
  1900. }
  1901. $this->_CloseUDPSocket();
  1902.  
  1903. return $data;
  1904. }
  1905. // }}}
  1906.  
  1907. // {{{ _GameSpy2Details() - Query Protocol: GameSpy2 - Type: Server Details
  1908. // @return array decoded data received from gameserver
  1909. // bool FALSE if an error occur
  1910. // @access protected
  1911. function _GameSpy2Details(&$recv)
  1912. {
  1913. $key = "";
  1914. $data = array();
  1915.  
  1916. while (($key = $this->_GetCharacterTerminatedString($recv, "\x00")) != "")
  1917. $data[$key] = $this->_GetCharacterTerminatedString($recv, "\x00");
  1918.  
  1919. if ($recv{0} == "\x00")
  1920. $recv = substr($recv, 1);
  1921.  
  1922. return $data;
  1923. }
  1924. // }}}
  1925.  
  1926. // {{{ _GameSpy2Players() - Query Protocol: GameSpy2 - Type: Players
  1927. // @return array decoded data received from gameserver
  1928. // bool FALSE if an error occur
  1929. // @access protected
  1930. function _GameSpy2Players(&$recv)
  1931. {
  1932. $tmp = "";
  1933. $item = 0;
  1934. $counter = 0;
  1935. $keys = array();
  1936. $data = array();
  1937.  
  1938. if (strlen($recv) == 0)
  1939. return $data;
  1940.  
  1941. $data["PlayerCount"] = $this->_GetByteAsAscii($recv);
  1942. $tmp = $this->_GetByteAsAscii($recv);
  1943. if ($tmp == ord("p"))
  1944. $recv = chr($tmp).$recv;
  1945. else
  1946. $data["PlayerCount"] += $tmp;
  1947.  
  1948. while (($tmp = $this->_GetCharacterTerminatedString($recv, "\x00")) != "")
  1949. {
  1950. if (substr($tmp, strlen($tmp) - 1) == "_")
  1951. $tmp = substr($tmp, 0, strlen($tmp) - 1);
  1952. array_push($keys, $tmp);
  1953. }
  1954.  
  1955. while (($tmp = $this->_GetCharacterTerminatedString($recv, "\x00")) != "")
  1956. {
  1957. $data[$counter][$keys[$item]] = $tmp;
  1958. $item++;
  1959. if ($item == count($keys))
  1960. {
  1961. $item = 0;
  1962. $counter++;
  1963. }
  1964. }
  1965.  
  1966. return $data;
  1967. }
  1968. // }}}
  1969.  
  1970. // {{{ _GameSpy2Teams() - Query Protocol: GameSpy2 - Type: Teams
  1971. // @return array decoded data received from gameserver
  1972. // bool FALSE if an error occur
  1973. // @access protected
  1974. function _GameSpy2Teams(&$recv)
  1975. {
  1976. $tmp = "";
  1977. $item = 0;
  1978. $counter = 0;
  1979. $keys = array();
  1980. $data = array();
  1981.  
  1982. if (strlen($recv) == 0)
  1983. return $data;
  1984.  
  1985. $numrules = $this->_GetByteAsAscii($recv);
  1986. while (($tmp = $this->_GetCharacterTerminatedString($recv, "\x00")) != "")
  1987. {
  1988. if (substr($tmp, strlen($tmp) - 1) == "_")
  1989. $tmp = substr($tmp, 0, strlen($tmp) - 1);
  1990. array_push($keys, $tmp);
  1991. }
  1992.  
  1993. while (($tmp = $this->_GetCharacterTerminatedString($recv, "\x00")) != "")
  1994. {
  1995. $data[$counter][$keys[$item]] = $tmp;
  1996. $item++;
  1997. if ($item == count($keys))
  1998. {
  1999. $item = 0;
  2000. $counter++;
  2001. }
  2002. }
  2003.  
  2004. return $data;
  2005. }
  2006. // }}}
  2007.  
  2008. // {{{ _GameSpy2AutoDetect() - Query Protocol: GameSpy2 - Type: AutoDetect Methode
  2009. // @return bool always TRUE
  2010. // @access protected
  2011. function _GameSpy2AutoDetect()
  2012. {
  2013. $this->_queryport = 23000;
  2014. return TRUE;
  2015. }
  2016. // }}}
  2017.  
  2018. // {{{ _GameSpy2FullInfo() - Query Protocol: GameSpy2 - Type: Request Full Server Info
  2019. // @return array decoded data received from gameserver
  2020. function _GameSpy2FullInfo()
  2021. {
  2022. $this->SetRequestData(array("Details", "Players", "Teams"));
  2023. return $this->_GameSpy2Main();
  2024. }
  2025. // }}}
  2026. // }}}
  2027.  
  2028. // {{{ Query Protocol: GameSpy3
  2029. // {{{ _GameSpy3Main() - Query Protocol: GameSpy3 - Type: Main Methode
  2030. // @return array decoded data received from gameserver
  2031. // bool FALSE if an error occur
  2032. // @access protected
  2033. function _GameSpy3Main($challenge = false)
  2034. {
  2035. $recv = "";
  2036. $recv_arr = array();
  2037. $recv_arr2 = array();
  2038. $prefix = "";
  2039. $tosend = "";
  2040. $data = array();
  2041. $end = 0;
  2042. $identifier = "\x04\x05\x06\x07";
  2043. $chall_str = "";
  2044.  
  2045. $this->_CreateUDPSocket();
  2046.  
  2047. if ($challenge)
  2048. {
  2049. $tosend = "\xFE\xFD\x09" . $identifier;
  2050. $this->_SendSocket($tosend);
  2051. $recv = implode("", $this->_GetSocketDataNr(1));
  2052. if (!$this->_CheckQueryHeader($recv, "\x09" . $identifier, $prefix))
  2053. return FALSE;
  2054. $chall_str = pack("N", (int)$recv);
  2055. }
  2056.  
  2057. $tosend = "\xFE\xFD\x00" . $identifier . $chall_str;
  2058. $tosend .= (array_search("Details", $this->_gettypes) !== FALSE) ? "\xFF" : "\x00";
  2059. $tosend .= (array_search("Players", $this->_gettypes) !== FALSE) ? "\xFF" : "\x00";
  2060. $tosend .= (array_search("Teams", $this->_gettypes) !== FALSE) ? "\xFF" : "\x00";
  2061. $tosend .= "\x01";
  2062. $this->_SendSocket($tosend);
  2063.  
  2064. while(!$end)
  2065. {
  2066. $recv = implode("", $this->_GetSocketDataNr(1));
  2067. if (!$this->_CheckQueryHeader($recv, "\x00" . $identifier, $prefix))
  2068. return FALSE;
  2069.  
  2070. $index = 0;
  2071. if (substr($recv, 0, 8) != "splitnum")
  2072. {
  2073. $end = 1;
  2074. }
  2075. else
  2076. {
  2077. $recv = substr($recv, 9);
  2078. $index = ord($recv{0});
  2079. if (ord($recv{0}) > ord("\x7F"))
  2080. {
  2081. $end = 1;
  2082. $index = count($recv_arr);
  2083. }
  2084. }
  2085.  
  2086. $recv_arr[$index] = substr($recv, 1);
  2087. }
  2088.  
  2089. $recv = "";
  2090. for ($i = 0; $i < count($recv_arr); $i++)
  2091. {
  2092. # we currently ignore the first byte
  2093. $recv_arr[$i] = substr($recv_arr[$i], 1);
  2094. $start = 0;
  2095. $end = strlen($recv_arr[$i]);
  2096.  
  2097. $recv_tmp = $recv_arr[$i];
  2098.  
  2099. # check head
  2100. $starttmp = $this->_GetCharacterTerminatedString($recv_tmp, "\x00");
  2101. if (substr($starttmp, -1) == "_")
  2102. $start = strlen($starttmp) + 2;
  2103.  
  2104. # check body
  2105. if (substr($recv_tmp, -2) != "\x00\x00")
  2106. {
  2107. $j = 0;
  2108. for($j = 2; (substr($recv_tmp, strlen($recv_tmp) - $j, 1) != "\x00" || $j == strlen($recv_tmp)); $j++);
  2109. $end = -$j + 1;
  2110. }
  2111.  
  2112. $recv .= substr($recv_arr[$i], $start, $end);
  2113. }
  2114.  
  2115. foreach ($this->_gettypes as $type)
  2116. {
  2117. $data[$type] = call_user_func_array(array(&$this, "_".$this->_protocol.$type), array(&$recv));
  2118. if ($this->ERROR)
  2119. return FALSE;
  2120. }
  2121. $this->_CloseUDPSocket();
  2122.  
  2123. return $data;
  2124. }
  2125. // }}}
  2126.  
  2127. // {{{ _GameSpy3Details() - Query Protocol: GameSpy2 - Type: Server Details
  2128. // @return array decoded data received from gameserver
  2129. // bool FALSE if an error occur
  2130. // @access protected
  2131. function _GameSpy3Details(&$recv)
  2132. {
  2133. return $this->_GameSpy2Details($recv);
  2134. }
  2135. // }}}
  2136.  
  2137. // {{{ _GameSpy3Players() - Query Protocol: GameSpy3 - Type: Players
  2138. // @return array decoded data received from gameserver
  2139. // bool FALSE if an error occur
  2140. // @access protected
  2141. function _GameSpy3Players(&$recv)
  2142. {
  2143. $data = $this->_GameSpy2Players($recv);
  2144. unset($data["PlayerCount"]);
  2145. return $data;
  2146. }
  2147. // }}}
  2148.  
  2149. // {{{ _GameSpy3Teams() - Query Protocol: GameSpy3 - Type: Teams
  2150. // @return array decoded data received from gameserver
  2151. // bool FALSE if an error occur
  2152. // @access protected
  2153. function _GameSpy3Teams(&$recv)
  2154. {
  2155. $tmp = "";
  2156. $item = 0;
  2157. $counter = 0;
  2158. $data = array();
  2159.  
  2160. if (strlen($recv) == 0)
  2161. return $data;
  2162.  
  2163. while(strlen($recv))
  2164. {
  2165. $key = $this->_GetCharacterTerminatedString($recv, "\x00");
  2166. $this->_GetCharacterTerminatedString($recv, "\x00");
  2167. if (substr($key, -1) == "_")
  2168. $key = substr($key, 0, -1);
  2169.  
  2170. $counter = 0;
  2171. while (($tmp = $this->_GetCharacterTerminatedString($recv, "\x00")) != "")
  2172. {
  2173. $data[$counter][$key] = $tmp;
  2174. $counter++;
  2175. }
  2176. }
  2177.  
  2178. return $data;
  2179. }
  2180. // }}}
  2181.  
  2182. // {{{ _GameSpy3AutoDetect() - Query Protocol: GameSpy3 - Type: AutoDetect Methode
  2183. // @return bool always TRUE
  2184. // @access protected
  2185. function _GameSpy3AutoDetect()
  2186. {
  2187. $this->_queryport = $this->_port + 13333;
  2188. return TRUE;
  2189. }
  2190. // }}}
  2191.  
  2192. // {{{ _GameSpy3FullInfo() - Query Protocol: GameSpy3 - Type: Request Full Server Info
  2193. // @return array decoded data received from gameserver
  2194. // @access protected
  2195. function _GameSpy3FullInfo()
  2196. {
  2197. $this->SetRequestData(array("Details", "Players", "Teams"));
  2198. return $this->_GameSpy3Main(false);
  2199. }
  2200. // }}}
  2201. // }}}
  2202.  
  2203. // {{{ Query Protocol: GameSpy4
  2204. // {{{ _GameSpy4Main() - Query Protocol: GameSpy4 - Type: Main Methode
  2205. // @return array decoded data received from gameserver
  2206. // bool FALSE if an error occur
  2207. // @access protected
  2208. function _GameSpy4Main()
  2209. {
  2210. return $this->_GameSpy3Main(true);
  2211. }
  2212. // }}}
  2213.  
  2214. // {{{ _GameSpy4Details() - Query Protocol: GameSpy2 - Type: Server Details
  2215. // @return array decoded data received from gameserver
  2216. // bool FALSE if an error occur
  2217. // @access protected
  2218. function _GameSpy4Details(&$recv)
  2219. {
  2220. return $this->_GameSpy3Details($recv);
  2221. }
  2222. // }}}
  2223.  
  2224. // {{{ _GameSpy4Players() - Query Protocol: GameSpy4 - Type: Players
  2225. // @return array decoded data received from gameserver
  2226. // bool FALSE if an error occur
  2227. // @access protected
  2228. function _GameSpy4Players(&$recv)
  2229. {
  2230. return $this->_GameSpy3Players($recv);
  2231. }
  2232. // }}}
  2233.  
  2234. // {{{ _GameSpy4Teams() - Query Protocol: GameSpy4 - Type: Teams
  2235. // @return array decoded data received from gameserver
  2236. // bool FALSE if an error occur
  2237. // @access protected
  2238. function _GameSpy4Teams(&$recv)
  2239. {
  2240. return $this->_GameSpy3Teams($recv);
  2241. }
  2242. // }}}
  2243.  
  2244. // {{{ _GameSpy4AutoDetect() - Query Protocol: GameSpy4 - Type: AutoDetect Methode
  2245. // @return bool always TRUE
  2246. // @access protected
  2247. function _GameSpy4AutoDetect()
  2248. {
  2249. return $this->_GameSpy3AutoDetect();
  2250. }
  2251. // }}}
  2252.  
  2253. // {{{ _GameSpy4FullInfo() - Query Protocol: GameSpy4 - Type: Request Full Server Info
  2254. // @return array decoded data received from gameserver
  2255. // @access protected
  2256. function _GameSpy4FullInfo()
  2257. {
  2258. $this->SetRequestData(array("Details", "Players", "Teams"));
  2259. return $this->_GameSpy3Main(true);
  2260. }
  2261. // }}}
  2262. // }}}
  2263. // }}}
  2264. }
  2265.  
  2266. /* {{{ Description
  2267.  * RCon Query Protocol Class
  2268.  *
  2269.  * Adds RCon Support to the Gameserver Query Class
  2270.  *
  2271.  * Supported Protocols:
  2272.  * - Half-Life ("HalfLife")
  2273.  *
  2274.  * Users:
  2275.  * $this->SetRConPassword("rconpassword")
  2276.  * ... rcon password to use for query
  2277.  *
  2278.  * $this->SetRConCommand("command")
  2279.  * ... rcon command to send
  2280.  *
  2281.  * $this->SetRequestData(array("RCon"))
  2282.  * ... "RCon" is the only available requestdata type
  2283.  *
  2284.  * Developers:
  2285.  * This class extends the gameserver query protocol
  2286.  * If you want to add your own rcon query, the only needed method must be named like:
  2287.  * _RConYourNameMain() ... methode will be called automatically from engine
  2288.  *
  2289.  }}} */
  2290.  
  2291. class RCon extends GSQuery
  2292. {
  2293. // {{{ global variable definitions
  2294. var $_rconcommand;
  2295. var $_rconpassword;
  2296. // }}}
  2297.  
  2298. // {{{ RCon() - main class constructor
  2299. function RCon()
  2300. {
  2301. $_rconcommand = "";
  2302. $_rconpassword = "";
  2303. $this->GSQuery();
  2304. return TRUE;
  2305. }
  2306. // }}}
  2307.  
  2308. // {{{ SetProtocol() - sets query protocol to use
  2309. // @param string $arg1 protocol to use for gameserver query
  2310. // @return bool always TRUE
  2311. // @access public
  2312. function SetProtocol($string)
  2313. {
  2314. GSQuery::SetProtocol("RCon".$string);
  2315. return TRUE;
  2316. }
  2317. // }}}
  2318.  
  2319. // {{{ SetRConCommand() - sets rcon command to send
  2320. // @param string $arg1 rcon command to send to the gameserver
  2321. // @return bool always TRUE
  2322. // @access public
  2323. function SetRconCommand($string)
  2324. {
  2325. $this->_rconcommand = $string;
  2326. trigger_error("<b>Set RCon Command</b>: ".$string);
  2327. return TRUE;
  2328. }
  2329. // }}}
  2330.  
  2331. // {{{ SetRConPassword() - sets rcon password
  2332. // @param string $arg1 rcon password to use for rcon query
  2333. // @return bool always TRUE
  2334. // @access public
  2335. function SetRConPassword($string)
  2336. {
  2337. $this->_rconpassword = $string;
  2338. trigger_error("<b>Set RCon Password</b>: ".$string);
  2339. return TRUE;
  2340. }
  2341. // }}}
  2342.  
  2343. // {{{ _CheckSettings() - check for valid settings
  2344. // @return bool FALSE if an error occur
  2345. // TRUE otherwise
  2346. // @access protected
  2347. function _CheckSettings()
  2348. {
  2349. GSQuery::_CheckSettings();
  2350.  
  2351. if ($this->_rconcommand == "")
  2352. $this->_SoftError("No RCon Command set");
  2353. if ($this->_rconpassword == "")
  2354. $this->_SoftError("No RCon Password set");
  2355.  
  2356. if ($this->ERROR)
  2357. return FALSE;
  2358.  
  2359. return TRUE;
  2360. }
  2361. // }}}
  2362.  
  2363. // {{{ RCon Protocol: HalfLife
  2364. // {{{ _RConHalfLifeMain() - RCon Protocol: HalfLife
  2365. // @return array decoded data received from gameserver
  2366. // bool FALSE if an error occur
  2367. // @access protected
  2368. function _RConHalfLifeMain()
  2369. {
  2370. $recv = "";
  2371. $chnum = 0;
  2372. $data = array();
  2373.  
  2374. $this->_CreateUDPSocket();
  2375. $this->_SendSocket("\xFF\xFF\xFF\xFFchallenge rcon\x00");
  2376. $recv = implode("", $this->_GetSocketDataNr(1));
  2377.  
  2378. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFFchallenge rcon ", $prefix))
  2379. return FALSE;
  2380.  
  2381. $chnum = substr($recv, 0, strlen($recv) - 2);
  2382. $this->_SendSocket("\xFF\xFF\xFF\xFFrcon $chnum ".$this->_rconpassword." ".$this->_rconcommand."\n");
  2383. $recv = implode("", $this->_GetSocketDataNr(1));
  2384.  
  2385. if (!$this->_CheckQueryHeader($recv, "\xFF\xFF\xFF\xFF", $prefix))
  2386. return FALSE;
  2387.  
  2388. $data["RCon"] = substr($recv, 1, strlen($recv) - 3);
  2389.  
  2390. if ($data["RCon"] == "Bad rcon_password." || $data["RCon"] == "Bad challenge.")
  2391. {
  2392. $this->_SoftError("RCon Protocol: Half-Life - ".$data["RCon"]);
  2393. return FALSE;
  2394. }
  2395.  
  2396. $this->_CloseUDPSocket();
  2397.  
  2398. return $data;
  2399. }
  2400. // }}}
  2401. // }}}
  2402. }
  2403.  
  2404. echo "<pre>\n";
  2405. define("DEBUG", TRUE);
  2406. $query = new GSQuery();
  2407. #$query->SetProtocol("AutoDetect");
  2408. $query->SetProtocol("GameSpy3");
  2409. #$query->SetProtocol("Doom3");
  2410. #$query->SetProtocols(array("HalfLife", "Quake2", "Quake3", "GameSpy", "GameSpyPortPlus10", "GameSpy2", "GameSpy3", "GameSpy4", "AllSeeingEye", "Doom3"));
  2411. #$query->SetProtocols(array("Doom3"));
  2412. #$query->SetProtocols(array("Quake4"));
  2413. if (isset($_GET["queryport"]) && $_GET["queryport"] != "")
  2414. $query->SetIpPort($_GET["ip"].":".$_GET["port"].":".$_GET["queryport"]);
  2415. else
  2416. $query->SetIpPort($_GET["ip"].":".$_GET["port"]);
  2417. $query->SetRequestData(array("FullInfo"));
  2418. $query->SetSocketTimeOut(0, 100000);
  2419. $query->SetSocketLoopTimeOut(1000);
  2420. $data = $query->GetData();
  2421.  
  2422. print_r($data);
  2423. echo "</pre>\n";
  2424.  
  2425. ?>
  2426.