PageRenderTime 36ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/js/network/ServerNetChannel.js

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
JavaScript | 293 lines | 169 code | 42 blank | 82 comment | 12 complexity | 67d7ba790f9195fc501e4be779901b71 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /**
  2. File:
  3. ServerNetChannel.js
  4. Created By:
  5. Mario Gonzalez
  6. Project:
  7. RealtimeMultiplayerNodeJS
  8. Abstract:
  9. This class is responsible for managing the socket connection for each client
  10. -> ClientNetChannel talks to this object
  11. <--> ServerNetChannel talks to it's GameController via delegation
  12. <-- ServerNetChannel broadcast the message to all clients
  13. Basic Usage:
  14. TODO: UPDATE USAGE
  15. */
  16. (function () {
  17. /**
  18. *
  19. * The auto-incrimented ID to use for the next client
  20. */
  21. var nextClientID = RealtimeMultiplayerGame.Constants.SERVER_SETTING.CLIENT_ID;
  22. RealtimeMultiplayerGame.namespace("RealtimeMultiplayerGame.network");
  23. /**
  24. * Creates a new ServerNetChannel instance
  25. * @param {RealtimeMultiplayerGame.network.ServerNetChannelDelegateProtocol} aDelegate A delegate that conforms to RealtimeMultiplayerGame.network.ServerNetChannelDelegateProtocol
  26. */
  27. RealtimeMultiplayerGame.network.ServerNetChannel = function (aDelegate) {
  28. this.clients = new SortedLookupTable();
  29. this.setDelegate(aDelegate);
  30. this.setupSocketIO();
  31. // this.setupWSServer();
  32. this.setupCmdMap();
  33. return this;
  34. };
  35. RealtimeMultiplayerGame.network.ServerNetChannel.prototype = {
  36. socketio: null, // Socket.IO server
  37. clients: null, // SortedLookupTable
  38. delegate: null, // Should conform to ServerNetChannel delegate
  39. outgoingSequenceNumber: 0, // A unique ID for each message
  40. cmdMap: {}, // Map the CMD constants to functions
  41. // Methods
  42. /**
  43. * Initializes socket.io
  44. */
  45. setupSocketIO: function () {
  46. var server = require('http').createServer(function (req, res) {
  47. });
  48. server.listen(RealtimeMultiplayerGame.Constants.SERVER_SETTING.SOCKET_PORT);
  49. this.socketio = require('socket.io').listen(server);
  50. var that = this;
  51. this.socketio.configure('production', function () {
  52. that.socketio.enable('browser client etag');
  53. that.socketio.set('log level', 1);
  54. that.socketio.set('transports', [
  55. 'websocket'
  56. , 'flashsocket'
  57. , 'htmlfile'
  58. , 'xhr-polling'
  59. , 'jsonp-polling'
  60. ]);
  61. });
  62. this.socketio.configure('development', function () {
  63. that.socketio.set('transports', ['websocket']);
  64. });
  65. this.socketio.on('connection', function (socket) {
  66. console.log(socket);
  67. that.onSocketConnection(socket)
  68. socket.on('message', function (data) {
  69. console.log(data)
  70. that.onSocketMessage(data, socket)
  71. });
  72. socket.on('disconnect', function () {
  73. console.log("disconnecting...");
  74. that.onSocketClosed(socket)
  75. });
  76. });
  77. },
  78. setupWSServer: function () {
  79. var profiler = require('v8-profiler');
  80. var util = require('util');
  81. var ws = require("../lib/bonsai-ws/ws.js");
  82. this.clientCount = 0;
  83. this.maxClients = 8;
  84. this.maxChars = 128;
  85. this.socketClients = [];
  86. var that = this;
  87. this.$ = new ws.Server(false);
  88. this.$.onConnect = function (conn) {
  89. var aClient = new RealtimeMultiplayerGame.network.Client(conn, that.getNextClientID());
  90. // Send the first message back to the client, which gives them a clientid
  91. var connectMessage = new RealtimeMultiplayerGame.model.NetChannelMessage(++this.outgoingSequenceNumber, aClient.getClientid(), true, RealtimeMultiplayerGame.Constants.CMDS.SERVER_CONNECT, { gameClock: that.delegate.getGameClock() });
  92. connectMessage.messageTime = that.delegate.getGameClock();
  93. aClient.getConnection().json.send(RealtimeMultiplayerGame.modules.bison.encode(connectMessage));
  94. // Add to our list of connected users
  95. that.clients.setObjectForKey(aClient, aClient.getSessionId());
  96. };
  97. this.$.onMessage = function (conn, msg) {
  98. console.log("MESSAGE RECEIVED", msg);
  99. };
  100. this.$.onClose = function (conn) {
  101. that.removeClient(conn.$clientID);
  102. console.log("Disconnected!");
  103. };
  104. this.removeClient = function (id) {
  105. if (this.socketClients[id]) {
  106. this.clientCount--;
  107. this.socketClients[id].remove();
  108. delete this.socketClients[id];
  109. }
  110. };
  111. this.$.listen(RealtimeMultiplayerGame.Constants.SERVER_SETTING.SOCKET_PORT);
  112. },
  113. /**
  114. * Map RealtimeMultiplayerGame.Constants.CMDS to functions
  115. */
  116. setupCmdMap: function () {
  117. this.cmdMap = {};
  118. this.cmdMap[RealtimeMultiplayerGame.Constants.CMDS.PLAYER_JOINED] = this.onPlayerJoined;
  119. },
  120. /**
  121. * Checks all the clients to see if its ready for a new message.
  122. * If they are, have the client perform delta-compression on the worldDescription and send it off.
  123. * @param gameClock The current (zero-based) game clock
  124. * @param worldDescription A description of all the entities currently in the world
  125. */
  126. tick: function (gameClock, worldDescription) {
  127. var worldEntityDescriptionString = worldDescription.getEntityDescriptionAsString();
  128. var entityDescriptionObject = {
  129. entities: worldEntityDescriptionString,
  130. gameClock: worldDescription.gameClock,
  131. gameTick: worldDescription.gameTick
  132. };
  133. // Send client the current world info
  134. this.clients.forEach(function (key, client) {
  135. // Collapse delta - store the world state
  136. client.entityDescriptionBuffer.push(entityDescriptionObject);
  137. // Ask if enough time passed, and send a new world update
  138. if (client.canSendMessage(gameClock)) {
  139. client.sendQueuedCommands(gameClock);
  140. }
  141. }, this);
  142. },
  143. // Socket.IO callbacks
  144. /**
  145. * Callback from socket.io when a client has connected
  146. * @param clientConnection
  147. */
  148. onSocketConnection: function (clientConnection) {
  149. var aClient = new RealtimeMultiplayerGame.network.Client(clientConnection, this.getNextClientID());
  150. // Send the first message back to the client, which gives them a clientid
  151. var connectMessage = new RealtimeMultiplayerGame.model.NetChannelMessage(++this.outgoingSequenceNumber, aClient.getClientid(), true, RealtimeMultiplayerGame.Constants.CMDS.SERVER_CONNECT, { gameClock: this.delegate.getGameClock() });
  152. connectMessage.messageTime = this.delegate.getGameClock();
  153. aClient.getConnection().json.send(connectMessage);
  154. // Add to our list of connected users
  155. this.clients.setObjectForKey(aClient, aClient.getSessionId());
  156. },
  157. /**
  158. * Callback from socket.io when a client has disconnected
  159. * @param client
  160. */
  161. onSocketClosed: function (clientConnection) {
  162. var client = this.clients.objectForKey(clientConnection.sessionId);
  163. if (!client) {
  164. console.warn("(ServerNetChannel)::onSocketClosed - ERROR - Attempting to remove client that was not found in our list! ");
  165. return;
  166. }
  167. this.delegate.shouldRemovePlayer(client.getClientid());
  168. this.clients.remove(clientConnection.sessionId);
  169. client.dealloc();
  170. },
  171. /**
  172. * Callback from socket.io when a ClientNetChannel has sent us a message
  173. * @param data
  174. * @param connection
  175. */
  176. onSocketMessage: function (data, connection) {
  177. var client = this.clients.objectForKey(connection.sessionId);
  178. //that.CMD_TO_FUNCTION[decodedMessage.cmds.cmd].apply(that, [connection, decodedMessage]);
  179. // Allow the client to track that data was received
  180. if (client) {
  181. client.onMessage(data);
  182. } else {
  183. console.log("(NetChannel)::onSocketMessage - no such client!");
  184. return;
  185. }
  186. //// Call the mapped function, always pass the connection. Also pass data if available
  187. if (this.cmdMap[data.cmd]) {
  188. this.cmdMap[data.cmd].call(this, client, data);
  189. } else if (this.delegate.cmdMap[data.cmd]) { // See if delegate has function mapped
  190. this.delegate.cmdMap[data.cmd].call(this.delegate, client, data);
  191. } else { // Display error
  192. console.log("(NetChannel)::onSocketMessage could not map '" + data.cmd + "' to function!");
  193. }
  194. },
  195. ////// Game callbacks
  196. /**
  197. * Callback for when a player has joined the match.
  198. * Note that joining the match, happens after connecting.
  199. * For example a player might be able to connect to the match, and watch the game for a while then want to join the match
  200. * @param client
  201. * @param data
  202. */
  203. onPlayerJoined: function (client, data) {
  204. console.log(client.getClientid() + " joined the game!");
  205. this.delegate.shouldAddPlayer(client.getClientid(), data);
  206. client.getConnection().json.send(data);
  207. },
  208. /*************
  209. * ACCESSORS *
  210. *************/
  211. getNextClientID: function () {
  212. return ++nextClientID
  213. },
  214. /**
  215. * Checks that an object contains the required methods and sets it as the delegate for this ServerNetChannel instance
  216. * @param {RealtimeMultiplayerGame.network.ServerNetChannelDelegateProtocol} aDelegate A delegate that conforms to RealtimeMultiplayerGame.network.ServerNetChannelDelegateProtocol
  217. */
  218. setDelegate: function (aDelegate) {
  219. var theInterface = RealtimeMultiplayerGame.network.ServerNetChannelDelegateProtocol;
  220. for (var member in theInterface) {
  221. if ((typeof aDelegate[member] != typeof theInterface[member])) {
  222. console.log("object failed to implement interface member " + member);
  223. return false;
  224. }
  225. }
  226. // Checks passed
  227. this.delegate = aDelegate;
  228. }
  229. };
  230. /**
  231. * Required methods for the ServerNetChannel delegate
  232. */
  233. RealtimeMultiplayerGame.network.ServerNetChannelDelegateProtocol = {
  234. setupCmdMap: function () {
  235. },
  236. shouldUpdatePlayer: function (clientID, data) {
  237. },
  238. shouldAddPlayer: function (clientID, data) {
  239. },
  240. shouldRemovePlayer: function (clientID) {
  241. },
  242. getNextEntityID: function () {
  243. },
  244. getGameClock: function () {
  245. },
  246. log: function () {
  247. }
  248. }
  249. })();