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

/gameq/protocols/core.php

http://github.com/Austinb/GameQ
PHP | 637 lines | 263 code | 80 blank | 294 comment | 22 complexity | e27f828f2d7c6013f9d3389fa36b6544 MD5 | raw file
Possible License(s): GPL-3.0
  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 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 General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. *
  19. */
  20. /**
  21. * Handles the core functionality for the protocols
  22. *
  23. * @author Austin Bischoff <austin@codebeard.com>
  24. */
  25. abstract class GameQ_Protocols_Core
  26. {
  27. /*
  28. * Constants for class states
  29. */
  30. const STATE_TESTING = 1;
  31. const STATE_BETA = 2;
  32. const STATE_STABLE = 3;
  33. const STATE_DEPRECATED = 4;
  34. /*
  35. * Constants for packet keys
  36. */
  37. const PACKET_ALL = 'all'; // Some protocols allow all data to be sent back in one call.
  38. const PACKET_BASIC = 'basic';
  39. const PACKET_CHALLENGE = 'challenge';
  40. const PACKET_CHANNELS = 'channels'; // Voice servers
  41. const PACKET_DETAILS = 'details';
  42. const PACKET_INFO = 'info';
  43. const PACKET_PLAYERS = 'players';
  44. const PACKET_STATUS = 'status';
  45. const PACKET_RULES = 'rules';
  46. const PACKET_VERSION = 'version';
  47. /*
  48. * Transport constants
  49. */
  50. const TRANSPORT_UDP = 'udp';
  51. const TRANSPORT_TCP = 'tcp';
  52. /**
  53. * Can only send one packet at a time, slower
  54. *
  55. * @var string
  56. */
  57. const PACKET_MODE_LINEAR = 'linear';
  58. /**
  59. * Can send multiple packets at once and get responses, after challenge request (if required)
  60. *
  61. * @var string
  62. */
  63. const PACKET_MODE_MULTI = 'multi';
  64. /**
  65. * Current version of this class
  66. *
  67. * @var string
  68. */
  69. protected $version = '2.0';
  70. /**
  71. * Short name of the protocol
  72. *
  73. * @var string
  74. */
  75. protected $name = 'unnamed';
  76. /**
  77. * The longer, fancier name for the protocol
  78. *
  79. * @var string
  80. */
  81. protected $name_long = 'unnamed';
  82. /**
  83. * IP address of the server we are querying.
  84. *
  85. * @var string
  86. */
  87. protected $ip = '127.0.0.1';
  88. /**
  89. * Port of the server we are querying.
  90. *
  91. * @var mixed FALSE|int
  92. */
  93. protected $port = NULL;
  94. /**
  95. * The trasport method to use to actually send the data
  96. * Default is UDP
  97. *
  98. * @var string UDP|TCP
  99. */
  100. protected $transport = self::TRANSPORT_UDP;
  101. /**
  102. * The protocol type used when querying the server
  103. *
  104. * @var string
  105. */
  106. protected $protocol = 'unknown';
  107. /**
  108. * Packets Mode is multi by default since most games support it
  109. *
  110. * @var string
  111. */
  112. protected $packet_mode = self::PACKET_MODE_MULTI;
  113. /**
  114. * Holds the valid packet types this protocol has available.
  115. *
  116. * @var array
  117. */
  118. protected $packets = array();
  119. /**
  120. * Holds the list of methods to run when parsing the packet response(s) data. These
  121. * methods should provide all the return information.
  122. *
  123. * @var array()
  124. */
  125. protected $process_methods = array();
  126. /**
  127. * The packet responses received
  128. *
  129. * @var array
  130. */
  131. protected $packets_response = array();
  132. /**
  133. * Holds the instance of the result class
  134. *
  135. * @var GameQ_Result
  136. */
  137. protected $result = NULL;
  138. /**
  139. * Options for this protocol
  140. *
  141. * @var array
  142. */
  143. protected $options = array();
  144. /**
  145. * Holds the challenge response, if there is a challenge needed.
  146. *
  147. * @var array
  148. */
  149. protected $challenge_response = NULL;
  150. /**
  151. * Holds the challenge buffer.
  152. *
  153. * @var GameQ_Buffer
  154. */
  155. protected $challenge_buffer = NULL;
  156. /**
  157. * Holds the result of the challenge, if any
  158. * Will hold the error here
  159. *
  160. * @var mixed
  161. */
  162. protected $challenge_result = TRUE;
  163. /**
  164. * Define the state of this class
  165. *
  166. * @var int
  167. */
  168. protected $state = self::STATE_STABLE;
  169. /**
  170. * Holds and changes we want to make to the normailze filter
  171. *
  172. * @var array
  173. */
  174. protected $normalize = FALSE;
  175. /**
  176. * Create the instance.
  177. *
  178. * @param string $ip
  179. * @param mixed $port false|int
  180. * @param array $options
  181. */
  182. public function __construct($ip = FALSE, $port = FALSE, $options = array())
  183. {
  184. $this->ip($ip);
  185. // We have a specific port set so let's set it.
  186. if($port !== FALSE)
  187. {
  188. $this->port($port);
  189. }
  190. // We have passed options so let's set them
  191. if(!empty($options))
  192. {
  193. $this->options($options);
  194. }
  195. }
  196. /**
  197. * String name of this class
  198. */
  199. public function __toString()
  200. {
  201. return $this->name;
  202. }
  203. /**
  204. * Get an option's value
  205. *
  206. * @param string $option
  207. * @return mixed
  208. */
  209. public function __get($option)
  210. {
  211. return isset($this->options[$option]) ? $this->options[$option] : NULL;
  212. }
  213. /**
  214. * Set an option's value
  215. *
  216. * @param string $option
  217. * @param mixed $value
  218. * @return boolean
  219. */
  220. public function __set($option, $value)
  221. {
  222. $this->options[$option] = $value;
  223. return TRUE;
  224. }
  225. /**
  226. * Short (callable) name of this class
  227. *
  228. * @return string
  229. */
  230. public function name()
  231. {
  232. return $this->name;
  233. }
  234. /**
  235. * Long name of this class
  236. */
  237. public function name_long()
  238. {
  239. return $this->name_long;
  240. }
  241. /**
  242. * Return the status of this Protocol Class
  243. */
  244. public function state()
  245. {
  246. return $this->state;
  247. }
  248. /**
  249. * Return the packet mode for this protocol
  250. */
  251. public function packet_mode()
  252. {
  253. return $this->packet_mode;
  254. }
  255. /**
  256. * Return the protocol property
  257. *
  258. */
  259. public function protocol()
  260. {
  261. return $this->protocol;
  262. }
  263. /**
  264. * Get/set the ip address of the server
  265. *
  266. * @param string $ip
  267. */
  268. public function ip($ip = FALSE)
  269. {
  270. // Act as setter
  271. if($ip !== FALSE)
  272. {
  273. $this->ip = $ip;
  274. }
  275. return $this->ip;
  276. }
  277. /**
  278. * Get/set the port of the server
  279. *
  280. * @param int $port
  281. */
  282. public function port($port = FALSE)
  283. {
  284. // Act as setter
  285. if($port !== FALSE)
  286. {
  287. $this->port = $port;
  288. }
  289. return $this->port;
  290. }
  291. /**
  292. * Get/set the transport type for this protocol
  293. *
  294. * @param string $type
  295. */
  296. public function transport($type = FALSE)
  297. {
  298. // Act as setter
  299. if($type !== FALSE)
  300. {
  301. $this->transport = $type;
  302. }
  303. return $this->transport;
  304. }
  305. /**
  306. * Set the options for the protocol call
  307. *
  308. * @param array $options
  309. */
  310. public function options($options = Array())
  311. {
  312. // Act as setter
  313. if(!empty($options))
  314. {
  315. $this->options = $options;
  316. }
  317. return $this->options;
  318. }
  319. /**
  320. * Determine whether or not this protocol has some kind of challenge
  321. */
  322. public function hasChallenge()
  323. {
  324. return (isset($this->packets[self::PACKET_CHALLENGE]) && !empty($this->packets[self::PACKET_CHALLENGE]));
  325. }
  326. /**
  327. * See if the challenge was ok
  328. */
  329. public function challengeOK()
  330. {
  331. return ($this->challenge_result === TRUE);
  332. }
  333. /**
  334. * Get/set the challenge response
  335. *
  336. * @param array $response
  337. */
  338. public function challengeResponse($response = Array())
  339. {
  340. // Act as setter
  341. if(!empty($response))
  342. {
  343. $this->challenge_response = $response;
  344. }
  345. return $this->challenge_response;
  346. }
  347. /**
  348. * Get/set the challenge result
  349. *
  350. * @param string $result
  351. */
  352. public function challengeResult($result = FALSE)
  353. {
  354. // Act as setter
  355. if(!empty($result))
  356. {
  357. $this->challenge_result = $result;
  358. }
  359. return $this->challenge_result;
  360. }
  361. /**
  362. * Get/set the challenge buffer
  363. *
  364. * @param GameQ_Buffer $buffer
  365. */
  366. public function challengeBuffer($buffer = NULL)
  367. {
  368. // Act as setter
  369. if(!empty($buffer))
  370. {
  371. $this->challenge_buffer = $buffer;
  372. }
  373. return $this->challenge_buffer;
  374. }
  375. /**
  376. * Verify the challenge response and parse it
  377. */
  378. public function challengeVerifyAndParse()
  379. {
  380. // Check to make sure the response exists
  381. if(!isset($this->challenge_response[0]))
  382. {
  383. // Set error and skip
  384. $this->challenge_result = 'Challenge Response Empty';
  385. return FALSE;
  386. }
  387. // Challenge is good to go
  388. $this->challenge_result = TRUE;
  389. // Now let's create a new buffer with this response
  390. $this->challenge_buffer = new GameQ_Buffer($this->challenge_response[0]);
  391. // Now parse the challenge and apply it
  392. return $this->parseChallengeAndApply();
  393. }
  394. /**
  395. * Get/set the packet response
  396. *
  397. * @param string $packet_type
  398. * @param array $response
  399. */
  400. public function packetResponse($packet_type, $response = Array())
  401. {
  402. // Act as setter
  403. if(!empty($response))
  404. {
  405. $this->packets_response[$packet_type] = $response;
  406. }
  407. return $this->packets_response[$packet_type];
  408. }
  409. /**
  410. * Return specific packet(s)
  411. *
  412. * @param mixed $type array|string
  413. */
  414. public function getPacket($type = array())
  415. {
  416. // We want an array of packets back
  417. if(is_array($type) && !empty($type))
  418. {
  419. $packets = array();
  420. // Loop the packets
  421. foreach($this->packets AS $packet_type => $packet_data)
  422. {
  423. // We want this packet
  424. if(in_array($packet_type, $type))
  425. {
  426. $packets[$packet_type] = $packet_data;
  427. }
  428. }
  429. return $packets;
  430. }
  431. elseif($type == '!challenge')
  432. {
  433. $packets = array();
  434. // Loop the packets
  435. foreach($this->packets AS $packet_type => $packet_data)
  436. {
  437. // Dont want challenge packets
  438. if($packet_type == self::PACKET_CHALLENGE)
  439. {
  440. continue;
  441. }
  442. $packets[$packet_type] = $packet_data;
  443. }
  444. return $packets;
  445. }
  446. elseif(is_string($type))
  447. {
  448. return $this->packets[$type];
  449. }
  450. // Return all the packets
  451. return $this->packets;
  452. }
  453. /* Begin working methods */
  454. /**
  455. * Process the response and return the raw data as an array.
  456. *
  457. * @throws GameQException
  458. */
  459. public function processResponse()
  460. {
  461. // Init the array
  462. $results = array();
  463. // Let's loop all the requred methods to get all the data we want/need.
  464. foreach ($this->process_methods AS $method)
  465. {
  466. // Lets make sure the data method defined exists.
  467. if(!method_exists($this, $method))
  468. {
  469. // We should never get here in a production environment
  470. throw new GameQException('Unable to load method '.__CLASS__.'::'.$method);
  471. return FALSE;
  472. }
  473. // Setup a catch for protocol level errors
  474. try
  475. {
  476. // Call the proper process method. All methods should return an array of data.
  477. // Preprocessing should be handled by these methods internally as well.
  478. // Merge in the results when done.
  479. $results = array_merge($results, call_user_func_array(array($this, $method), array()));
  480. }
  481. catch (GameQ_ProtocolsException $e)
  482. {
  483. // Check to see if we are in debug, if so bubble up the exception
  484. if($this->debug)
  485. {
  486. throw new GameQException($e->getMessage(), $e->getCode(), $e);
  487. return FALSE;
  488. }
  489. // We ignore this and continue
  490. continue;
  491. }
  492. }
  493. // Now add some default stuff
  494. $results['gq_online'] = (count($results) > 0);
  495. $results['gq_address'] = $this->ip;
  496. $results['gq_port'] = $this->port;
  497. $results['gq_protocol'] = $this->protocol;
  498. $results['gq_type'] = (string) $this;
  499. $results['gq_transport'] = $this->transport;
  500. // Return the raw results
  501. return $results;
  502. }
  503. /**
  504. * This method is called before the actual query packets are sent to the server. This allows
  505. * the class to modify any changes before being sent.
  506. *
  507. * @return boolean
  508. */
  509. public function beforeSend()
  510. {
  511. return TRUE;
  512. }
  513. /**
  514. * Get the normalize property
  515. */
  516. public function getNormalize()
  517. {
  518. return $this->normalize;
  519. }
  520. /**
  521. * Apply the challenge string to all the packets that need it.
  522. *
  523. * @param string $challenge_string
  524. */
  525. protected function challengeApply($challenge_string)
  526. {
  527. // Let's loop thru all the packets and append the challenge where it is needed
  528. foreach($this->packets AS $packet_type => $packet)
  529. {
  530. $this->packets[$packet_type] = sprintf($packet, $challenge_string);
  531. }
  532. return TRUE;
  533. }
  534. /**
  535. * Parse the challenge buffer and get the proper challenge string out
  536. */
  537. protected function parseChallengeAndApply()
  538. {
  539. return TRUE;
  540. }
  541. /**
  542. * Determine whether or not the response is valid for a specific packet type
  543. *
  544. * @param string $packet_type
  545. */
  546. protected function hasValidResponse($packet_type)
  547. {
  548. // Check for valid packet. All packet responses should have atleast 1 array key (0).
  549. if(isset($this->packets_response[$packet_type][0])
  550. && !empty($this->packets_response[$packet_type][0])
  551. )
  552. {
  553. return TRUE;
  554. }
  555. return FALSE;
  556. }
  557. }