diff --git a/.gitignore b/.gitignore index 2e40ea7..555f0bc 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ node_modules/ *.log .DS_Store -pixi.js/ lab/audio/ -lab/filter/ \ No newline at end of file +lab/filter/ diff --git a/app/Bootstrap/HttpServer.js b/app/Bootstrap/HttpServer.js index e333b45..b2a7183 100755 --- a/app/Bootstrap/HttpServer.js +++ b/app/Bootstrap/HttpServer.js @@ -1,7 +1,7 @@ define([ 'http', 'node-static', - 'Lobby/Api' + 'Server/Api' ], function (http, nodeStatic, Api) { diff --git a/app/Game/Server/Channel.js b/app/Game/Channel/Channel.js similarity index 62% rename from app/Game/Server/Channel.js rename to app/Game/Channel/Channel.js index a422c26..d9628a9 100755 --- a/app/Game/Server/Channel.js +++ b/app/Game/Channel/Channel.js @@ -1,7 +1,7 @@ define([ - "Game/Server/GameController", + "Game/Channel/GameController", "Lib/Utilities/NotificationCenter", - "Game/Server/User", + "Game/Channel/User", "Lib/Utilities/Protocol/Helper", "Lib/Utilities/Options", "Game/Config/Settings" @@ -9,7 +9,7 @@ function (GameController, Nc, User, ProtocolHelper, Options, Settings) { - function Channel (pipeToLobby, name, options) { + function Channel (pipeToServer, options) { var self = this; @@ -17,10 +17,10 @@ levelUids: Settings.DEFAULT_LEVELS }); - this.name = name; + this.name = options.channelName; this.users = {}; - this.pipeToLobby = pipeToLobby; + this.pipeToServer = pipeToServer; this.gameController = new GameController(this); @@ -34,32 +34,38 @@ 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); } // Channel command callbacks - Channel.prototype.onAddUser = function (userId) { + Channel.prototype.onAddUser = function (options) { var self = this; if(!this.gameController.level || !this.gameController.level.isLoaded) { var token = Nc.on("game/level/loaded", function() { - self.sendJoinSuccess(userId); + self.sendJoinSuccess(options); Nc.off(token); }); } else { - self.sendJoinSuccess(userId); + self.sendJoinSuccess(options); } } - Channel.prototype.sendJoinSuccess = function(userId) { - var user = new User(userId, this); - var joinedUsers = Object.keys(this.users); + Channel.prototype.sendJoinSuccess = function(options) { + var user = new User(options.id, options); + + var joinedUsers = []; + for(var userId in this.users) { + joinedUsers.push(this.users[userId].options) + } var levelUid = null; if(this.gameController.level) { @@ -69,23 +75,36 @@ this.users[user.id] = user; var options = { - userId: user.id, - channelName: this.name, + user: user.options, joinedUsers: joinedUsers, levelUid: levelUid }; - Nc.trigger('user/' + user.id + "/joinSuccess", options); - Nc.trigger('user/joined', user); + //Nc.trigger('user/' + user.id + "/joinSuccess", options); + user.sendControlCommand("joinSuccess", options); + Nc.trigger('user/joined', user); + + this.broadcastControlCommandExcept("userJoined", user.options, user); }; 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/Server/Collision/Detector.js b/app/Game/Channel/Collision/Detector.js similarity index 100% rename from app/Game/Server/Collision/Detector.js rename to app/Game/Channel/Collision/Detector.js diff --git a/app/Game/Server/Control/PlayerController.js b/app/Game/Channel/Control/PlayerController.js similarity index 100% rename from app/Game/Server/Control/PlayerController.js rename to app/Game/Channel/Control/PlayerController.js diff --git a/app/Game/Server/GameController.js b/app/Game/Channel/GameController.js similarity index 91% rename from app/Game/Server/GameController.js rename to app/Game/Channel/GameController.js index c40ef36..9131742 100755 --- a/app/Game/Server/GameController.js +++ b/app/Game/Channel/GameController.js @@ -1,15 +1,15 @@ define([ "Game/Core/GameController", - "Game/Server/Physics/Engine", + "Game/Channel/Physics/Engine", "Game/Config/Settings", - "Game/Server/Control/PlayerController", + "Game/Channel/Control/PlayerController", "Lib/Utilities/RequestAnimFrame", "Lib/Utilities/NotificationCenter", "Lib/Vendor/Box2D", - "Game/Server/Player", - "Game/Server/GameObjects/GameObject", - "Game/Server/GameObjects/Doll", - "Game/Server/GameObjects/Items/RagDoll" + "Game/Channel/Player", + "Game/Channel/GameObjects/GameObject", + "Game/Channel/GameObjects/Doll", + "Game/Channel/GameObjects/Items/RagDoll" ], function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, Nc, Box2D, Player, GameObject, Doll, RagDoll) { @@ -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); @@ -88,15 +88,6 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N }, respawnTime * 1000); }; - /* - GameController.prototype.createPlayer = function(user) { - var player = new Player(user.id, this.physicsEngine); - player.setPlayerController(new PlayerController(player)) - - return player; - }; - */ - GameController.prototype.updateWorld = function () { var update = this.getWorldUpdateObject(false); diff --git a/app/Game/Server/GameObjects/Doll.js b/app/Game/Channel/GameObjects/Doll.js similarity index 98% rename from app/Game/Server/GameObjects/Doll.js rename to app/Game/Channel/GameObjects/Doll.js index 4d2738d..8349094 100755 --- a/app/Game/Server/GameObjects/Doll.js +++ b/app/Game/Channel/GameObjects/Doll.js @@ -1,6 +1,6 @@ define([ "Game/Core/GameObjects/Doll", - "Game/Server/GameObjects/Item", + "Game/Channel/GameObjects/Item", "Lib/Vendor/Box2D", "Lib/Utilities/NotificationCenter" ], diff --git a/app/Game/Server/GameObjects/GameObject.js b/app/Game/Channel/GameObjects/GameObject.js similarity index 100% rename from app/Game/Server/GameObjects/GameObject.js rename to app/Game/Channel/GameObjects/GameObject.js diff --git a/app/Game/Server/GameObjects/Item.js b/app/Game/Channel/GameObjects/Item.js similarity index 100% rename from app/Game/Server/GameObjects/Item.js rename to app/Game/Channel/GameObjects/Item.js diff --git a/app/Game/Server/GameObjects/Items/RagDoll.js b/app/Game/Channel/GameObjects/Items/RagDoll.js similarity index 100% rename from app/Game/Server/GameObjects/Items/RagDoll.js rename to app/Game/Channel/GameObjects/Items/RagDoll.js diff --git a/app/Game/Server/GameObjects/Items/Skateboard.js b/app/Game/Channel/GameObjects/Items/Skateboard.js similarity index 100% rename from app/Game/Server/GameObjects/Items/Skateboard.js rename to app/Game/Channel/GameObjects/Items/Skateboard.js diff --git a/app/Game/Server/GameObjects/SpectatorDoll.js b/app/Game/Channel/GameObjects/SpectatorDoll.js similarity index 100% rename from app/Game/Server/GameObjects/SpectatorDoll.js rename to app/Game/Channel/GameObjects/SpectatorDoll.js diff --git a/app/Game/Server/GameObjects/Tile.js b/app/Game/Channel/GameObjects/Tile.js similarity index 100% rename from app/Game/Server/GameObjects/Tile.js rename to app/Game/Channel/GameObjects/Tile.js diff --git a/app/Game/Server/Loader/Level.js b/app/Game/Channel/Loader/Level.js similarity index 100% rename from app/Game/Server/Loader/Level.js rename to app/Game/Channel/Loader/Level.js diff --git a/app/Game/Server/Loader/TiledLevel.js b/app/Game/Channel/Loader/TiledLevel.js similarity index 100% rename from app/Game/Server/Loader/TiledLevel.js rename to app/Game/Channel/Loader/TiledLevel.js diff --git a/app/Game/Server/Physics/Engine.js b/app/Game/Channel/Physics/Engine.js similarity index 100% rename from app/Game/Server/Physics/Engine.js rename to app/Game/Channel/Physics/Engine.js diff --git a/app/Game/Server/PipeToLobby.js b/app/Game/Channel/PipeToServer.js similarity index 60% rename from app/Game/Server/PipeToLobby.js rename to app/Game/Channel/PipeToServer.js index 33dc741..113ceca 100755 --- a/app/Game/Server/PipeToLobby.js +++ b/app/Game/Channel/PipeToServer.js @@ -1,11 +1,11 @@ define([ "Lib/Utilities/NotificationCenter", - "Game/Server/Channel" + "Game/Channel/Channel" ], function (Nc, Channel) { - function PipeToLobby (process) { + function PipeToServer (process) { var self = this; @@ -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); } @@ -28,7 +27,7 @@ function (Nc, Channel) { }); } - PipeToLobby.prototype.send = function (recipient, data) { + PipeToServer.prototype.send = function (recipient, data) { var message = { recipient: recipient, data: data @@ -37,10 +36,15 @@ function (Nc, Channel) { this.process.send(message); }; - PipeToLobby.prototype.onMessage = function (message) { + PipeToServer.prototype.onMessage = function (message) { Nc.trigger(message.recipient + '/controlCommand', message); } - return PipeToLobby; + 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/Server/Player.js b/app/Game/Channel/Player.js similarity index 96% rename from app/Game/Server/Player.js rename to app/Game/Channel/Player.js index 6cbdebb..188160c 100755 --- a/app/Game/Server/Player.js +++ b/app/Game/Channel/Player.js @@ -5,8 +5,8 @@ define([ function (Parent, Nc) { - function Player(id, physicsEngine) { - Parent.call(this, id, physicsEngine); + function Player(id, physicsEngine, user) { + Parent.call(this, id, physicsEngine, user); } Player.prototype = Object.create(Parent.prototype); diff --git a/app/Game/Server/User.js b/app/Game/Channel/User.js similarity index 87% rename from app/Game/Server/User.js rename to app/Game/Channel/User.js index c573a7f..486b8de 100755 --- a/app/Game/Server/User.js +++ b/app/Game/Channel/User.js @@ -7,20 +7,13 @@ define([ function(Parent, Nc, ProtocolHelper, ProtocolParser) { - function User(id, channel) { - Parent.call(this, id); + function User(id, options) { + Parent.call(this, id, options); - this.channel = channel; this.player = null; this.isReady = false; var self = this; - Nc.on('user/joined', function(user) { // FIXME: use sendToAllUsersExcept instead - if(user.id != self.id) { - self.sendControlCommand("userJoined", user.id); - } - }); - Nc.on('user/' + this.id + "/joinSuccess", function(options) { self.sendControlCommand("joinSuccess", options); }); diff --git a/app/Game/Client/GameController.js b/app/Game/Client/GameController.js index 2146858..f1e1c6e 100755 --- a/app/Game/Client/GameController.js +++ b/app/Game/Client/GameController.js @@ -208,10 +208,12 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque }; var sortedPlayers = playersArray.sort(function(a,b) { - if(a.score > b.score) return 1; - if(a.score < b.score) return -1; - if(a.deaths < b.deaths) return 1; - if(a.deaths > b.deaths) return -1; + if(a.stats.score > b.stats.score) return -1; + if(a.stats.score < b.stats.score) return 1; + if(a.stats.deaths < b.stats.deaths) return -1; + if(a.stats.deaths > b.stats.deaths) return 1; + if(a.stats.health > b.stats.health) return -1; + if(a.stats.health < b.stats.health) return 1; return 0; }); @@ -236,7 +238,7 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque var lines = []; sortedPlayers.forEach(function(player, i) { - var name = player == this.me ? "You" : "Player " + (Object.keys(this.players).indexOf(player.id) + 1); + var name = player.getNickname(); lines.push( pad("" + (i + 1) + ".", 2, false) + " " + pad(name, 12, true) + diff --git a/app/Game/Client/Networker.js b/app/Game/Client/Networker.js index bc665ca..dbf7d8a 100755 --- a/app/Game/Client/Networker.js +++ b/app/Game/Client/Networker.js @@ -13,14 +13,14 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { this.socketLink = socketLink; this.gameController = null; this.users = {}; - this.userId = null; this.socketLink.on('connect', this.onConnect.bind(this)); this.socketLink.on('disconnect', this.onDisconnect.bind(this)); 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 +35,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 = "/"; } @@ -51,12 +56,11 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { Networker.prototype.onJoinSuccess = function (options) { console.log("join success") - this.userId = options.userId; this.gameController = new GameController(); this.gameController.loadLevel(options.levelUid); - this.onUserJoined(options.userId); + this.onUserJoined(options.user); if (options.joinedUsers) { for(var i = 0; i < options.joinedUsers.length; i++) { @@ -67,6 +71,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]); @@ -103,9 +112,10 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { // Commands from server - Networker.prototype.onUserJoined = function (userId) { - var user = new User(userId); - this.users[userId] = user; + Networker.prototype.onUserJoined = function (options) { + var user = new User(options.id, options); + console.log(options.nickname) + this.users[user.id] = user; if (this.gameController && this.gameController.level @@ -116,8 +126,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/Client/Player.js b/app/Game/Client/Player.js index aa84f28..b21171c 100755 --- a/app/Game/Client/Player.js +++ b/app/Game/Client/Player.js @@ -6,8 +6,8 @@ define([ function (Parent, Nc, Settings) { - function Player(id, physicsEngine) { - Parent.call(this, id, physicsEngine); + function Player(id, physicsEngine, user) { + Parent.call(this, id, physicsEngine, user); this.playerInfoView = null; this.playerInfoViewVisibleTimeout = null; @@ -81,6 +81,10 @@ function (Parent, Nc, Settings) { } }; + Player.prototype.getNickname = function() { + return this.user.options.nickname; + }; + Player.prototype.render = function() { // dolls are self responsible 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..1579d68 100755 --- a/app/Game/Core/GameController.js +++ b/app/Game/Core/GameController.js @@ -79,18 +79,22 @@ 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) { - var player = new Player(user.id, this.physicsEngine); + var player = new Player(user.id, this.physicsEngine, user); this.players[user.id] = player; return player; }; diff --git a/app/Game/Core/Player.js b/app/Game/Core/Player.js index a47b06a..7209806 100755 --- a/app/Game/Core/Player.js +++ b/app/Game/Core/Player.js @@ -10,13 +10,14 @@ define([ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { - function Player (id, physicsEngine) { + function Player (id, physicsEngine, user) { this.stats = { health: 100, deaths: 0, score: 0 } + this.user = user; this.physicsEngine = physicsEngine; this.playerController = null; this.doll; diff --git a/app/Game/Core/User.js b/app/Game/Core/User.js index 3934880..de3264f 100755 --- a/app/Game/Core/User.js +++ b/app/Game/Core/User.js @@ -1,7 +1,13 @@ define(function () { - function User (id) { + function User (id, options) { this.id = id; + this.options = options; + + this.options.id = this.id; + if(!this.options.nickname) { + this.options.nickname = this.id; + } } return User; diff --git a/app/Lib/Utilities/Extensions.js b/app/Lib/Utilities/Extensions.js index 6af99e1..1f9c032 100755 --- a/app/Lib/Utilities/Extensions.js +++ b/app/Lib/Utilities/Extensions.js @@ -8,7 +8,7 @@ function() { return f + this.substr(1); } - if(GLOBALS.context == "Server") { + if(GLOBALS.context == "Channel") { console.checkpoint = function (s) { console.log(' \033[32mbeep - \033[0m' + s); diff --git a/app/Lib/Utilities/NotificationCenter.js b/app/Lib/Utilities/NotificationCenter.js index 7c8b6db..76d3d3f 100755 --- a/app/Lib/Utilities/NotificationCenter.js +++ b/app/Lib/Utilities/NotificationCenter.js @@ -6,6 +6,71 @@ function () { function NotificationCenter () { this.topics = {}; this.subUid = -1; +/* + var i = 0; + this.nc = { + client: { + view: { + mesh: { + create: i++, + add: i++, + remove: i++, + update: i++ + }, + playerInfo: { + createAndAdd: i++, + remove: i++, + update: i++ + }, + preloadBar: { + update: i++ + }, + fullScreen: { + change: i++ + }, + debugMode: { + toggle: i++ + }, + gameInfo: { + toggle: i++ + } + events: { + ready: i++ + } + }, + input: { + handAction: { + request: i++ + }, + xy: { + change: i++ + } + }, + server: { + gameCommand: { + send: i++ + } + } + }, + core: { + game: { + gameObject: { + add: i++, + remove: i++ + } + events: { + level: { + loaded: i++ + } + } + } + }, + channel: { + pipeToServer: function(v) { return v + "-ns.channel.pipeToServer")} + } + + }; + */ } NotificationCenter.prototype.trigger = function (topic /*, arguments*/) { diff --git a/app/Lobby/Api.js b/app/Lobby/Api.js deleted file mode 100644 index 4efd2ff..0000000 --- a/app/Lobby/Api.js +++ /dev/null @@ -1,52 +0,0 @@ -define([ - "Lib/Utilities/NotificationCenter", - "Lib/Utilities/Protocol/Helper" -], - -function (Nc, ProtocolHelper) { - - function Api(coordinator) { - this.coordinator = coordinator; - this.isError = false; - this.output = null; - } - - Api.prototype.handleCall = function(queryParameters) { - - var command; - try { - var message = JSON.parse(queryParameters); - command = message.command; - } catch(e) { - console.error(e) - } - - var output = null; - switch(command) { - case "getChannels": - output = this.coordinator.getChannels(); - break; - default: - this.isError = true; - output = "Command not found"; - break; - } - - 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.getContentType = function() { - return "application/json"; - }; - - return Api; - -}); \ No newline at end of file diff --git a/app/Lobby/Coordinator.js b/app/Lobby/Coordinator.js deleted file mode 100755 index b1d2d60..0000000 --- a/app/Lobby/Coordinator.js +++ /dev/null @@ -1,107 +0,0 @@ -define([ - "Lobby/User", - "Game/Server/Channel", - "Lobby/PipeToChannel", - "Lib/Utilities/NotificationCenter" -], - -function (User, Channel, PipeToChannel, Nc) { - - function Coordinator () { - this.channelPipes = {}; - this.lobbyUsers = {}; - - 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'); - } - - 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]; - } - - 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.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 = []; - for (var channelName in this.channelPipes) { - list.push({ - name: channelName - }); - } - return list; - }; - - return Coordinator; - -}); \ No newline at end of file diff --git a/app/Server/Api.js b/app/Server/Api.js new file mode 100644 index 0000000..846deb5 --- /dev/null +++ b/app/Server/Api.js @@ -0,0 +1,69 @@ +define([ + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Protocol/Helper" +], + +function (Nc, ProtocolHelper) { + + function Api(coordinator) { + this.coordinator = coordinator; + this.isError = false; + this.output = null; + } + + Api.prototype.handleCall = function(queryParameters) { + + 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) + } + + 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; + } + + 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.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; + +}); \ No newline at end of file diff --git a/app/Server/Coordinator.js b/app/Server/Coordinator.js new file mode 100644 index 0000000..317534d --- /dev/null +++ b/app/Server/Coordinator.js @@ -0,0 +1,65 @@ +define([ + "Server/User", + "Game/Channel/Channel", + "Server/PipeToChannel", + "Lib/Utilities/NotificationCenter", + "Game/Config/Settings" +], + +function (User, Channel, PipeToChannel, Nc, Settings) { + + function Coordinator() { + this.channelPipes = {}; + + Nc.on('coordinator/message', this.onMessage, this); + + console.checkpoint('create Coordinator'); + } + + Coordinator.prototype.createUser = function (socketLink) { + new User(socketLink, this); + } + + Coordinator.prototype.assignUserToChannel = function (user, channelName) { + var channelPipe = this.channelPipes[channelName]; + user.setChannelPipe(channelPipe); + } + + Coordinator.prototype.onDestroyPipe = function(channelName) { + delete this.channelPipes[channelName]; + } + + Coordinator.prototype.getChannels = function(options) { + 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 + } + }; + + 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/Lobby/PipeToChannel.js b/app/Server/PipeToChannel.js similarity index 63% rename from app/Lobby/PipeToChannel.js rename to app/Server/PipeToChannel.js index 8c6fe29..e23285e 100755 --- a/app/Lobby/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/Lobby/User.js b/app/Server/User.js old mode 100755 new mode 100644 similarity index 56% rename from app/Lobby/User.js rename to app/Server/User.js index 57fe570..40b96a4 --- a/app/Lobby/User.js +++ b/app/Server/User.js @@ -1,60 +1,81 @@ -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; + } + + var userOptions = { + id: this.id, + nickname: options.nickname + } + this.channelPipe.send('channel', { addUser: userOptions }); + }; + + 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/channel.js b/channel.js index 6d397d8..3294623 100755 --- a/channel.js +++ b/channel.js @@ -1,4 +1,4 @@ -GLOBALS = { context: "Server" }; +GLOBALS = { context: "Channel" }; var requirejs = require('requirejs'); requirejs.config({ @@ -10,11 +10,11 @@ requirejs.config({ var inspector = {}; requirejs([ - "Game/Server/PipeToLobby" + "Game/Channel/PipeToServer" ], -function (PipeToLobby) { - var PipeToLobby = new PipeToLobby(process); +function (PipeToServer) { + var PipeToServer = new PipeToServer(process); - inspector.PipeToLobby = PipeToLobby; + inspector.PipeToServer = PipeToServer; }); \ No newline at end of file diff --git a/server.js b/server.js index 21dd3ef..9d829aa 100755 --- a/server.js +++ b/server.js @@ -1,4 +1,4 @@ -GLOBALS = { context: "Server" }; +GLOBALS = { context: "Channel" }; var requirejs = require('requirejs'); var inspector; @@ -23,7 +23,7 @@ var options = { requirejs([ "Bootstrap/HttpServer", "Bootstrap/Socket", - "Lobby/Coordinator" + "Server/Coordinator" ], function (HttpServer, Socket, Coordinator) { diff --git a/static/html/index.html b/static/html/index.html index eda77ba..70127b6 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