Protocol.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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. */
  20. namespace GameQ;
  21. /**
  22. * Handles the core functionality for the protocols
  23. *
  24. * @SuppressWarnings(PHPMD.NumberOfChildren)
  25. *
  26. * @author Austin Bischoff <[email protected]>
  27. */
  28. abstract class Protocol
  29. {
  30. /**
  31. * Constants for class states
  32. */
  33. const STATE_TESTING = 1;
  34. const STATE_BETA = 2;
  35. const STATE_STABLE = 3;
  36. const STATE_DEPRECATED = 4;
  37. /**
  38. * Constants for packet keys
  39. */
  40. const PACKET_ALL = 'all'; // Some protocols allow all data to be sent back in one call.
  41. const PACKET_BASIC = 'basic';
  42. const PACKET_CHALLENGE = 'challenge';
  43. const PACKET_CHANNELS = 'channels'; // Voice servers
  44. const PACKET_DETAILS = 'details';
  45. const PACKET_INFO = 'info';
  46. const PACKET_PLAYERS = 'players';
  47. const PACKET_STATUS = 'status';
  48. const PACKET_RULES = 'rules';
  49. const PACKET_VERSION = 'version';
  50. /**
  51. * Transport constants
  52. */
  53. const TRANSPORT_UDP = 'udp';
  54. const TRANSPORT_TCP = 'tcp';
  55. const TRANSPORT_SSL = 'ssl';
  56. const TRANSPORT_TLS = 'tls';
  57. /**
  58. * Short name of the protocol
  59. *
  60. * @type string
  61. */
  62. protected $name = 'unknown';
  63. /**
  64. * The longer, fancier name for the protocol
  65. *
  66. * @type string
  67. */
  68. protected $name_long = 'unknown';
  69. /**
  70. * The difference between the client port and query port
  71. *
  72. * @type int
  73. */
  74. protected $port_diff = 0;
  75. /**
  76. * The transport method to use to actually send the data
  77. * Default is UDP
  78. *
  79. * @type string
  80. */
  81. protected $transport = self::TRANSPORT_UDP;
  82. /**
  83. * The protocol type used when querying the server
  84. *
  85. * @type string
  86. */
  87. protected $protocol = 'unknown';
  88. /**
  89. * Holds the valid packet types this protocol has available.
  90. *
  91. * @type array
  92. */
  93. protected $packets = [];
  94. /**
  95. * Holds the response headers and the method to use to process them.
  96. *
  97. * @type array
  98. */
  99. protected $responses = [];
  100. /**
  101. * Holds the list of methods to run when parsing the packet response(s) data. These
  102. * methods should provide all the return information.
  103. *
  104. * @type array
  105. */
  106. protected $process_methods = [];
  107. /**
  108. * The packet responses received
  109. *
  110. * @type array
  111. */
  112. protected $packets_response = [];
  113. /**
  114. * Holds the instance of the result class
  115. *
  116. * @type null
  117. */
  118. protected $result = null;
  119. /**
  120. * Options for this protocol
  121. *
  122. * @type array
  123. */
  124. protected $options = [];
  125. /**
  126. * Define the state of this class
  127. *
  128. * @type int
  129. */
  130. protected $state = self::STATE_STABLE;
  131. /**
  132. * Holds specific normalize settings
  133. *
  134. * @todo: Remove this ugly bulk by moving specific ones to their specific game(s)
  135. *
  136. * @type array
  137. */
  138. protected $normalize = [
  139. // General
  140. 'general' => [
  141. // target => source
  142. 'dedicated' => [
  143. 'listenserver',
  144. 'dedic',
  145. 'bf2dedicated',
  146. 'netserverdedicated',
  147. 'bf2142dedicated',
  148. 'dedicated',
  149. ],
  150. 'gametype' => ['ggametype', 'sigametype', 'matchtype'],
  151. 'hostname' => ['svhostname', 'servername', 'siname', 'name'],
  152. 'mapname' => ['map', 'simap'],
  153. 'maxplayers' => ['svmaxclients', 'simaxplayers', 'maxclients', 'max_players'],
  154. 'mod' => ['game', 'gamedir', 'gamevariant'],
  155. 'numplayers' => ['clients', 'sinumplayers', 'num_players'],
  156. 'password' => ['protected', 'siusepass', 'sineedpass', 'pswrd', 'gneedpass', 'auth', 'passsord'],
  157. ],
  158. // Indvidual
  159. 'player' => [
  160. 'name' => ['nick', 'player', 'playername', 'name'],
  161. 'kills' => ['kills'],
  162. 'deaths' => ['deaths'],
  163. 'score' => ['kills', 'frags', 'skill', 'score'],
  164. 'ping' => ['ping'],
  165. ],
  166. // Team
  167. 'team' => [
  168. 'name' => ['name', 'teamname', 'team_t'],
  169. 'score' => ['score', 'score_t'],
  170. ],
  171. ];
  172. /**
  173. * Quick join link
  174. *
  175. * @type string
  176. */
  177. protected $join_link = '';
  178. /**
  179. * @param array $options
  180. */
  181. public function __construct(array $options = [])
  182. {
  183. // Set the options for this specific instance of the class
  184. $this->options = $options;
  185. }
  186. /**
  187. * String name of this class
  188. *
  189. * @return string
  190. */
  191. public function __toString()
  192. {
  193. return $this->name;
  194. }
  195. /**
  196. * Get the port difference between the server's client (game) and query ports
  197. *
  198. * @return int
  199. */
  200. public function portDiff()
  201. {
  202. return $this->port_diff;
  203. }
  204. /**
  205. * "Find" the query port based off of the client port and port_diff
  206. *
  207. * This method is meant to be overloaded for more complex maths or lookup tables
  208. *
  209. * @param int $clientPort
  210. *
  211. * @return int
  212. */
  213. public function findQueryPort($clientPort)
  214. {
  215. return $clientPort + $this->port_diff;
  216. }
  217. /**
  218. * Return the join_link as defined by the protocol class
  219. *
  220. * @return string
  221. */
  222. public function joinLink()
  223. {
  224. return $this->join_link;
  225. }
  226. /**
  227. * Short (callable) name of this class
  228. *
  229. * @return string
  230. */
  231. public function name()
  232. {
  233. return $this->name;
  234. }
  235. /**
  236. * Long name of this class
  237. *
  238. * @return string
  239. */
  240. public function nameLong()
  241. {
  242. return $this->name_long;
  243. }
  244. /**
  245. * Return the status of this Protocol Class
  246. *
  247. * @return int
  248. */
  249. public function state()
  250. {
  251. return $this->state;
  252. }
  253. /**
  254. * Return the protocol property
  255. *
  256. * @return string
  257. */
  258. public function getProtocol()
  259. {
  260. return $this->protocol;
  261. }
  262. /**
  263. * Get/set the transport type for this protocol
  264. *
  265. * @param string|null $type
  266. *
  267. * @return string
  268. */
  269. public function transport($type = null)
  270. {
  271. // Act as setter
  272. if (!is_null($type)) {
  273. $this->transport = $type;
  274. }
  275. return $this->transport;
  276. }
  277. /**
  278. * Set the options for the protocol call
  279. *
  280. * @param array $options
  281. *
  282. * @return array
  283. */
  284. public function options($options = [])
  285. {
  286. // Act as setter
  287. if (!empty($options)) {
  288. $this->options = $options;
  289. }
  290. return $this->options;
  291. }
  292. /*
  293. * Packet Section
  294. */
  295. /**
  296. * Return specific packet(s)
  297. *
  298. * @param array $type
  299. *
  300. * @return array
  301. */
  302. public function getPacket($type = [])
  303. {
  304. $packets = [];
  305. // We want an array of packets back
  306. if (is_array($type) && !empty($type)) {
  307. // Loop the packets
  308. foreach ($this->packets as $packet_type => $packet_data) {
  309. // We want this packet
  310. if (in_array($packet_type, $type)) {
  311. $packets[$packet_type] = $packet_data;
  312. }
  313. }
  314. } elseif ($type == '!challenge') {
  315. // Loop the packets
  316. foreach ($this->packets as $packet_type => $packet_data) {
  317. // Dont want challenge packets
  318. if ($packet_type != self::PACKET_CHALLENGE) {
  319. $packets[$packet_type] = $packet_data;
  320. }
  321. }
  322. } elseif (is_string($type)) {
  323. // Return specific packet type
  324. $packets = $this->packets[$type];
  325. } else {
  326. // Return all packets
  327. $packets = $this->packets;
  328. }
  329. // Return the packets
  330. return $packets;
  331. }
  332. /**
  333. * Get/set the packet response
  334. *
  335. * @param array|null $response
  336. *
  337. * @return array
  338. */
  339. public function packetResponse(array $response = null)
  340. {
  341. // Act as setter
  342. if (!empty($response)) {
  343. $this->packets_response = $response;
  344. }
  345. return $this->packets_response;
  346. }
  347. /*
  348. * Challenge section
  349. */
  350. /**
  351. * Determine whether or not this protocol has a challenge needed before querying
  352. *
  353. * @return bool
  354. */
  355. public function hasChallenge()
  356. {
  357. return (isset($this->packets[self::PACKET_CHALLENGE]) && !empty($this->packets[self::PACKET_CHALLENGE]));
  358. }
  359. /**
  360. * Parse the challenge response and add it to the buffer items that need it.
  361. * This should be overloaded by extending class
  362. *
  363. * @codeCoverageIgnore
  364. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  365. *
  366. * @param \GameQ\Buffer $challenge_buffer
  367. *
  368. * @return bool
  369. */
  370. public function challengeParseAndApply(Buffer $challenge_buffer)
  371. {
  372. return true;
  373. }
  374. /**
  375. * Apply the challenge string to all the packets that need it.
  376. *
  377. * @param string $challenge_string
  378. *
  379. * @return bool
  380. */
  381. protected function challengeApply($challenge_string)
  382. {
  383. // Let's loop through all the packets and append the challenge where it is needed
  384. foreach ($this->packets as $packet_type => $packet) {
  385. $this->packets[$packet_type] = sprintf($packet, $challenge_string);
  386. }
  387. return true;
  388. }
  389. /**
  390. * Get the normalize settings for the protocol
  391. *
  392. * @return array
  393. */
  394. public function getNormalize()
  395. {
  396. return $this->normalize;
  397. }
  398. /*
  399. * General
  400. */
  401. /**
  402. * Generic method to allow protocol classes to do work right before the query is sent
  403. *
  404. * @codeCoverageIgnore
  405. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  406. *
  407. * @param \GameQ\Server $server
  408. */
  409. public function beforeSend(Server $server)
  410. {
  411. }
  412. /**
  413. * Method called to process query response data. Each extending class has to have one of these functions.
  414. *
  415. * @return mixed
  416. */
  417. abstract public function processResponse();
  418. }