NODE.JS accessing shared variable in socket.IO event handler - node.js

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.

Related

This "if else" statement don't work in my socket.io/Node.js environment

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....

Can't tell if this is a race condition

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".

Server state and users in a Meteor app, how to create state?

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.

What event order guarantees does node make?

I see in the documentation that listeners will be executed "in order" for a given event, but what other guarantees are there? For instance, is the following code guranteed to print 0 through 9 sequentially, or is that just a side effect of the current implementation?
var EventEmitter = require('events').EventEmitter
var ev = new EventEmitter();
ev.on("foo", console.log);
for (var i = 0; i < 10; i++) {
ev.emit("foo", i);
}
For instance, is the following code guaranteed to print 0 through 9 sequentially
Hmm. I don't think it's actually guaranteed in the documentation anywhere, but that's the only reasonable way an event queue can work. If events aren't delivered in the order that they were sent, it can lead to very tangled logic on the receiving end.
As pointed out in one of the comments on your question, in the all-JavaScript case, it can't work any other way, because the event is dispatched synchronously during the emit() call. For native objects, something similar applies - they need to call emit() via the V8 bindings, so ultimately those events get delivered in the order the native code sends them, as well.
Listeners will be executed in the order that they are attached.
var EventEmitter = require('events').EventEmitter
var ev = new EventEmitter();
ev.on("foo", console.log);
ev.on("foo", function(i){ console.log('...'); });
for (var i = 0; i < 10; i++) {
ev.emit('foo', i);
}
Will output:
1
...
2
...
3
...
// and so on
But change the order of registration to:
ev.on('foo', function(i) { console.log('...'); });
ev.on('foo', console.log);
And the output will be:
...
1
...
2
...
3
// and so on
As I'm sure you can tell, that has nothing to do with the fact that the original code prints the values sequentially. I'm not sure if listeners called via emit are executed on a separate thread or not but by the looks of your results, I'd guess not which is why you see the sequential output.

Assigning data to a socket

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.

Resources