added max user and refactored coordinator/serveruser a bit fixes #105

This commit is contained in:
logsol 2015-03-03 23:43:15 +01:00
parent ccd146f01b
commit 6b472dc134
9 changed files with 156 additions and 66 deletions

View file

@ -53,6 +53,7 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) {
this.sendCommand('join', options); this.sendCommand('join', options);
DomController.setConnected(true); DomController.setConnected(true);
} else { } else {
alert("Error: no channel name");
window.location.href = "/"; window.location.href = "/";
} }
} }
@ -80,7 +81,7 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) {
} }
Networker.prototype.onJoinError = function(options) { Networker.prototype.onJoinError = function(options) {
// alert(options.message); alert(options.message);
window.location.href = "/"; window.location.href = "/";
}; };

View file

@ -76,6 +76,7 @@ define(function() {
NETWORK_LOG_FILTER: ['ping', 'pong', 'worldUpdate', 'lookAt'], NETWORK_LOG_FILTER: ['ping', 'pong', 'worldUpdate', 'lookAt'],
// CHANNEL // CHANNEL
CHANNEL_MAX_USERS: 20,
CHANNEL_DESTRUCTION_TIME: 5 * 60, CHANNEL_DESTRUCTION_TIME: 5 * 60,
CHANNEL_END_ROUND_TIME: 4, //10, CHANNEL_END_ROUND_TIME: 4, //10,
CHANNEL_DEFAULT_MAX_USERS: 40, CHANNEL_DEFAULT_MAX_USERS: 40,

View file

@ -144,11 +144,19 @@ function (ColorConverter, Exception, PointerLockManager, Qs) {
var html = ""; var html = "";
if(list.length > 0) { if(list.length > 0) {
for (var i = 0; i < list.length; i++) { for (var i = 0; i < list.length; i++) {
var channel = list[i]; var channel = list[i];
html += "<tr>"; var fullState = channel.playerCount >= channel.maxUsers;
html += "<td><a href='#" + channel.name + "'>" + channel.name + "</a></td>"; var fullString = fullState ? "&nbsp;Full" : "";
var fullStyle = fullState ? 'class="full"' : "";
var players = channel.playerCount
? "<span id='players'>Player:<br>- " + channel.players.join("<br>- ") + "</span>"
: "";
html += "<tr "+fullStyle+">";
html += "<td><a href='#" + channel.channelName + "'>" + channel.channelName + "</a></td>";
html += "<td>death match</td>"; html += "<td>death match</td>";
html += "<td>" + channel.playerCount + "</td>"; html += "<td class='playersCell'>" + channel.playerCount + fullString + players + "</td>";
html += "</tr>"; html += "</tr>";
}; };
} else { } else {
@ -190,8 +198,15 @@ function (ColorConverter, Exception, PointerLockManager, Qs) {
Qs.$("form#createform").onsubmit = function(e) { Qs.$("form#createform").onsubmit = function(e) {
try { try {
var channelName = Qs.$("#customname").value;
create(channelName, onCreateSuccess); var options = {
channelName: Qs.$("#customname").value,
levelUids: getSelectedMaps(),
maxUsers: parseInt(Qs.$("#userLimit").value, 10),
scoreLimit: parseInt(Qs.$("#scoreLimit").value, 10)
};
create(options, onCreateSuccess);
} catch(e) { } catch(e) {
console.error(e) console.error(e)
} }
@ -246,7 +261,15 @@ function (ColorConverter, Exception, PointerLockManager, Qs) {
} }
if(!channelExists(list, defaultChannelName)) { if(!channelExists(list, defaultChannelName)) {
create(defaultChannelName, function() {
var options = {
channelName: defaultChannelName,
levelUids: getSelectedMaps(),
maxUsers: parseInt(Qs.$("#userLimit").value, 10),
scoreLimit: parseInt(Qs.$("#scoreLimit").value, 10)
};
create(options, function() {
join(nickname, defaultChannelName); // only called on success join(nickname, defaultChannelName); // only called on success
}); });
} else { } else {
@ -283,7 +306,7 @@ function (ColorConverter, Exception, PointerLockManager, Qs) {
function channelExists(list, channelName) { function channelExists(list, channelName) {
for (var i = 0; i < list.length; i++) { for (var i = 0; i < list.length; i++) {
var channel = list[i]; var channel = list[i];
if(channel.name == channelName) { if(channel.channelName == channelName) {
return true; return true;
} }
} }
@ -302,18 +325,33 @@ function (ColorConverter, Exception, PointerLockManager, Qs) {
return true; return true;
} }
function validateForCreate(channelName, maps) { function validateForCreate(options) {
return true; return true;
if(maps.length < 1) { // great validation on server side does it all.
/*
if(options.levelUids.length < 1) {
alert("Please choose at least one map.") alert("Please choose at least one map.")
return false; return false;
} }
if(!channelName) { if(!options.channelName || options.channelName.length < 3) {
alert("Please provide a channel name.") alert("Please provide a channel name of at least 3 characters.")
return false;
}
if(!parseInt(options.maxUsers) > 1 || !parseInt(options.maxUsers) < 20) {
alert("Number of users must be larger than 1 and smaller than 20.");
return false;
}
if(!parseInt(options.scoreLimit) > 1 || !parseInt(options.scoreLimit) < 99) {
alert("Score limit must be larger than 1 and smaller than 99.");
return false; return false;
} }
return true; return true;
*/
} }
function getSelectedMaps() { function getSelectedMaps() {
@ -367,20 +405,12 @@ function (ColorConverter, Exception, PointerLockManager, Qs) {
} }
} }
function create(channelName, callback) { function create(options, callback) {
var maps = getSelectedMaps();
if(validateForCreate(channelName, maps)) { if(validateForCreate(options)) {
var options = { options["minUsers"] = 1;
channelName: channelName, localStorage["customname"] = options.channelName;
levelUids: maps,
maxUsers: 10,
minUsers: 2,
scoreLimit: parseInt(Qs.$("#scoreLimit").value, 10)
}
localStorage["customname"] = channelName;
ajax("createChannel", options, function(responseText) { ajax("createChannel", options, function(responseText) {
if(typeof callback == 'function') { if(typeof callback == 'function') {

View file

@ -96,22 +96,23 @@ function (Nc, ProtocolHelper, validate, Options, Settings, FileSystem) {
newOptions.maxUsers = options.maxUsers; newOptions.maxUsers = options.maxUsers;
} else { } else {
this.isError = true; this.isError = true;
return "Could not create channel, Max users invalid. (Limited to " + Settings.CHANNEL_MAX_USERS + " users)"; return "Could not create channel, user limit invalid. Limited to " + Settings.CHANNEL_MAX_USERS + " users";
} }
if(validate(options.minUsers, {optional: true, type: 'number', min: 0, max: Settings.CHANNEL_MAX_USERS})) { if(validate(options.minUsers, {optional: true, type: 'number', min: 0, max: Settings.CHANNEL_MAX_USERS})) {
newOptions.minUsers = options.minUsers; newOptions.minUsers = options.minUsers;
} else { } else {
this.isError = true; this.isError = true;
return "Could not create channel, Max users too high. Limited to: " + Settings.CHANNEL_MAX_USERS; return "Could not create channel, minimal users limit too high. Limited to: " + Settings.CHANNEL_MAX_USERS;
} }
// Limits // Limits
if(validate(options.scoreLimit, {type: 'number', min: 1, max: 999})) { var scoreLimitPreferences = {type: 'number', min: 1, max: 999};
if(validate(options.scoreLimit, scoreLimitPreferences)) {
newOptions.scoreLimit = options.scoreLimit; newOptions.scoreLimit = options.scoreLimit;
} else { } else {
this.isError = true; this.isError = true;
return "Could not create channel, score limit (" + options.scoreLimit + ")."; return "Could not create channel, score limit (" + options.scoreLimit + ") must be between " + scoreLimitPreferences.min + " and " + scoreLimitPreferences.max;
} }
var defaultOptions = { var defaultOptions = {

View file

@ -11,7 +11,6 @@ function (User, PipeToChannel, Nc, Settings) {
function Coordinator() { function Coordinator() {
this.channelPipes = {}; this.channelPipes = {};
this.users = [];
Nc.on(Nc.ns.server.events.controlCommand.coordinator, this.onMessage, this); Nc.on(Nc.ns.server.events.controlCommand.coordinator, this.onMessage, this);
@ -19,21 +18,12 @@ function (User, PipeToChannel, Nc, Settings) {
} }
Coordinator.prototype.createUser = function (socketLink) { Coordinator.prototype.createUser = function (socketLink) {
this.users.push(new User(socketLink, this)); new User(socketLink, this);
} }
Coordinator.prototype.removeUser = function (user) { // was assignUserToChannel...
for(var i = 0; i < this.users.length; i++) { Coordinator.prototype.getChannelPipeByName = function (channelName) {
if(this.users[i] === user) { return this.channelPipes[channelName];
this.users.splice(i, 1);
break;
}
}
}
Coordinator.prototype.assignUserToChannel = function (user, channelName) {
var channelPipe = this.channelPipes[channelName];
user.setChannelPipe(channelPipe);
} }
Coordinator.prototype.onDestroyPipe = function(channelName) { Coordinator.prototype.onDestroyPipe = function(channelName) {
@ -44,18 +34,17 @@ function (User, PipeToChannel, Nc, Settings) {
var list = []; var list = [];
for (var channelName in this.channelPipes) { for (var channelName in this.channelPipes) {
var count = 0; var options = this.channelPipes[channelName].options;
for(var i = 0; i < this.users.length; i++) { var playerNames = [];
if(this.users[i].channelPipe === this.channelPipes[channelName]){ var users = this.channelPipes[channelName].getUsers();
count++; for (var i = 0; i < users.length; i++) {
} playerNames[i] = users[i].options.nickname;
} };
options.players = playerNames;
options.playerCount = options.players.length;
list.push({ list.push(options);
name: channelName,
playerCount: count
});
} }
return list; return list;
} }

View file

@ -12,6 +12,8 @@ function (Nc, childProcess) {
function PipeToChannel (options) { function PipeToChannel (options) {
this.fork = null; this.fork = null;
this.options = options;
this.users = [];
try { try {
this.fork = fork('channel.js' this.fork = fork('channel.js'
@ -28,8 +30,6 @@ function (Nc, childProcess) {
this.send('channel/' + options.channelName, { CREATE: true, options: options }); this.send('channel/' + options.channelName, { CREATE: true, options: options });
this.fork.on('message', this.onMessage.bind(this)); this.fork.on('message', this.onMessage.bind(this));
var self = this;
} }
// While creating user // While creating user
@ -52,6 +52,10 @@ function (Nc, childProcess) {
this.fork.send(message); this.fork.send(message);
} }
PipeToChannel.prototype.isFull = function() {
return this.users.length >= this.options.maxUsers;
};
PipeToChannel.prototype.onMessage = function (message) { PipeToChannel.prototype.onMessage = function (message) {
switch(message.recipient) { switch(message.recipient) {
case 'coordinator': case 'coordinator':
@ -64,6 +68,26 @@ function (Nc, childProcess) {
} }
PipeToChannel.prototype.addUser = function(user) {
this.users.push(user);
this.send('channel', { addUser: user.options });
};
PipeToChannel.prototype.removeUser = function(user) {
for(var i = 0; i < this.users.length; i++) {
if(this.users[i] === user) {
this.users.splice(i, 1);
break;
}
}
this.send('channel', { releaseUser: user.id });
};
PipeToChannel.prototype.getUsers = function() {
return this.users;
};
return PipeToChannel; return PipeToChannel;
}); });

View file

@ -14,6 +14,7 @@ function (Parent, ProtocolHelper, Nc) {
this.coordinator = coordinator; this.coordinator = coordinator;
this.socketLink = socketLink; this.socketLink = socketLink;
this.channelPipe = null; this.channelPipe = null;
this.options = null;
socketLink.on('message', this.onMessage.bind(this)); socketLink.on('message', this.onMessage.bind(this));
socketLink.on('disconnect', this.onDisconnect.bind(this)); socketLink.on('disconnect', this.onDisconnect.bind(this));
@ -23,15 +24,23 @@ function (Parent, ProtocolHelper, Nc) {
User.prototype = Object.create(Parent.prototype); User.prototype = Object.create(Parent.prototype);
/*
User.prototype.setChannelPipe = function(channelPipe) { User.prototype.setChannelPipe = function(channelPipe) {
if(channelPipe) { if(channelPipe) {
this.channelPipe = channelPipe; if (channelPipe.isWithinUserLimit()) {
this.channelPipe = channelPipe;
this.channelPipe.addUser(this);
} else {
var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel is full"});
this.socketLink.send(message);
}
} else { } else {
var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel not found"}); var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel not found"});
this.socketLink.send(message); this.socketLink.send(message);
} }
}; };
*/
// Socket callbacks // Socket callbacks
@ -41,14 +50,12 @@ function (Parent, ProtocolHelper, Nc) {
User.prototype.onDisconnect = function () { User.prototype.onDisconnect = function () {
this.coordinator.removeUser(this);
if(!this.channelPipe) { if(!this.channelPipe) {
console.warn("Disconnecting user without a channel."); console.warn("Disconnecting user without a channel. (Maybe channel was full)");
return; return;
} }
this.channelPipe.send('channel', { releaseUser: this.id }); this.channelPipe.removeUser(this);
} }
@ -56,18 +63,29 @@ function (Parent, ProtocolHelper, Nc) {
// Remember: control commands are coordinator relevant commands // Remember: control commands are coordinator relevant commands
User.prototype.onJoin = function(options) { User.prototype.onJoin = function(options) {
this.coordinator.assignUserToChannel(this, options.channelName);
if(!this.channelPipe) { var channelPipe = this.coordinator.getChannelPipeByName(options.channelName);
console.warn("Can not join user because channel (" + options.channelName + ") does not exist.")
if(!channelPipe) {
var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel " + options.channelName + " not found."});
this.socketLink.send(message);
return; return;
} }
if (channelPipe.isFull()) {
var message = ProtocolHelper.encodeCommand("joinError", {message:"Sorry! Channel " + options.channelName + " is full."});
this.socketLink.send(message);
return;
}
this.channelPipe = channelPipe;
var userOptions = { var userOptions = {
id: this.id, id: this.id,
nickname: options.nickname nickname: options.nickname
} }
this.channelPipe.send('channel', { addUser: userOptions }); this.options = userOptions;
this.channelPipe.addUser(this);
}; };
/* FIXME: watch out and check in wich direction game and control commands flow */ /* FIXME: watch out and check in wich direction game and control commands flow */

View file

@ -20,7 +20,7 @@ body {
background: #222; background: #222;
color: #ccc; color: #ccc;
font-family: 'Joystix', "Lucida Grande", sans-serif; font-family: 'Joystix', "Lucida Grande", sans-serif;
text-transform: uppercase; /*text-transform: uppercase;*/
margin: 0; margin: 0;
padding: 0; padding: 0;
display: table; display: table;
@ -113,7 +113,7 @@ article#menu {
margin: 4em auto; margin: 4em auto;
background: #1a1a1a; background: #1a1a1a;
padding: 2em; padding: 2em;
max-width: 30em; max-width: 40em;
} }
table, th, td { table, th, td {
@ -142,6 +142,31 @@ tr:hover td {
background: #222; background: #222;
} }
.full {
color: #777;
}
.full a {
color: inherit;
cursor:not-allowed;;
}
#players {
position: absolute;
border: 1px solid #777;
padding: 20px;
margin-left: 35px;
margin-top: -10px;
background: rgba(20, 20, 20, 0.95);
box-shadow: 5px 5px 5px #000;
display: none;
}
.playersCell:hover #players {
display: block;
}
a { a {
color: #ccc; color: #ccc;
} }

View file

@ -22,6 +22,7 @@
<h2>Create your own!</h2> <h2>Create your own!</h2>
<p><label>Name:<br> <input id="customname"></label></p> <p><label>Name:<br> <input id="customname"></label></p>
<p><label>Score limit:<br> <input id="scoreLimit" type="number" value="5"></label></p> <p><label>Score limit:<br> <input id="scoreLimit" type="number" value="5"></label></p>
<p><label>User limit:<br> <input id="userLimit" type="number" value="10"></label></p>
<fieldset> <fieldset>
<legend>Maps</legend> <legend>Maps</legend>
<ul id="maps"> <ul id="maps">