Trouble understanding sockets.io room - javascript

I am trying to configure a 1v1 game with multiple rooms on my server with sockets.io. When the user create a room, a random alphanumeric identifier of 5 letters is generated and the user joins the room.
function handleNewGame() {
let roomName = makeid(5);
client.join(roomName);
}
Then another user is supposed to join with the room name. The room name is taken from a tag inside the html main page and then is sent by a function.
This is the function that handles game joining on server side
function handleJoinGame(roomName) {
const room = io.sockets.adapter.rooms[roomName];
}
All of this is wrapped inside an
io.on('connection', client => {
}
The problem is that at this point, before the other user even tries to join, the room is undefined.
console.log(room);
This gives me undefined.
If I try to actually print out all the rooms, it actually exist, but the method can't find it. What did I do wrong?

Forget the library for a moment. Conceptually, a room is just a group of users. an array of socket connections to whom you broadcast using a for loop. It should come with a room manager since you want to have many rooms otherwise you had single room anyway.
Let's assume an object of arrays.
var roomManager = {
"room12345" : [socket1, socket2, socket5],
"room76432" : [socket3, socket4],
}
and when socket1 wants to broadcast, you know its room room12345 so you broadcast to all others in that room.
So really you can implement this logic yourself quite easily.

Actually, the io.sockets.adapter.rooms returns a Map. Said so, I had to get the room with a key, using
const room = io.sockets.adapter.rooms.get(roomName);
I've solved the issue.

Related

Ideal way to handle multiple socket rooms?

So I am creating a chat application and I want to handle multiple chat rooms. Now I watched some tutorials and came up with a way.
const io = require("socket.io")(http);
io.on("connection", (socket) => {
socket.on("joinRoom", (roomid) => {
//Joining the room
socket.join(roomid)
//Broadcasting all previous messages
io.to(roomid).emit("messages",allPreviousMessages)
})
socket.on("chatMessage", (data) => {
//Saving msg to dB then broadcasting
io.to(roomid).emit("message",receivedMessage)
})
socket.on("disconnect",(data) => {
//updating user's lastSeen info in dB
})
})
So on my frontend when user clicks on a chatroom we call the "joinRoom" event and connect to the room and on clicking another chatroom make the same process of joining room.
Is this an ideal way for handling multiple chatrooms? If not so please let me know a better solution.
I think the best way to implement private rooms or channels or chats is this way. I have implemented an example for these three sections. Link
User token and api must be authenticated before connecting to socket.io. If this part is ok it will connect to socket.io otherwise it can't cause you to see the event that is there. Something happens by calling each of them. For example, by calling this onNotificationForVoiceCall event, the received data is first checked, then it is checked whether this user is present in the list of online users or not, and the state of the next step is checked. Whether or not this room has already been created in the database, the response of all these operations is returned to the user by socket.emit,
And I fixed some bug in project.

Socket.io - tell client if he creates or joins a room

I currently use the following code to allow users to create/join a specific room in socket.io:
socket.on('create', function (roomId) {
...
socket.join(roomId);
...
});
since the join function is used for both creating AND joining the same room, I would like tell the client if he was the one that created the room or just joined an existing one.
WHAT I TRIED TO DO
I tried to store the Id (and other informations not related to the problem), then check whenever that id already exists and emit a different message depending on the results. something like:
// "repo" is a variable declared globally
socket.on('create', function (roomId) {
...
socket.join(roomId);
if (repo.hasRoom(roomId)) {
app.io.sockets.in(roomId).emit('someone Joined');
} else {
app.io.sockets.in(roomId).emit('someone Created this room');
repo.saveRoom(roomId);
}
...
});
the above code doesn't work, as the client is unable to receive the message emitted (I guess because is on the "create" event). to be clear, I CAN determine on the server if a room is created, but the CLIENT doesn't know.
Is there an effective way to notify the client if he was responsible for creating or just joining a room?

Pusher one to one chat structure

I'm a bit confused on the presence-channels in Pusher's platform, as I'm building a chat application from scratch. Now, I know some of you guys have seen tons of "realtime chat app" topics around, but, I'm looking for a peer-to-peer chat and not the site-wide global thingy. More like a facebook chat, where you can go one-to-one.
Now, I've seen an example in PubNub's demos (named Babel) but, that thing is far from what I'm looking for because I've checked the requests in the console and even if it's not shown, the sent messages between other users are shown in my network request logs too because it's being filtered in JS and not server-side and thats not something I want for sure.
So, coming back to the subject,
I'm aware of the channel / private-channel / presence channel functionality, and I decided to do this:
When opening the app, every user subcribes to his private-user_id channel ( creates, if it doesn't exist already ).
At the same time ( while opening the app ) user1 subscribes to a presence-global channel where others keep track if friends are online.
When others want to send him a message, e.g. user2 to user1, he subscribes to private-1 thereafter javascript will process the events.
Now, I know something's wrong with this because.. if user3 would send a message to user1 he'd subscribe to private-user1 so I guess he'll see the events that user2 is triggering when sending messages to user1 too, right ? Or did I get this wrong ?
I've read in their docs that presence channel is actually a private channel extension, so I'm thinking now.. why using private channels anymore, and then, how can I notify all my friends I'm online.
But then, something else comes up in their docs , telling me that channels provide two important things (among others), from which, first is a way of filtering data and second is a way of controlling access.
How am I supposed to "filter data" since there's no link in their docs, or better, what do you have in mind for a one-to-one chat. I'm sorry if I got all their docs wrong, I had a look on their sample applications but none of them are using the one-to-one technique which I'm looking for.
I am new to Pusher and socket connections etc, but I've learned how to authenticate, how to create , detect and process the events in the channel, and I can create a simple global chat with online members, but, when it comes to private-channels I'm quite confused on how to create separate channels for two users.
Thanks in advance !
The purpose of private channels is to restrict who can subscribe to that channel. So, you can either:
Use it to ensure only a users friends can subscribe to updates
Use it for notifications only for that user
In one-to-one chat I'd suggest you choose the latter (No.2).
With this in mind I'd set out achieving one-to-one chat as follows:
The Forum
When users join the chat application they all subscribe to two channels:
private-notifications-<user_id> where user_id is their unique user ID e.g. leggetter in my case. This channel is utilised for user-specific notifications.
presence-forum for all users in that forum. The question called this presence-global.
This is achieved as follows:
var notifications = pusher.subscribe( 'private-notifications-user_one' );
var forum = pusher.subscribe( 'presence-forum' );
Upon subscription to each channel the channel authentication process will take place.
Within the forum you could have a general public chat on the presence-forum/presence-global presence channel by sending and receiving messages.
Starting one-to-one chat
When one user (user_one) wants to have a private chat with another user (user_two) you obviously need something in the UI to trigger this. Say user_one clicks on something next to user_two that indicates they want a one-to-one chat. When this happens a request should be made to the server (the authority) to indicate that user_one wants to initiate the private chat with user_two†.
Note: † if you chose a channel naming convention for one-to-one chat the private channel authentication could actually be used as the private one-to-one chat initiation
When the server receives this request it can generate a unique private channel name for this one-to-one chat. A really simple way of doing this is by concatenating the user IDs e.g. private-chat-<initiating_user>-<receiving_user> (there are other considerations e.g. maybe you want to ensure the channel name is always the same between the two users). In our simple scenario the channel name would be private-chat-user_one-user_two.
The server can then trigger a one-to-one-chat-request event on the private notification channel for each user delivering the one-to-one private chat channel name in the payload.
// Trigger event on both user channels with one call
var channels = [ 'private-notifications-user_one', 'private-notifications-user_two' ];
// Additional event data could also be sent
// e.g. more info on the initiating user
var eventData = {
'channel_name': 'private-chat-user_one-user_two',
'initiated_by': 'user_one'
'chat_with' : 'user_two'
};
pusher.trigger( channels, 'one-to-one-chat-request', eventData );
When user_one receives the one-to-one-chat-request they will subscribe to the eventData.channel_name channel and the auth process will take place for that channel.
// A lookup of private chats
// where the key is the user ID of the current user is chatting with
var privateChats = {};
notifications.bind( 'one-to-one-chat-request', function( data ) {
// MY_USER_ID would need to be stored somewhere
// and in this case the value would be 'user_one'.
// expectingChatWith should make sure user_one is waiting for
// a private chat response with the given user
if( data.initiated_by === MY_USER_ID &&
expectingChatWith( data.chat_with ) ) {
startPrivateChat( data.chat_with, data.channel_name );
}
} );
function startPrivateChat( withUserId, channelName ) {
privateChats[ withUserId ] = pusher.subscribe( channelName );
}
When user_two receives the one-to-one-chat-request the user will need to be notified about the request and either accept or decline it. If the user accepts then the client-side code simply subscribes to the channel. If the user declines then a request should be sent to the server and an event triggered on private-notifications-user_one telling them their one-to-one chat request was declined. This will allow user_one to unsubscribe from the private chat channel.
var privateChats = {};
notifications.bind( 'one-to-one-chat-request', function( data ) {
if( ... ) { ... }
// has somebody request to chat with this user?
else if( data.chatWith === MY_USER_ID ) {
// Prompt the user
// Note: more user info required
displayChatPrompt( data );
}
} );
// callback when the user accepts the chat request
function accepted( chatUserId, channelName ) {
startPrivateChat( chatUserId, channelName );
}
// the user doesn't want to chat
function declined( chatUserId ) {
// send info to the server indicating declined request
}
Private one-to-one chat success
With both user_one and user_two subscribed to private-chat-user_one-user_two they can trigger events on the channel and participate in their private one-to-one chat.

Checking whether user subscribed to certain channel in socket.io

Is it possible to check whether a user has subscribed to a certain channel in socket.io?
Let´s say I have a channel called news. User subscribed to that channel on client-side. But the site´s data is dynamic, therefore the news-tab of the site might not be open at any time. I do not want to create the content for the news-tab if the news tab is not open on the client-side. I know that the news-tab is not open, when the user has not subscribed to the news channel.
Is there a way to check that?
You can use io.sockets.clients('news') and this will return the socket id's of all clients. If you know the client socket.id, you could also call io.sockets.manager.roomClients[socket.id] (room name will have '/' leading character)
This is a sample I use for my admin clients to call to get client counts per room:
socket.on('countClientsInRoom', function (room, callback) {
var count = io.sockets.clients(room).length;
callback(count);
});

Socket.io pair rooms

Im using the opentok and socket.io packages to try and create 2 "groups". I have been able to pair up non grouped users with a 1to1 relationship just fine. What Im trying to do is have 2 groups of users. Lets say one group is a help desk and the other group is a customer. I want all the customers to be grouped together but not connect to one another. I would also like for the same behavior with the help desk group. I then want any 1to1 pair to group together ie. helpdeskTOcustomer. I would provide some code, but from a logical standpoint im not even sure how to begin coding this and the only thing I would be able to provide is just the slightly modified code from here.. http://www.tokbox.com/developersblog/code-snippets/opentok-sessions-using-node-js/
It's not really clear from your question exactly what you're trying to do (e.g. what do you mean by "pair" or "group together"), but you may find some use of Socket.IO's rooms.
Sometimes you want to put a bunch of sockets in one room, and send a message to them [all at once]. You can leverage rooms by calling join on a socket, and then [send data or emit events] with the flags to and in:
io.sockets.on('connection', function (socket) {
socket.join('a room');
socket.broadcast.to('a room').send('im here');
io.sockets.in('some other room').emit('hi');
});
[Edit]
Okay, after seeing your comment and looking over the OpenTok docs a bit (I wasn't familiar with it, it seems pretty neat), it looks like you just want a queue for each type of user, right? Here's a bit of code (more like pseudocode, as I'm not intimately familiar with your app or the OpenTok API):
var customers_waiting = [];
var employees_waiting = [];
io.sockets.on("connection", function(socket) {
// Determining whether a connecting socket is a customer
// or an employee will be a function of your specific application.
// Determining this in this callback may not work depending on your needs!
if(/* client is a customer*/) {
customers_waiting.push(socket); // put the customer in the queue
else if(/* client is an employee */) {
employees_waiting.push(socket); // put the employee in the queue
}
try_to_make_pair();
});
function try_to_make_pair() {
if(customers_waiting.length > 0 && employees_waiting.length > 0) {
// If we have people in both queues, remove the customer and employee
// from the front of the queues and put them in a session together.
customer = customers_waiting.shift();
employee = employees_waiting.shift();
opentok.createSession('localhost', {}, function(session) {
enterSession(session, customer);
enterSession(session, employee);
}
}
}

Categories