I am trying to build a simple chat server(practice), with nodejs. I have a global var chatRooms, and it can be changed or read by all requests to the server. I am trying to figure out if I am possibly causing some race conditions. I am also using sockets via the node net module.
var net = require('net');
var chatRooms = {fender:[]};
function enterRoom(socket, room){
charRooms[room].push(socket.username);
}
function leaveRoom(socket, room){
var indexOfUser = chatRooms[room].indexOf(socket.username);
chatRoom[room].splice(indexOfUser, 1);
}
so, I am trying to figure out this situation:
user A joins chat room fender: - chatRoom looks like {fender:['A']}
user B joins chat room fender: - chatRoom looks like {fender:['A', 'B']}
user B and A want to leave the room, and do so at the same time:
user B indexOfUser === 1;
user A indexOfUser === 0;
user A splices chatRooms gobal var, before user B does.
global ChatRoom var now looks like (fender:['B']}
user B indexOfUser no longer === 1
so when user B splices ChatRooms it will do so using the wrong index.
Is something like this possible, or the single threaded nature of node prevents this from happening.
What is cant figure out is if these two lines of code will be problematic
var indexOfUser = chatRooms[room].indexOf(socket.username);
// another request alters charRooms before the next line of code is reached.
chatRoom[room].splice(indexOfUser, 1);
Single threaded nature of node prevents that. Every full block of code is strongly isolated, i.e. no other code can run in "parallel".
Related
So I am developing a simple game. The logic flow is when 2 players connect to the room, the game will initialize and emit a 3 seconds countdown event alongside with a button . if one player clicks the button, that player will emit an event making him/her the host of the game.
The button will appear for 3 seconds. If it disappear without being clicked, the server will randomly pick the host.
Whether or not the button is clicked,after 3 seconds all clients will emit a "ready" event.
Here is my code.
if the button is clicked, the "host" event will be emitted by the client,and my server side code is
client.on('host',function(){
var player = room.getPlayer(client.id);
player.isHost=true;
});
Then here is the server side for the "ready" event.
client.on("ready", function() {
var player = room.getPlayer(client.id);
var table = room.getTable(player.tableID);
if (2>1) {
for (var i = 0; i < table.players.length; i++) {
if (table.players[i].isHost===true){
console.log("you are the host")
} else {
console.log("we are going to randomly pick a host")
}
}
}
})
Now if no players click the button the terminal will show
we are going to randomly pick a host
we are going to randomly pick a host
we are going to randomly pick a host
we are going to randomly pick a host
Otherwise it will be like
we are going to randomly pick a host
you are the host
we are going to randomly pick a host
you are the host
At this stage only 2 clients are allow for the game so the players.length is 2.It seems like the if/else will be executed same time as the players.length?
I think the first thing to mention is that the if (2>1) { ... is completely unnecessary. The code will simply go through to the for loop as if it's not there.
Your code besides the if statement, is honestly ok. There's nothing that screams 'I cause more loops then necessary'. At this point I would suggest posting anything in the project related to:
Multithreading
Socket Handling / connecting the socket outside of this
I believe more information is due.
Thanks for the helps. Yes the for loop is not necessary and it is the cause of the problem. My solution is to detach it from the if-else statement.
for (var i = 0; i < table.players.length; i++) {
}
//test if Host exit in the table
if (table.ifHost===true){
console.log("you are the host")
} else {
var randomNumber = Math.floor(Math.random() * table.playerLimit);
var Host=table.players[randomNumber]
}
The interesting thing I found out about the socket io is when two sockets fire this event, if Host doesn't exit,the first one will execute the else(noHost) and then create a Host. The second socket will execute the if condition as the host was created by the previous socket....
I have a Meteor application where I use RiveScript, a chatbot module for Node. The module can save some aspects of user input. My issue is that when I run the module on server, the state is not saved for one user, but for all users. How would I go about creating a state for each user?
One method would be to create a new bot for each user like so:
let RiveScript = require('rivescript');
let users = {};
Meteor.onConnection(connection => {
users[connection.id] = new RiveScript({utf8: true});
users[connection.id].bot.loadDirectory('directory/',
() => {
users[connection.id].bot.sortReplies();
}
);
connection.onClose(() => {
delete users[connection.id]
})
});
However, in terms of memory management this can cause an issue. Are there any commonly used patterns in this regard?
I am not familiar with Meteor nor RiveScript, so I hope I'm answering your question.
If you want to keep a certain state for each conversation, e.g.
Chat #1 : the bot is waiting for the user to answer a particular question
Chat #2 : the bot is waiting for a command
...
Why don't you use a simple array mapping a conversation identifier (or in your case connection.id, I'm guessing) to its current state?
Example:
// Just to simulate an enumeration
var States = {WAITING: 0, WAITING_XXX_CONFIRMATION: 1, ...}
var state = [];
Meteor.onConnection(connection => {
state[connection.id] = States.WAITING;
});
// Somewhere else, e.g. in response to a command
if (state[connection.id] == WAITING_XXX_CONFIRMATION && input === 'yes') {
// do something
// reset the state
set[connection.id] = WAITING;
}
You could then keep track of the state of every conversation, and act accordingly. You could also wrap the state management inside an object to make it more reusable and nice to use (e.g. StateManager, to be able to make calls such as StateManager.of(chatId).set(newState).
Hope this helps.
It seems that socket.io cannot send the list of connected users, like this:
socket.emit('users', sIo.sockets.clients());
It gives me the following error:
/Users/enrico/Desktop/helloExpress/node_modules/socket.io/lib/parser.js:75
data = JSON.stringify(ev);
^
TypeError: Converting circular structure to JSON
Apparently it cannot stringify the returned value from sIo.sockets.clients() Any ideas on how to fix this? Thanks in advance.
Since the problem is a circular reference there no real 'fixing it'. Circular reference means that some object in the structure points to another part of the object, making an infinite loop. What you can do is something like this.
var returnList = [];
var clients = sIo.sockets.clients(); // grab list of clients, assuming its an array
for(var i = 0; i < clients.length; i++) {
var client = clients[i]; // next client in array
// push values into return list
returnList.push({
name: client.name,
someOther: client.value,
another: client.thing
});
}
// emit cleaned up list
socket.emit('users', returnList);
With this code you can cherry pick the values you want and send only those. This is good for several other reasons. Since this clients list is likely an internal implementation is might also send information about other clients connection.
This is all also pretty speculative as I'm not 100% what libraries you're using, looks like Socket.IO but I cannot find any socket.clients() method.
I'm working with Socket.io, node.js, and express to make a simple two player game that can be played online. Everything I've found on the subject so far either doesn't address my question or is far more complex than what I'm after.
Essentially, I want players to be able to join a room (which I have solved already), and when in a room, need to assign "player 1" and "player 2" attributes to each player. I imagine assigning player 1 to the first connected user and player 2 to the second, but I haven't figured out exactly how to do the assignment of values/variables to an individual socket connection.
You can add attributes to individual sockets like this:
io.sockets.on('connection', function(socket){
socket.name = 'John';
console.log(socket.name); // prints out 'John'
});
It seems like what you're looking for should be pretty straightforward. I like #iheartramen's answer, except it makes it difficult to determine what sockets belong to what room. What I would do is keep a list of rooms, and what sockets belong to them:
var _rooms = [
{
id: 'Some Unique Room ID',
playerSockets: []
}
//...
}
Then when a player connects to a given room, you just add their connection to the list:
io.sockets.on('connection',function(socket){
var room = getRoom(); // however it is you're mapping rooms...
// this returns a reference to a room object
// in the _rooms array
// you can cap the number of players here
socket.playerNumber = room.sockets.length+1;
room.sockets.push( socket );
}
And now you have backwards and forward mapping. You don't have to worry about thread safety with the push() method because there's only a single event thread.
I am doing an experimental online poker game using NODE.JS and socket.IO. The game requires 3 players to join to start. I use socket.IO to listen to the connections from joining players. Whenever there are 3 players coming, they will form one group. Currently I use some shared variables to do this. But if there are lots of players coming in at the same time, I am fear it will cause synchronization problem. As you can see from the code snippet, the players, groups, groupNumber, comingPlayer, clients are all shared between multiple 'connection' event handlers. So when one event handler is executed and another event handler got scheduled by the V8 engine, it may corrupt the states of these shared variables.
I have done some research using Google but didn't find satisfactory answers. So I posted here to see if any expert can help me. Thanks in advance!
var clients = {}; // {"player1": 1} {"player2": 1} {"player3": 1}
var groups = {}; // {"1": ["player1", "player2", "player3"]
var groupNumber = 1; // the current group number
var comingPlayers = 0; // a temporary variable for the coming players
var players = []; // a temporary array containing the players which have not formed 1 group
socket.on('connection', function(client) {
sockets[client.id] = client;
players.push(client.id);
clients[client.id] = groupNumber;
comingPlayers++;
if (comingPlayers === 3) { // now there are 3 players which can compose 1 group
groups[groupNumber] = arrayClone(players);
gamePlay(groupNumber);
players = [];
groupNumber++;
comingPlayers = 0;
}
}
The code you've shown is atomic (it will completely finish before another event handler can run), so you shouldn't have any synchronization issues. Remember that in node, all user-written code runs in a single thread. If somebody else is trying to connect, it won't interrupt what you're doing.