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