Concurrent timers in node.js with socket.io - node.js

So I'm developing simple game with the following scenario:
2 users need to start a game (so I'm creating new room for 3rd user and so on)
2 users is maximum per room
When game is started, event is sent to client, after 60 seconds server needs to end the game.
Code which I wrote will work only for 1 room, but for multiple rooms, cancelling is not correct because my lastRoom variable is incorrect.
I'm new to node.js so I'm not really sure how to deal with this.
Some code:
var lastRoom = 'default'; //this is the first created room
function tryToStartGame(socket){
var clients = io.sockets.adapter.rooms[lastRoom];
console.log("Size of room "+lastRoom+" is: "+getObjectSize(clients));
//we are checking size of last room
if (getObjectSize(clients) == 2){
//players in the room should be different
games['default']= {
'leftPlayer': getFromObject(clients, 0),
'rightPlayer': getFromObject(clients, 1),
'stuff': "some data here"
'roomName': lastRoom
};
console.log("We can start the game");
//let all the people in that room
io.to(lastRoom).emit('game', games['default']);
//game to cancel
gameToCancel = lastRoom;
//client needs to be aware when game is ended
//but if we have simultaneous games this will not work
setTimeout(function(){
console.log("Cancelling game in: "+gameToCancel);
io.to(gameToCancel).emit('gameEnded', "Winner logic is todo ;) ");
}, 8000); //after 8 seconds for test
//reset the room name, so next time when this function is called in second room
//we will have something different
lastRoom = 'game'+new Date().getTime();
}
//we have less then 2 players, wait for another player
if (getObjectSize(clients)<2){
console.log("Less then 2 players");
socket.emit('waiting', 'Waiting for another user to join the game');
}
}
And tryToStartGame(socket) function is called always at connection like this:
io.on('connection', function(socket){
//when client says he wants to play
socket.on('joinGame', function(username){
//add user
addUser(socket, username);
//send list of players
io.emit('playersList', getFormatedPlayers());
//figure out in which room this player bellongs
socket.join(lastRoom);
//try to start the game
tryToStartGame(socket);
});
Problematic part is that lastRoom variable is overwritten and then setTimeout picks the wrong room, so what happens that basically last game is canceled, and the previous ones are not.
How should I correctly track and cancel the game in correct rooms ?

As you noted, lastRoom is a global variable and might/will change between the time you set it and the timeout
You can create a closure on the timeout function to keep the room as a locale variable:
var lastRoom = 'default'; //this has to be set in the same function than the call to setTimeout
//client needs to be aware when game is ended
//but if we have simultaneous games this will not work
setTimeout(function(){
var gameToCancel = lastRoom;
console.log("Cancelling game in: "+gameToCancel);
io.to(gameToCancel).emit('gameEnded', "Winner logic is todo ;) ");
}, 8000); //after 8 seconds for test

Related

Socket: When player in a room closes browser, how server can correctly emit to other sockets in that room?

Please, read to the end, help me!
I'm developing a multiroom game using SocketIO, with a client-side (React) and a server-side (Node). I'm having a problem on handling this situation:
Consider that at least two clients are inside a Room, after have joining it with socket.join(roomId) (at server-side).
Situation 1: When a player leaves the room manually (clicking a button in the page), the client-side does socket.emit("playerLeaving", someVar) to the server-side listener socket.on("playerLeaving", doStuff) that do some important things with the database (let's call it X listener). Then this listener fires back an event to the other client sockets in that room, with io.sockets.in(roomID).emit("playerLeft", someVar). No problem here.
Situation 2: Instead of manually leaving that room, that user just close the tab/browser, which fires a "disconnect" event do the server-side. Event in that case, the server must do the same procedure that he did on 1st situation, inside that X listener, so I just tried to emit this event socket.emit("playerLeaving", roomID) from inside the "disconnect" handler at server-side, so the X listener would receive it, do his stuffs, and broadcast to the other players that someone has left that room.
THE PROBLEM: the "disconnect" handler (at server-side) emits to X listener (also server-side), but this one doesn't receives it. Summin up, when a player in a room closes the tab/browser, the server isn't handling it correctly and consequently the other players in that room doesn't even get to know about that.
MY CODE:
SERVER-SIDE:
io.sockets.on('connection', (socket) => {
socket.on("playerJoiningRoom", (roomID) => {
socket.join(roomID); // to sign socket to a room
// .... some code
io.broadcast.to(roomID).emit("someoneJoined", someVar); // this works fine
})
// X-listener
socket.on("playerLeaving", (someVar) => {
// ... do stuff in database
io.sockets.in(roomID).emit("playerLeft", otherVar);
}
socket.on("disconnect", () => {
// ...some code here
// then, emit to the listener above
// example: socket.emit("playerLeaving", someVar);
});
})
}
CLIENT-SIDE
function socketEnteredRoom(socket, roomID) {
// notify server
socket.emit("playerJoiningRoom", roomID);
// listen to other players joining the room
socket.on("someoneJoined", someVar);
// listen to other players leaving the room. This listener works when some player leave manually (leave button), but don't work when a player closes tab/browser
socket.on("someoneLeft", someVar);
}
// when player wants to exit room
function handlerExitRoom() {
socket.emit("playerLeaving", someVar);
}
Waiting for your help! Thank you!
(sorry if this is a repeated question, but I couldn't find a answer that worked)
I'm not sure since I can't see your full code, but a common issue is people try to emit to rooms their socket is in in disconnect but the socket is no longer in any rooms at this point. Try changing to disconnect to disconnecting.
Try this,
//server-side
socket.on("disconnect", () => {
io.emit("playerLeaving", someVar);
});
//client-side
socket.emit("disconnect", () => {
});

how to avoid countdown timer reset on page refresh , node js / socket io

server.js
io.on('connection', function (socket) {
var addedUser = false;
socket.on('setTimer', function(data) {
timer.setEndTime(data.time);
socket.broadcast.emit('currentEndTime', {time: timer.getEndTime() });
});
});
client.js
$(function() {
var timer = new Timer(),
socket = io.connect('http://localhost:3000');
socket.on('currentEndTime', function (data) {
//this is the full date time in ms.
timer.setEndTimeFromServer(data.time);
});
set = setInterval(function(){
$('.time').trigger('click');
clearInterval(set);
},100);
$('.time').on('click', function(e) {
e.stopPropagation();
var time = $(this).text() * 1000;
timer.setEndTime(time);
timer.timeRemaining();
socket.emit('setTimer', { time: time });
});
});
Hi there, i am trying to integrate countdown timer for node js/socket io application. Timer works fine, but how do i avoid timer reset on new socket connection/page refresh. Thank You
This is because of two things:
Your socket goes away when you hit the refresh button and a new one is created and thus your connection to the server (and hence the timer value) goes away when you hit the refresh button.
When you load the page first, you don't get the current value of the timer and print it on your page to start everything.
You can have each individual timer stored in a key value storage. You can use the IP Address as key and timer value as value or something like that. This way, you can retrieve the current value of the timer for the user when user connects to the server and continue counting down.
If you use the socket ID as key, it will do you no good except to pointlessly populate a key value storage and spike up your memory usage. Use something more persistent, such as IP Address, username, e-mail or something of that sort as key and current value of timer as value.
Also, your current solution is flawed because it faces the "refresh lag". When you hit the refresh button, you stop counting for the amount of time that is spent on re-rendering of your web page. If it's nothing more than a handful of milliseconds, you're fine; but as long as it goes to a few hundreds of milliseconds, this will make actual difference. Do the count down at the server side. Just notify the client at the set intervals. This will increase the server workload, but at least you're not bound to having bad timers.
If you're going to have a single countdown, like "x days, x hours, x minutes, x seconds remaining to Superbowl or some other big event", you're much better off without socket.io. Just send the timestamp to the big event to the client and do the remainder on the client side.

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

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.

node.js + socket.IO - socket not reconnecting?

I know socket.io has a built in feature for reconnecting and everything, however I don't think that it is working - as I have seen from others it's also not working for them either.
If a user puts their computer to sleep, it disconnects them, and then when they open it back up they are no longer connected so they don't any of the notifications or anything until they refresh the page. Perhaps it's just something that I'm not doing correctly?
var io = require('socket.io').listen(8080);
var users = {};
////////////////USER CONNECTED/////
console.log("Sever is now running");
io.sockets.on('connection', function (socket) {
//Tell the client that they are connected
socket.emit('connected');
//Once the users session is recieved
socket.on('session', function (session) {
//Add users to users variable
users[socket.id] = {userID:session, socketID:socket};
//When user disconnects
socket.on('disconnect', function () {
//socket.socket.connect();
var count= 0;
for(var key in users){
if(users[key].userID==session)++count;
if(count== 2) break;
}
if(count== 1){
socket.broadcast.emit('disconnect', { data : session});
}
//Remove users session id from users variable
delete users[socket.id];
});
socket.on('error', function (err) {
//socket.socket.connect();
});
socket.emit("connection") needs to be called when the user reconnects, or at least the events that happen in that event need to be called.
Also socket.socket.connect(); doesn't work, it returns with an error and it shuts the socket server down with an error of "connect doesn't exist".
The problem is related to io.connect params.
Look at this client code (it will try to reconnect forever, with max delay between attempts 3sec):
ioParams = {'reconnection limit': 3000, 'max reconnection attempts': Number.MAX_VALUE, 'connect timeout':7000}
socketAddress = null
socket = io.connect(socketAddress, ioParams)
There are two important parameters out there, related to your problem:
reconnection limit - limit the upper time of delay between reconnect attemts. Normally it's getting bigger and bigger in time of server outage
max reconnection attempts - how many times you want to try. Default is 10. In most cases this is the problem why the client stops trying.

Resources