From 810a74a28b7ab1a777765e7e224cfd26f31290c9 Mon Sep 17 00:00:00 2001 From: Jeena Date: Sat, 1 Mar 2014 23:11:36 +0100 Subject: [PATCH] fixed #58 --- .gitignore | 1 - app/Game/Channel/Channel.js | 31 ++- app/Game/Channel/GameController.js | 2 +- app/Game/Channel/PipeToServer.js | 8 +- app/Game/Client/Networker.js | 18 +- app/Game/Config/Settings.js | 1 + app/Game/Core/GameController.js | 10 +- app/Lib/Utilities/NotificationCenter.js | 7 +- app/Server/Api.js | 89 ++++--- app/Server/Coordinator.js | 112 +++------ app/Server/PipeToChannel.js | 16 +- app/Server/User.js | 135 ++++++----- static/html/index.html | 296 ++++++++++++++++++------ 13 files changed, 445 insertions(+), 281 deletions(-) mode change 100755 => 100644 app/Server/Coordinator.js mode change 100755 => 100644 app/Server/User.js diff --git a/.gitignore b/.gitignore index 5deab03..16c263b 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules/ *.log .DS_Store -pixi.js/ lab/audio/ diff --git a/app/Game/Channel/Channel.js b/app/Game/Channel/Channel.js index 28bb2f2..7344a25 100755 --- a/app/Game/Channel/Channel.js +++ b/app/Game/Channel/Channel.js @@ -9,7 +9,7 @@ function (GameController, Nc, User, ProtocolHelper, Options, Settings) { - function Channel (pipeToServer, name, options) { + function Channel (pipeToServer, options) { var self = this; @@ -17,7 +17,7 @@ levelUids: Settings.DEFAULT_LEVELS }); - this.name = name; + this.name = options.channelName; this.users = {}; this.pipeToServer = pipeToServer; @@ -34,11 +34,13 @@ Nc.on('broadcastGameCommand', this.broadcastGameCommand, this); Nc.on('broadcastGameCommandExcept', this.broadcastGameCommandExcept, this); - console.checkpoint('channel ' + name + ' created'); - } + console.checkpoint('channel ' + this.name + ' created'); - Channel.validateName = function (name) { - return true; + setTimeout(function() { + if(Object.keys(self.users).length < 1) { + self.destroy(); + } + }, Settings.CHANNEL_DESTRUCTION_TIME * 1000); } @@ -81,11 +83,22 @@ Channel.prototype.onReleaseUser = function (userId) { var user = this.users[userId]; - this.broadcastControlCommandExcept("userLeft", user.id, user); - Nc.trigger('user/left', user); - delete this.users[user.id]; + Nc.trigger('user/left', userId); + delete this.users[userId]; + + this.broadcastControlCommand("userLeft", userId); + + // FIXME: if this was the last user terminate forked process + if(Object.keys(this.users).length < 1) { + this.destroy(); + } } + Channel.prototype.destroy = function() { + console.checkpoint("channel (" + this.name + ") destroyed"); + this.pipeToServer.destroy(); + }; + // Sending commands diff --git a/app/Game/Channel/GameController.js b/app/Game/Channel/GameController.js index e447fae..f7fdc6b 100755 --- a/app/Game/Channel/GameController.js +++ b/app/Game/Channel/GameController.js @@ -21,7 +21,7 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N Parent.call(this); Nc.on('user/joined', this.onUserJoined, this); - Nc.on('user/left', this.onUserLeft, this); // FIXME: refactor this.userLeft -> this.onUserLeft, even in core and client + Nc.on('user/left', this.onUserLeft, this); Nc.on('user/resetLevel', this.onResetLevel, this); Nc.on('user/clientReady', this.onClientReady, this); Nc.on('player/killed', this.onPlayerKilled, this); diff --git a/app/Game/Channel/PipeToServer.js b/app/Game/Channel/PipeToServer.js index 396ff55..113ceca 100755 --- a/app/Game/Channel/PipeToServer.js +++ b/app/Game/Channel/PipeToServer.js @@ -17,10 +17,9 @@ function (Nc, Channel) { process.on('message', function (message, handle) { if(message.data.hasOwnProperty('CREATE')) { - self.channel = new Channel(this, message.data['CREATE']); + self.channel = new Channel(self, message.data.options); } else if (message.data.hasOwnProperty('KILL')) { self.channel.destroy(); - process.exit(0); } else { self.onMessage(message); } @@ -41,6 +40,11 @@ function (Nc, Channel) { Nc.trigger(message.recipient + '/controlCommand', message); } + PipeToServer.prototype.destroy = function() { + this.send('coordinator', {destroy:this.channel.name}); + this.process.exit(0); + }; + return PipeToServer; }); \ No newline at end of file diff --git a/app/Game/Client/Networker.js b/app/Game/Client/Networker.js index bc665ca..258a618 100755 --- a/app/Game/Client/Networker.js +++ b/app/Game/Client/Networker.js @@ -20,7 +20,8 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { var self = this; this.socketLink.on('message', function (message) { - if(Settings.NETWORK_LOG_INCOMING) { + var m = JSON.parse(message) + if(Settings.NETWORK_LOG_INCOMING && !m.gameCommand) { console.log('INCOMING', message); } ProtocolHelper.applyCommand(message, self); @@ -35,8 +36,13 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { Networker.prototype.onConnect = function () { console.log('connected.') var channel = JSON.parse(localStorage["channel"]); + var player = JSON.parse(localStorage["player"]); if(channel.name) { - this.sendCommand('join', channel.name); + var options = { + channelName: channel.name, + nickname: player.nickname + } + this.sendCommand('join', options); } else { window.location.href = "/"; } @@ -67,6 +73,11 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { this.initPing(); } + Networker.prototype.onJoinError = function(options) { + alert(options.message); + window.location.href = "/"; + }; + Networker.prototype.onLevelLoaded = function() { for (var userId in this.users) { this.gameController.createPlayer(this.users[userId]); @@ -116,8 +127,7 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { } Networker.prototype.onUserLeft = function (userId) { - var user = this.users[userId]; - this.gameController.onUserLeft(user); + this.gameController.onUserLeft(userId); delete this.users[userId]; } diff --git a/app/Game/Config/Settings.js b/app/Game/Config/Settings.js index 9cf9fd3..29efe6e 100755 --- a/app/Game/Config/Settings.js +++ b/app/Game/Config/Settings.js @@ -62,6 +62,7 @@ define(function() { // NETWORKING WORLD_UPDATE_BROADCAST_INTERVAL: 70, + CHANNEL_DESTRUCTION_TIME: 30, NETWORK_LOG_INCOMING: false, NETWORK_LOG_OUTGOING: false } diff --git a/app/Game/Core/GameController.js b/app/Game/Core/GameController.js index 499e5a8..820c2e4 100755 --- a/app/Game/Core/GameController.js +++ b/app/Game/Core/GameController.js @@ -79,14 +79,18 @@ function (PhysicsEngine, TiledLevel, Player, Nc) { } */ - GameController.prototype.onUserLeft = function (user) { - var player = this.players[user.id]; + GameController.prototype.onUserLeft = function (userId) { + var player = this.players[userId]; + if(!player) { + console.warn("User (", userId ,") left who has not joined"); + return; + } var i = this.gameObjects.animated.indexOf(player); if(i>=0) this.gameObjects.animated.splice(i, 1); player.destroy(); - delete this.players[user.id]; + delete this.players[userId]; } GameController.prototype.createPlayer = function(user) { diff --git a/app/Lib/Utilities/NotificationCenter.js b/app/Lib/Utilities/NotificationCenter.js index 578cc6a..76d3d3f 100755 --- a/app/Lib/Utilities/NotificationCenter.js +++ b/app/Lib/Utilities/NotificationCenter.js @@ -65,11 +65,8 @@ function () { } } }, - server: { - pipeToServer: function(v) { return v + "-ns.server.pipeToServer")} - }, - lobby: { - + channel: { + pipeToServer: function(v) { return v + "-ns.channel.pipeToServer")} } }; diff --git a/app/Server/Api.js b/app/Server/Api.js index 4efd2ff..846deb5 100644 --- a/app/Server/Api.js +++ b/app/Server/Api.js @@ -5,48 +5,65 @@ define([ function (Nc, ProtocolHelper) { - function Api(coordinator) { - this.coordinator = coordinator; - this.isError = false; - this.output = null; - } + function Api(coordinator) { + this.coordinator = coordinator; + this.isError = false; + this.output = null; + } - Api.prototype.handleCall = function(queryParameters) { + Api.prototype.handleCall = function(queryParameters) { - var command; - try { - var message = JSON.parse(queryParameters); - command = message.command; - } catch(e) { - console.error(e) - } + var command, + output = null; + + try { + var message = JSON.parse(queryParameters); + command = message.command; + } catch(e) { + this.isError = true; + output = "JSON syntax error"; + console.error(e) + } - var output = null; - switch(command) { - case "getChannels": - output = this.coordinator.getChannels(); - break; - default: - this.isError = true; - output = "Command not found"; - break; - } + switch(command) { + case "getChannels": + output = this.coordinator.getChannels(); + break; + case "createChannel": + // FIXME: sanitize input + output = this.createChannel(message.options); + break; + default: + this.isError = true; + output = "Command not found"; + break; + } - this.output = output; - } + this.output = output; + } - Api.prototype.getOutput = function() { - var output = {}; - var key = this.isError ? "error" : "success"; - output[key] = this.output; - return JSON.stringify(output); - - }; + Api.prototype.createChannel = function(options) { + var result = this.coordinator.createChannel(options); + if(result !== false) { + return result; + } else { + this.isError = true; + return "Could not create channel, name might already exist."; + } + }; - Api.prototype.getContentType = function() { - return "application/json"; - }; + Api.prototype.getOutput = function() { + var output = {}; + var key = this.isError ? "error" : "success"; + output[key] = this.output; + return JSON.stringify(output); + + }; + + Api.prototype.getContentType = function() { + return "application/json"; + }; - return Api; + return Api; }); \ No newline at end of file diff --git a/app/Server/Coordinator.js b/app/Server/Coordinator.js old mode 100755 new mode 100644 index 454afae..317534d --- a/app/Server/Coordinator.js +++ b/app/Server/Coordinator.js @@ -2,106 +2,64 @@ define([ "Server/User", "Game/Channel/Channel", "Server/PipeToChannel", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Game/Config/Settings" ], -function (User, Channel, PipeToChannel, Nc) { +function (User, Channel, PipeToChannel, Nc, Settings) { - function Coordinator () { - this.channelPipes = {}; - this.lobbyUsers = {}; + function Coordinator() { + this.channelPipes = {}; + + Nc.on('coordinator/message', this.onMessage, this); console.checkpoint('create Coordinator'); } Coordinator.prototype.createUser = function (socketLink) { - var user = new User(socketLink, this); - console.checkpoint('creating user'); - this.assignUserToLobby(user); - } - - Coordinator.prototype.assignUserToLobby = function (user) { - if(user.channelPipe) { - //user.channel.releaseUser(user); -> generate message - } - this.lobbyUsers[user.id] = user; - console.checkpoint('assign user to lobby'); + new User(socketLink, this); } Coordinator.prototype.assignUserToChannel = function (user, channelName) { - - if(user.channelPipe) { - //user.channel.releaseUser(user); -> generate message - } - - if(!Channel.validateName(channelName)) { - //TODO send validation error - return false; - } - - var channelPipe = this.channelPipes[channelName]; - if(!channelPipe) { - this.createPipe(channelName); - } - - //channel.addUser(user); - //user.setChannel(channel); - Nc.trigger('user/joined', user); - - delete this.lobbyUsers[user.id]; + var channelPipe = this.channelPipes[channelName]; + user.setChannelPipe(channelPipe); } - Coordinator.prototype.removeUser = function (user) { - - Nc.trigger('user/left', user); - //NotificationCenter.off('channel/' + user.channel.channelName + '/user/' + user.id); - - delete this.lobbyUsers[user.id]; + Coordinator.prototype.onDestroyPipe = function(channelName) { + delete this.channelPipes[channelName]; } - Coordinator.prototype.createPipe = function(channelName) { - - var channelPipe = new PipeToChannel(channelName); - this.channelPipes[channelName] = channelPipe; - - - Nc.on('channel/' + channelName + '/message', function (data) { - channelPipe.send('channel', data); - }, this); - - // sending info to user - Nc.on('user/joined', function (user) { - /* - Nc.on('channel/' + channelName + '/user/' + user.id, function (recipient, data) { - channelPipe.send(recipient, data); - }, this); - */ - - channelPipe.send('channel', { addUser: user.id }); - - }, this); - - Nc.on('user/left', function (user) { - channelPipe.send('channel', { releaseUser: user.id }); - }, this); - - Nc.on('user/controlCommand', function (userId, data) { - channelPipe.sendToUser(userId, data); - }, this); - - return channelPipe; - }; - Coordinator.prototype.getChannels = function(options) { - var list = []; + var list = []; for (var channelName in this.channelPipes) { list.push({ name: channelName }); } return list; + } + + Coordinator.prototype.createChannel = function(options) { + if(this.channelPipes[options.channelName]) { + return false; + } + + var channelPipe = new PipeToChannel(options); + this.channelPipes[options.channelName] = channelPipe; + return { + channelName: options.channelName, + link: "#" + options.channelName, + timeout: Settings.CHANNEL_DESTRUCTION_TIME + } }; - return Coordinator; + Coordinator.prototype.onMessage = function(message) { + if(message.destroy) { + delete this.channelPipes[message.destroy]; + } + }; + + return Coordinator; + }); \ No newline at end of file diff --git a/app/Server/PipeToChannel.js b/app/Server/PipeToChannel.js index 8c6fe29..e23285e 100755 --- a/app/Server/PipeToChannel.js +++ b/app/Server/PipeToChannel.js @@ -7,21 +7,21 @@ function (Nc, childProcess) { var fork = childProcess.fork; - function PipeToChannel (channelName) { + function PipeToChannel (options) { - this.channelPipe = null; + this.fork = null; try { - this.channelPipe = fork('channel.js'); + this.fork = fork('channel.js'); } catch (err) { throw 'Failed to fork channel! (' + err + ')'; } - console.checkpoint('creating channel process for ' + channelName); + console.checkpoint('creating channel process for ' + options.channelName); - this.send('channel/' + channelName, { CREATE: channelName }); + this.send('channel/' + options.channelName, { CREATE: true, options: options }); - this.channelPipe.on('message', this.onMessage.bind(this)); + this.fork.on('message', this.onMessage.bind(this)); var self = this; } @@ -33,7 +33,7 @@ function (Nc, childProcess) { data: data } - this.channelPipe.send(message); + this.fork.send(message); } // If user already created @@ -43,7 +43,7 @@ function (Nc, childProcess) { data: data } - this.channelPipe.send(message); + this.fork.send(message); } PipeToChannel.prototype.onMessage = function (message) { diff --git a/app/Server/User.js b/app/Server/User.js old mode 100755 new mode 100644 index 57fe570..95f7654 --- a/app/Server/User.js +++ b/app/Server/User.js @@ -1,60 +1,77 @@ -define([ - "Game/Core/User", - "Lib/Utilities/Protocol/Helper", - "Lib/Utilities/NotificationCenter" -], - -function (Parent, ProtocolHelper, Nc) { - - function User (socketLink, coordinator) { - Parent.call(this, socketLink.id); - - this.coordinator = coordinator; - this.channelProcess = null; - this.socketLink = socketLink; - - socketLink.on('message', this.onMessage.bind(this)); - socketLink.on('disconnect', this.onDisconnect.bind(this)); - - Nc.on("user/" + this.socketLink.id + "/message", this.socketLink.send, this.socketLink); - } - - User.prototype = Object.create(Parent.prototype); - - - // Socket callbacks - - User.prototype.onMessage = function (message) { - ProtocolHelper.applyCommand(message, this); - } - - User.prototype.onDisconnect = function () { - this.coordinator.removeUser(this); - } - - - // User command callbacks - // Remember: control commands are coordinator relevant commands - - User.prototype.onJoin = function(options) { - this.coordinator.assignUserToChannel(this, options); - }; - - User.prototype.onLeave = function(options) { - this.coordinator.assignUserToLobby(this); - }; - - User.prototype.onGameCommand = function(options) { - // repacking for transport via pipe - var message = ProtocolHelper.encodeCommand("gameCommand", options); - Nc.trigger("user/controlCommand", this.id, message); - }; - - User.prototype.onPing = function(timestamp) { - var message = ProtocolHelper.encodeCommand("pong", timestamp); - Nc.trigger("user/" + this.socketLink.id + "/message", message); - }; - - return User; - +define([ + "Game/Core/User", + "Lib/Utilities/Protocol/Helper", + "Lib/Utilities/NotificationCenter" +], + +function (Parent, ProtocolHelper, Nc) { + + function User (socketLink, coordinator) { + Parent.call(this, socketLink.id); + + this.coordinator = coordinator; + this.socketLink = socketLink; + this.channelPipe = null; + + socketLink.on('message', this.onMessage.bind(this)); + socketLink.on('disconnect', this.onDisconnect.bind(this)); + + Nc.on("user/" + this.socketLink.id + "/message", this.socketLink.send, this.socketLink); + } + + User.prototype = Object.create(Parent.prototype); + + User.prototype.setChannelPipe = function(channelPipe) { + if(channelPipe) { + this.channelPipe = channelPipe; + } else { + var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel not found"}); + this.socketLink.send(message); + } + }; + + + // Socket callbacks + + User.prototype.onMessage = function (message) { + ProtocolHelper.applyCommand(message, this); + } + + User.prototype.onDisconnect = function () { + if(!this.channelPipe) { + console.warn("Disconnecting user without a channel."); + return; + } + + this.channelPipe.send('channel', { releaseUser: this.id }); + } + + + // User command callbacks + // Remember: control commands are coordinator relevant commands + + User.prototype.onJoin = function(options) { + this.coordinator.assignUserToChannel(this, options.channelName); + + if(!this.channelPipe) { + console.warn("Can not join user because channel (" + options.channelName + ") does not exist.") + return; + } + + this.channelPipe.send('channel', { addUser: this.id }); + }; + + User.prototype.onGameCommand = function(options) { + // repacking for transport via pipe + var message = ProtocolHelper.encodeCommand("gameCommand", options); + this.channelPipe.sendToUser(this.id, message); + }; + + User.prototype.onPing = function(timestamp) { + var message = ProtocolHelper.encodeCommand("pong", timestamp); + Nc.trigger("user/" + this.socketLink.id + "/message", message); + }; + + return User; + }); \ No newline at end of file diff --git a/static/html/index.html b/static/html/index.html index eda77ba..88b6423 100644 --- a/static/html/index.html +++ b/static/html/index.html @@ -2,42 +2,92 @@ Chuck Lobby + -
+
+

Chuck

- - - - - - - - - - - -
Name
- -
-

- - -

- + +
+
+

Create your own!

+

+
+ Maps +
    +
  • + +
  • +
  • + +
  • +
+
+ +

+ + +

+
+
+ +
+

+ +

+

+ + +

+
+ +
+

Channel list

+ + + + + + + + +
NameType
+

+ + + +

+
+
+ \ No newline at end of file