PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/SocketServer.php

https://github.com/mockenoff/Mega-Hovertank-Wars
PHP | 246 lines | 223 code | 16 blank | 7 comment | 36 complexity | 9c4cf9f4dc8b1d81e23f71bef7ee15b6 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. include_once('User.php');
  3. include_once('Game.php');
  4. include_once('dbConnect.php');
  5. class socketServer {
  6. private $ip;
  7. private $port;
  8. private $users;
  9. private $games;
  10. private $master;
  11. private $sockets;
  12. private $debug;
  13. private $dbc;
  14. function __construct($ip = '127.0.0.1', $port = 12345) {
  15. $this->ip = $ip;
  16. $this->port = $port;
  17. $this->games = array();
  18. $this->users = array();
  19. $this->debug = true;
  20. $this->master = $this->WebSocket($ip, $port);
  21. $this->sockets = array($this->master);
  22. $this->dbc = new dbConnect();
  23. mysql_select_db('cs4311', $this->dbc->conn);
  24. $this->listen();
  25. }
  26. private function WebSocket($address, $port) {
  27. $master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
  28. socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
  29. socket_bind($master, $address, $port) or die("socket_bind() failed");
  30. socket_listen($master, 20) or die("socket_listen() failed");
  31. echo "Server Started : " . date('Y-m-d H:i:s') . "\n";
  32. echo "Master socket : " . $master."\n";
  33. echo "Listening on : " . $address . " port " . $port . "\n\n";
  34. return $master;
  35. }
  36. private function listen() {
  37. while(true){
  38. $changed = $this->sockets;
  39. socket_select($changed, $write = NULL, $except = NULL, NULL);
  40. foreach($changed as $socket) {
  41. $this->checkTime();
  42. if($socket == $this->master){
  43. $client = socket_accept($this->master);
  44. if($client < 0) { $this->console('socket_accept() failed'); continue; }
  45. else{ $this->connect($client); }
  46. }
  47. else {
  48. $bytes = @socket_recv($socket, $buffer, 2048, 0);
  49. if($bytes == 0) { $this->disconnect($socket); }
  50. else {
  51. $user = $this->getuserbysocket($socket);
  52. if(!$user->handshake) { $this->dohandshake($user, $buffer); }
  53. else{ $this->process($user, $buffer); }
  54. }
  55. }
  56. }
  57. }
  58. }
  59. private function connect($socket) {
  60. $user = new User(uniqid(), $socket);
  61. array_push($this->users, $user);
  62. array_push($this->sockets, $socket);
  63. $this->console($socket . " CONNECTED!");
  64. }
  65. private function process($user, $msg) {
  66. /* if(substr($msg, 0, 22) == "<policy-file-request/>") {
  67. echo "POLICY FILE REQUEST\n";
  68. $crossFile = file("crossdomain.xml");
  69. $crossFile = join('',$crossFile);
  70. $this->send($user->socket, $crossFile);
  71. return;
  72. }*/
  73. $msg = $this->unwrap($msg);
  74. $this->say("< " . $msg);
  75. $data = explode('|', $msg);
  76. switch($data[0]) {
  77. case 0:
  78. $this->send($user->socket, '10|'.$data[1].'|'.$data[2].'|'.$data[3].'|'.$data[4].'|Received initialized user data');
  79. $user->gid = $data[1];
  80. $user->uip = $data[2];
  81. $user->uid = $data[3];
  82. $user->gud = $data[4];
  83. if(array_key_exists($data[1], $this->games) === FALSE) {
  84. $game = new Game($data[1]);
  85. $this->games[$data[1]] = $game;
  86. $this->console('New game object ' . $data[1]);
  87. }
  88. $this->games[$data[1]]->addPlayer($data[3], $user->socket);
  89. $this->incrementStat(0, $data[3]);
  90. if($this->games[$data[1]]->isFull()) {
  91. $players = $this->games[$data[1]]->getPlayers();
  92. $msg = '0';
  93. foreach($players as $player) {
  94. $usr = $this->getuserbyuid($player['uid']);
  95. $msg .= '|' . $usr->gud . '|' . $usr->uid;
  96. }
  97. $this->broadcast($data[1], 0, $user->socket, $msg);
  98. }
  99. break;
  100. case 1:
  101. $this->send($user->socket, 'Received input data!');
  102. $this->broadcast($data[1], $data[3], $user->socket, $msg);
  103. break;
  104. case 2:
  105. if($this->getuserbyuid($data[5]) != null) {
  106. $this->incrementStat(1, $data[5]);
  107. $this->incrementStat(2, $data[6]);
  108. $this->broadcast($data[1], $data[3], $user->socket, $msg);
  109. }
  110. break;
  111. case 5:
  112. $this->broadcast($data[1], $data[3], $user->socket, $msg);
  113. break;
  114. default:
  115. $this->console('Unknown command: ' . $msg);
  116. break;
  117. }
  118. }
  119. private function broadcast($gid, $uid, $socket, $msg = '') {
  120. $players = $this->games[$gid]->getPlayers();
  121. foreach($players as $player) {
  122. if($player['uid'] != $uid) $this->send($player['socket'], $msg);
  123. }
  124. }
  125. private function send($client, $msg) {
  126. $this->say("> " . $msg);
  127. $msg = $this->wrap($msg);
  128. socket_write($client, $msg, strlen($msg));
  129. }
  130. private function disconnect($socket) {
  131. $found = null;
  132. $n = count($this->users);
  133. for($i = 0; $i < $n; $i++) {
  134. if($this->users[$i]->socket == $socket) { $found = $i; break; }
  135. }
  136. if(!is_null($found)) {
  137. $this->broadcast($this->users[$found]->gid, $this->users[$found]->uid, 0, '2|'.$this->users[$found]->gid.'|'.$this->users[$found]->uip.'|'.$this->users[$found]->uid.'|'.$this->users[$found]->gud.'|'.$this->users[$found]->uid.'|0');
  138. $this->games[$this->users[$found]->gid]->dropPlayer($this->users[$found]->uid, $socket);
  139. if(count($this->games[$this->users[$found]->gid]->players) < 1) {
  140. $query = sprintf("DELETE FROM games WHERE gid = '%s'", $this->users[$found]->gid);
  141. if(!mysql_query($query, $this->dbc->conn))
  142. $this->console('Failed to delete game ' . $this->users[$found]->gid . ' from the database: ' . mysql_error());
  143. unset($this->games[$this->users[$found]->gid]);
  144. }
  145. array_splice($this->users, $found, 1);
  146. }
  147. $index = array_search($socket, $this->sockets);
  148. socket_close($socket);
  149. $this->console($socket . " DISCONNECTED!");
  150. if($index >= 0) { array_splice($this->sockets, $index, 1); }
  151. }
  152. private function dohandshake($user, $buffer) {
  153. $this->console("\nRequesting handshake...");
  154. $this->console($buffer);
  155. list($resource, $host, $origin) = $this->getheaders($buffer);
  156. $this->console("Handshaking...");
  157. $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
  158. "Upgrade: WebSocket\r\n" .
  159. "Connection: Upgrade\r\n" .
  160. "WebSocket-Origin: " . $origin . "\r\n" .
  161. "WebSocket-Location: ws://" . $host . $resource . "\r\n" .
  162. "\r\n";
  163. socket_write($user->socket, $upgrade . chr(0), strlen($upgrade . chr(0)));
  164. $user->handshake = true;
  165. $this->console($upgrade);
  166. $this->console("Done handshaking...");
  167. return true;
  168. }
  169. private function getheaders($req) {
  170. $r = $h = $o = null;
  171. if(preg_match("/GET (.*) HTTP/" ,$req,$match)) { $r = $match[1]; }
  172. if(preg_match("/Host: (.*)\r\n/" ,$req,$match)) { $h = $match[1]; }
  173. if(preg_match("/Origin: (.*)\r\n/",$req,$match)) { $o = $match[1]; }
  174. return array($r, $h, $o);
  175. }
  176. private function getuserbysocket($socket) {
  177. $found = null;
  178. foreach($this->users as $user) {
  179. if($user->socket == $socket) { $found = $user; break; }
  180. }
  181. return $found;
  182. }
  183. private function getuserbyuid($uid = -1) {
  184. $found = null;
  185. foreach($this->users as $user) {
  186. if($user->uid == $uid) { $found = $user; break; }
  187. }
  188. return $found;
  189. }
  190. private function checkTime() {
  191. foreach($this->games as $game) {
  192. $players = $game->getPlayers();
  193. $this->console($game->timeLeft());
  194. if(count($players) == 1 && $game->timeLeft() < 595) {
  195. foreach($players as $player) {
  196. $this->say('Winner of game ' . $game->gid);
  197. $this->broadcast($game->gid, 0, 0, '4|' . $game->gid . '|0|0|0');
  198. }
  199. }
  200. if($game->timeLeft() <= 0) {
  201. $this->say('Out of time for game ' . $game->gid);
  202. $this->broadcast($game->gid, 0, 0, '3|' . $game->gid . '|0|0|0');
  203. }
  204. }
  205. }
  206. private function incrementStat($action = -1, $uid = -1) {
  207. $query = '';
  208. switch($action) {
  209. case 0:
  210. $query = sprintf("UPDATE users SET games=games+1 WHERE uid = '%s'", mysql_real_escape_string($uid));
  211. break;
  212. case 1:
  213. $query = sprintf("UPDATE users SET deaths=deaths+1 WHERE uid = '%s'", mysql_real_escape_string($uid));
  214. break;
  215. case 2:
  216. $query = sprintf("UPDATE users SET kills=kills+1 WHERE uid = '%s'", mysql_real_escape_string($uid));
  217. break;
  218. }
  219. if($query != '') {
  220. $this->console($query);
  221. if(!mysql_query($query))
  222. $this->console('Failed to run query for incrementStat(' . $action . ',' . $uid . '): ' . mysql_error());
  223. }
  224. }
  225. private function console($msg = "") { if($this->debug) { echo $msg . "\n"; } }
  226. private function say($msg = "") { echo $msg . "\n"; }
  227. private function wrap($msg = "") { return chr(0) . $msg . chr(255); }
  228. private function unwrap($msg = "") { return substr($msg, 1, strlen($msg) - 2); }
  229. }
  230. ?>