/mods/servers/gameq/Communicate.php

https://github.com/ichraffsnicht/ClanSphere-Enhanced · PHP · 218 lines · 96 code · 36 blank · 86 comment · 21 complexity · 4bf48831800ed9ad6d530fd83c5c7063 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 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. * $Id: Communicate.php,v 1.12 2009/10/20 20:34:22 evilpie Exp $
  19. */
  20. /**
  21. * Handles all communication with the gameservers
  22. *
  23. * @author Aidan Lister <aidan@php.net>
  24. * @author Tom Buskens <t.buskens@deviation.nl>
  25. * @version $Revision: 1.12 $
  26. */
  27. class GameQ_Communicate
  28. {
  29. private $sockets = array();
  30. /**
  31. * Perform a batch query
  32. *
  33. * @param array $packets Packet data
  34. * @param int $timeout Query timeout in ms
  35. * @param string $type Query type, "challenge" or other
  36. * @param int $sock Local socket number to open
  37. * @return array Packet data
  38. */
  39. public function query($packets, $timeout, $type = 'data', $sock)
  40. {
  41. // Create a socket for each packet
  42. foreach ($packets as $pid => &$packet) {
  43. // We only send packets for the current type
  44. if (!isset($packet[$type])) continue;
  45. // Open a socket on the server
  46. $socket = $this->open($packet['addr'], $packet['port'], $pid, $sock, $timeout, $packet['transport']);
  47. if ($socket === false) continue;
  48. // Write data to the socket
  49. $this->write($socket, $packet[$type]);
  50. // Increment the socket (if we're not using the default ones)
  51. $sock = ($sock == 0) ? 0 : ++$sock;
  52. }
  53. // Listen to the sockets
  54. $responses = $this->listen($timeout);
  55. // Add responses to packets
  56. foreach ($this->sockets as $pid => $socket) {
  57. $sid = (int) $socket;
  58. if (isset($responses[$sid])) {
  59. $packets[$pid]['response'] = $responses[$sid];
  60. }
  61. }
  62. // Close sockets
  63. if ($type != 'challenge') $this->close();
  64. return $packets;
  65. }
  66. /**
  67. * Open an UDP socket.
  68. *
  69. * @param string $address Server address
  70. * @param int $port Server port
  71. * @param string $pid Packet id
  72. * @param int $sock Local socket number to use
  73. * @param int $timeout Timeout to use when open connection,
  74. * only used for tcp
  75. * @param string $transport Transport type, udp/tcp
  76. * @return resource Socket object, or false if the connection failed
  77. */
  78. private function open($address, $port, $pid, $sock, $timeout, $transport = 'udp')
  79. {
  80. // Check if we already opened a socket for this packet
  81. // This should only be so if it is a challenge-response type packet
  82. if (isset($this->sockets[$pid])) return $this->sockets[$pid];
  83. // Resolve address
  84. $address = $this->getIp($address);
  85. if ($address === false) {
  86. return false;
  87. }
  88. $errno = null;
  89. $errstr = null;
  90. // Set socket context
  91. $context = stream_context_create();
  92. if ($sock != 0)
  93. {
  94. $opts['socket']['bindto'] = '0:' . $sock;
  95. stream_context_set_option ($context, $opts);
  96. }
  97. // Open udp socket to client
  98. $addr = sprintf("%s://%s:%d", $transport, $address, $port);
  99. // Timeout is only applied for tcp connections
  100. $socket = @stream_socket_client($addr, $errno, $errstr, ($timeout/1000), STREAM_CLIENT_CONNECT, $context);
  101. // Set non-blocking, add socket to list
  102. if ($socket !== false) {
  103. $this->sockets[$pid] = $socket;
  104. stream_set_blocking($socket, false);
  105. }
  106. return $socket;
  107. }
  108. /**
  109. * Write to a socket.
  110. *
  111. * @param resource $socket Socket to write to
  112. * @param string $packet String to write
  113. */
  114. private function write($socket, $packet)
  115. {
  116. fwrite($socket, $packet);
  117. }
  118. /**
  119. * Listen to an array of sockets.
  120. *
  121. * @param array $sockets An array of sockets
  122. * @param int $timeout Maximum waiting time (ms)
  123. * @return array Result data
  124. */
  125. private function listen($timeout)
  126. {
  127. // Initialize
  128. $loops = 0;
  129. $maxloops = 50;
  130. $result = array();
  131. $starttime = microtime(true);
  132. $r = $this->sockets;
  133. $w = null;
  134. $e = null;
  135. if (count($this->sockets) == 0) return $result;
  136. while (($t = $timeout * 1000 - (microtime(true) - $starttime) * 10000) > 0 ) {
  137. $s = stream_select($r, $w, $e, 1, $t);
  138. if ($s === false || $s <= 0) break;
  139. if (++$loops > $maxloops) break;
  140. foreach ($r as $socket) {
  141. $response = stream_socket_recvfrom($socket, 2048);
  142. if ($response === false) continue;
  143. $result[(int) $socket][] = $response;
  144. }
  145. $r = $this->sockets;
  146. }
  147. return $result;
  148. }
  149. /**
  150. * Close all sockets.
  151. */
  152. private function close()
  153. {
  154. foreach ($this->sockets as &$socket) {
  155. fclose($socket);
  156. }
  157. $this->sockets = array();
  158. }
  159. /**
  160. * Get the address to connect to
  161. *
  162. * @param string $addr An ip or hostname
  163. * @return string An IP address, or false if the address was invalid
  164. */
  165. public function getIp($address)
  166. {
  167. // If it isn't a valid IP assume it is a hostname
  168. $preg = '#^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' .
  169. '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$#';
  170. if (!preg_match($preg, $address)) {
  171. $result = gethostbyname($address);
  172. // Not a valid host nor IP
  173. if ($result === $address) {
  174. $result = false;
  175. }
  176. } else {
  177. $result = $address;
  178. }
  179. return $result;
  180. }
  181. }
  182. ?>