PageRenderTime 57ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/engine/System/vendor/austinb/gameq/gameq/protocols/gamespy3.php

https://gitlab.com/leon0399/damnit-engine
PHP | 446 lines | 180 code | 70 blank | 196 comment | 28 complexity | 8a6245e3265214718e82c7384001fe55 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of GameQ.
  4. *
  5. * GameQ is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GameQ is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /**
  19. * GameSpy3 Protocol Class
  20. *
  21. * This class is used as the basis for all game servers
  22. * that use the GameSpy3 protocol for querying
  23. * server status.
  24. *
  25. * Note: UT3 and Crysis2 have known issues with GSv3 responses
  26. *
  27. * @author Austin Bischoff <austin@codebeard.com>
  28. */
  29. class GameQ_Protocols_Gamespy3 extends GameQ_Protocols
  30. {
  31. /**
  32. * Set the packet mode to linear
  33. *
  34. * @var string
  35. */
  36. protected $packet_mode = self::PACKET_MODE_LINEAR;
  37. /**
  38. * Array of packets we want to look up.
  39. * Each key should correspond to a defined method in this or a parent class
  40. *
  41. * @var array
  42. */
  43. protected $packets = array(
  44. self::PACKET_CHALLENGE => "\xFE\xFD\x09\x10\x20\x30\x40",
  45. self::PACKET_ALL => "\xFE\xFD\x00\x10\x20\x30\x40%s\xFF\xFF\xFF\x01",
  46. );
  47. /**
  48. * Methods to be run when processing the response(s)
  49. *
  50. * @var array
  51. */
  52. protected $process_methods = array(
  53. "process_all",
  54. );
  55. /**
  56. * Default port for this server type
  57. *
  58. * @var int
  59. */
  60. protected $port = 1; // Default port, used if not set when instanced
  61. /**
  62. * The protocol being used
  63. *
  64. * @var string
  65. */
  66. protected $protocol = 'gamespy3';
  67. /**
  68. * String name of this protocol class
  69. *
  70. * @var string
  71. */
  72. protected $name = 'gamespy3';
  73. /**
  74. * Longer string name of this protocol class
  75. *
  76. * @var string
  77. */
  78. protected $name_long = "Gamespy3";
  79. /**
  80. * Parse the challenge response and apply it to all the packet types
  81. * that require it.
  82. *
  83. * @see GameQ_Protocols_Core::parseChallengeAndApply()
  84. */
  85. protected function parseChallengeAndApply()
  86. {
  87. // Pull out the challenge
  88. $challenge = substr(preg_replace( "/[^0-9\-]/si", "", $this->challenge_buffer->getBuffer()), 1);
  89. $challenge_result = sprintf(
  90. "%c%c%c%c",
  91. ( $challenge >> 24 ),
  92. ( $challenge >> 16 ),
  93. ( $challenge >> 8 ),
  94. ( $challenge >> 0 )
  95. );
  96. // Apply the challenge and return
  97. return $this->challengeApply($challenge_result);
  98. }
  99. /*
  100. * Internal methods
  101. */
  102. protected function preProcess_all($packets)
  103. {
  104. $return = array();
  105. // Get packet index, remove header
  106. foreach ($packets as $index => $packet)
  107. {
  108. // Make new buffer
  109. $buf = new GameQ_Buffer($packet);
  110. // Skip the header
  111. $buf->skip(14);
  112. // Get the current packet and make a new index in the array
  113. $return[$buf->readInt16()] = $buf->getBuffer();
  114. }
  115. unset($buf);
  116. // Sort packets, reset index
  117. ksort($return);
  118. // Grab just the values
  119. $return = array_values($return);
  120. // Compare last var of current packet with first var of next packet
  121. // On a partial match, remove last var from current packet,
  122. // variable header from next packet
  123. for ($i = 0, $x = count($return); $i < $x - 1; $i++)
  124. {
  125. // First packet
  126. $fst = substr($return[$i], 0, -1);
  127. // Second packet
  128. $snd = $return[$i+1];
  129. // Get last variable from first packet
  130. $fstvar = substr($fst, strrpos($fst, "\x00")+1);
  131. // Get first variable from last packet
  132. $snd = substr($snd, strpos($snd, "\x00")+2);
  133. $sndvar = substr($snd, 0, strpos($snd, "\x00"));
  134. // Check if fstvar is a substring of sndvar
  135. // If so, remove it from the first string
  136. if (strpos($sndvar, $fstvar) !== false)
  137. {
  138. $return[$i] = preg_replace("#(\\x00[^\\x00]+\\x00)$#", "\x00", $return[$i]);
  139. }
  140. }
  141. // Now let's loop the return and remove any dupe prefixes
  142. for($x = 1; $x < count($return); $x++)
  143. {
  144. $buf = new GameQ_Buffer($return[$x]);
  145. $prefix = $buf->readString();
  146. // Check to see if the return before has the same prefix present
  147. if($prefix != null && strstr($return[($x-1)], $prefix))
  148. {
  149. // Update the return by removing the prefix plus 2 chars
  150. $return[$x] = substr(str_replace($prefix, '', $return[$x]), 2);
  151. }
  152. unset($buf);
  153. }
  154. unset($x, $i, $snd, $sndvar, $fst, $fstvar);
  155. // Implode into a string and return
  156. return implode("", $return);
  157. }
  158. protected function process_all()
  159. {
  160. // Make sure we have a valid response
  161. if(!$this->hasValidResponse(self::PACKET_ALL))
  162. {
  163. return array();
  164. }
  165. // Set the result to a new result instance
  166. $result = new GameQ_Result();
  167. // Parse the response
  168. $data = $this->preProcess_all($this->packets_response[self::PACKET_ALL]);
  169. // Create a new buffer
  170. $buf = new GameQ_Buffer($data);
  171. // We go until we hit an empty key
  172. while($buf->getLength())
  173. {
  174. $key = $buf->readString();
  175. if (strlen($key) == 0)
  176. {
  177. break;
  178. }
  179. $result->add($key, $buf->readString());
  180. }
  181. // Now we need to offload to parse the remaining data, player and team information
  182. $this->parsePlayerTeamInfo($buf, $result);
  183. // Return the result
  184. return $result->fetch();
  185. }
  186. protected function delete_result(&$result, $array)
  187. {
  188. foreach($array as $key)
  189. {
  190. unset($result[$key]);
  191. }
  192. return TRUE;
  193. }
  194. protected function move_result(&$result, $old, $new)
  195. {
  196. if (isset($result[$old]))
  197. {
  198. $result[$new] = $result[$old];
  199. unset($result[$old]);
  200. }
  201. return TRUE;
  202. }
  203. /**
  204. * Parse the player and team information but do it smartly. Update to the old parseSub method.
  205. *
  206. * @param GameQ_Buffer $buf
  207. * @param GameQ_Result $result
  208. */
  209. protected function parsePlayerTeamInfo(GameQ_Buffer &$buf, GameQ_Result &$result)
  210. {
  211. /*
  212. * Explode the data into groups. First is player, next is team (item_t)
  213. *
  214. * Each group should be as follows:
  215. *
  216. * [0] => item_
  217. * [1] => information for item_
  218. * ...
  219. */
  220. $data = explode("\x00\x00", $buf->getBuffer());
  221. // By default item_group is blank, this will be set for each loop thru the data
  222. $item_group = '';
  223. // By default the item_type is blank, this will be set on each loop
  224. $item_type = '';
  225. // Loop through all of the $data for information and pull it out into the result
  226. for($x=0; $x < count($data)-1; $x++)
  227. {
  228. // Pull out the item
  229. $item = $data[$x];
  230. // If this is an empty item, move on
  231. if($item == '' || $item == "\x00")
  232. {
  233. continue;
  234. }
  235. /*
  236. * Left as reference:
  237. *
  238. * Each block of player_ and team_t have preceeding junk chars
  239. *
  240. * player_ is actually \x01player_
  241. * team_t is actually \x00\x02team_t
  242. *
  243. * Probably a by-product of the change to exploding the data from the original.
  244. *
  245. * For now we just strip out these characters
  246. */
  247. // Check to see if $item has a _ at the end, this is player info
  248. if(substr($item, -1) == '_')
  249. {
  250. // Set the item group
  251. $item_group = 'players';
  252. // Set the item type, rip off any trailing stuff and bad chars
  253. $item_type = rtrim(str_replace("\x01", '', $item), '_');
  254. }
  255. // Check to see if $item has a _t at the end, this is team info
  256. elseif(substr($item, -2) == '_t')
  257. {
  258. // Set the item group
  259. $item_group = 'teams';
  260. // Set the item type, rip off any trailing stuff and bad chars
  261. $item_type = rtrim(str_replace(array("\x00", "\x02"), '', $item), '_t');
  262. }
  263. // We can assume it is data belonging to a previously defined item
  264. else
  265. {
  266. // Make a temp buffer so we have easier access to the data
  267. $buf_temp = new GameQ_Buffer($item);
  268. // Get the values
  269. while ($buf_temp->getLength())
  270. {
  271. // No value so break the loop, end of string
  272. if (($val = $buf_temp->readString()) === '')
  273. {
  274. break;
  275. }
  276. // Add the value to the proper item in the correct group
  277. $result->addSub($item_group, $item_type, trim($val));
  278. }
  279. // Unset out buffer
  280. unset($buf_temp);
  281. }
  282. }
  283. // Free up some memory
  284. unset($data, $item, $item_group, $item_type, $val);
  285. }
  286. /**
  287. * Parse the player and team info
  288. *
  289. * @param GameQ_Buffer $buf
  290. * @param GameQ_Result $result
  291. * @throws GameQ_ProtocolsException
  292. * @return boolean
  293. */
  294. protected function parsePlayerTeamInfoNew(GameQ_Buffer &$buf, GameQ_Result &$result)
  295. {
  296. /**
  297. * Player info is always first, team info (if defined) is second.
  298. *
  299. * Reference:
  300. *
  301. * Player info is preceeded by a hex code of \x01
  302. * Team info is preceeded by a hex code of \x02
  303. */
  304. // Check the header to make sure the player data is proper
  305. if($buf->read(1) != "\x01")
  306. {
  307. //throw new GameQ_ProtocolsException("First character in player buffer != '\x01'");
  308. return FALSE;
  309. }
  310. // Offload the player parsing
  311. $this->parseSubInfo('players', $buf->readString("\x00\x00\x00"), $result);
  312. // Check to make sure we have team information
  313. if($buf->getLength() >= 6)
  314. {
  315. // Burn chars
  316. $buf->skip(2);
  317. // Check the header to make sure the data is proper
  318. if($buf->read(1) != "\x02")
  319. {
  320. //throw new GameQ_ProtocolsException("First character in team buffer != '\x02'");
  321. return FALSE;
  322. }
  323. // Offload the team parsing
  324. $this->parseSubInfo('teams', $buf->readString("\x00\x00\x00"), $result);
  325. }
  326. return TRUE;
  327. }
  328. /**
  329. * Parse the sub-item information for players and teams
  330. *
  331. * @param string $section
  332. * @param string $data
  333. * @param GameQ_Result $result
  334. * @return boolean
  335. */
  336. protected function parseSubInfo($section, $data, GameQ_Result &$result)
  337. {
  338. /*
  339. * Explode the items so we can iterate easier
  340. *
  341. * Items should split up as follows:
  342. *
  343. * [0] => item_
  344. * [1] => data for item_
  345. * [2] => item2_
  346. * [3] => data for item2_
  347. * ...
  348. */
  349. $items = explode("\x00\x00", $data);
  350. print_r($items);
  351. // Loop through all of the items
  352. for($x = 0; $x < count($items); $x += 2)
  353. {
  354. // $x is always the key for the item (i.e. player_, ping_, team_, score_, etc...)
  355. $item_type = rtrim($items[$x], '_,_t');
  356. // $x+1 is always the data for the above item
  357. // Make a temp buffer so we have easier access to the data
  358. $buf_temp = new GameQ_Buffer($items[$x+1]);
  359. // Get the values
  360. while ($buf_temp->getLength())
  361. {
  362. // No value so break the loop, end of string
  363. if (($val = $buf_temp->readString()) === '')
  364. {
  365. break;
  366. }
  367. // Add the value to the proper item in the correct group
  368. $result->addSub($section, $item_type, trim($val));
  369. }
  370. // Unset out buffer
  371. unset($buf_temp, $val);
  372. }
  373. unset($x, $items, $item_type);
  374. return TRUE;
  375. }
  376. }