I am making a simple Node.js game that uses Express, Socket.io, and an Http server. All of the users are stored in a multidimensional object on the server. This is how the server-side code works:
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.use(express.static(__dirname + '/'));
var playerList = {};
createPlayer = function(array,width,height,spdx,spdy,x,y,color,name,id) {
var player = {
width:width,
height:height,
spdx:spdx,
spdy:spdy,
x:x,
y:y,
wKeyDown:false,
aKeyDown:false,
sKeyDown:false,
dKeyDown:false,
color:color,
name:name,
id:id
}
array[id] = player;
}
io.on('connection', function(socket) {
socket.on('new player', function(id, name) {
id = parseInt(id);
if (!playerList[id]) {
createPlayer(playerList,25,25,4,4,Math.round(Math.random() * 800),Math.round(Math.random() * 600),randomColor(),name,id);
}
socket.on('pressW', function(id, keyDown) {
playerList[id].wKeyDown = keyDown;
});
socket.on('pressA', function(id, keyDown) {
playerList[id].aKeyDown = keyDown;
});
socket.on('pressS', function(id, keyDown) {
playerList[id].sKeyDown = keyDown;
});
socket.on('pressD', function(id, keyDown) {
playerList[id].dKeyDown = keyDown;
});
});
socket.on('disconnect', function() {
});
};
sendPlayerList = function() {
//newPlayerList is used to prevent client from seeing other users IDs
var newPlayerList = {};
var count = 0;
for (var q in playerList) {
player = {
x:playerList[q].x,
y:playerList[q].y,
width:playerList[q].width,
height:playerList[q].height,
color:playerList[q].color,
name:playerList[q].name,
}
newPlayerList[count] = player;
count++;
}
io.emit('edit playerlist', newPlayerList);
}
SPLInterval = setInterval(sendPlayerList, 1000);
Here is the client-side code for connection:
var id;
$('#playbutton').click(function() {
var name = document.getElementById('name').value;
id = Math.floor(Date.now() * Math.random());
socket.emit('new player', id, name);
});
On the client-side, in the update loop, when the game wants to tell the server your input, it emits your input like so:
update = function() {
ctx.clearRect(0,0,canvas.width,canvas.height);
if (document.hasFocus()) {
socket.emit('pressD', id, dKeyDown);
socket.emit('pressS', id, sKeyDown);
socket.emit('pressA', id, aKeyDown);
socket.emit('pressW', id, wKeyDown);
}else{
socket.emit('pressD', id, false);
socket.emit('pressS', id, false);
socket.emit('pressA', id, false);
socket.emit('pressW', id, false);
}
clientUpdatePlayer();
updatePlayers();
}
}
var updateInterval = setInterval(update, 31.25);
The function to update players just draws players based on the player list sent from the server.
My problem is that when a user disconnects, they stay in the player list.
I don't understand how I should go about fixing this. I identify users by getting the ID they send from the client, but I can't get the user's id when they disconnect.
There is a lot more code, but I tried to only include the code that I thought was necessary. I am willing to include more code if that is needed.
You could just store the id value in the parent scope, which the disconnect event handler would have access to:
io.on('connection', function(socket) {
var userId;
socket.on('new player', function(id, name) {
userId = id = parseInt(id);
// ...
});
socket.on('disconnect', function() {
delete playerList[userId];
});
};
Maybe I'm late to the party but I was stuck with something similar and found it the hard way and this may help someone.
The best way to detect if the user is disconnected is would be to first set the username in socket session.
Send the name from the client on emit
socket.emit("newUser", username);
and on server
socket.on('newUser',function (username) {
// we store the username in the socket session for this client
socket.username = username;
});
and when the user disconnects find that on the disconnect event
socket.on('disconnect', function () {
var connectionMessage = socket.username + " Disconnected from Socket " + socket.id;
console.log(connectionMessage);
});
and you can take it from there.
This worked for me:
On every new connection or user who comes online generate a socket Id, add it to the user object, and add it to the array of all the users online.
const users = [];
io.on('connection', (socket) => {
const socketId = socket.id;
socket.on('user online', (data) => {
users.push({ ...data, socketId });
io.emit('view user online', user);
});
Then in the disconnect, use forEach to loop through each object in the array, then use for to loop through and delete each key in the object:
socket.on('disconnect', () => {
users.forEach((user) => {
if (user.socketId === socket.id) {
for (const key in user) {
delete user[key];
}
}
});
logger(`A user has disconnected`);
});
});
});
Tweak to the way you want.
var users = [];
socket.on('newUser', (username) => {
users.push({
id: socket.id,
username: username
});
});
socket.on('disconnect', () => {
const presentUser = users.find(user => user.id == socket.id);
users = users.filter(user => user != presentUser);
});
We can use socket id for storing data as a refrence in playerList. whenever user will disconnect you can delete element from object according to socket id
var playerList = {};
io.on("connection", socket => {
if (!Object.values(playerList).includes(playername) && playername != null) {
var U_data = {
[socket.id]: playername
};
playerList = { ...playerList, ...U_data };
}
socket.on("disconnect", function(e, id) {
console.log(socket.id);
delete playerList[socket.id];
io.emit("broadcast", Object.values(playerList));
});
}
Related
I have a vue.js/node/socket.io chat application. I need to create a list of online users that will stay up to date when users connect or disconnect. I'm trying using this code, but I'm not able to update the users list when someone join the room. How I can fix this?
'use strict';
const express = require('express');
const socketIO = require('socket.io');
const PORT = process.env.PORT || 3000;
const INDEX = '/index.html';
// TODO: controllo query url
const server = express()
.use('/modules', express.static(__dirname + '/node_modules'))
.use('/assets', express.static(__dirname + '/assets'))
.use((req, res) => res.sendFile(INDEX, { root: __dirname }))
.listen(PORT, () => console.log(`Listening on ${PORT}`));
const io = socketIO(server);
let connectedUsers = [];
io.on('connection', (socket) => {
console.log('Connected peer: '+ socket.id);
socket.on('accessRoom', (username,room) => {
connectedUsers = { nickname: username, id: socket.id }
socket.join(room, (err) => {
socket.emit('accessStatus', 'done');
console.log(this)
})
updateUsersList()
})
socket.emit('channelStatus', socketData)
socket.emit('message', socket.id, 'connected')
socket.on('message', (id, msg) => {
socket.broadcast.emit('message', id, msg);
})
socket.on('disconnect', () => {
console.log('Disconnected')
})
function updateUsersList(){
socket.emit('usersList', connectedUsers)
}
});
io.on('ping', (socket) => {
console.log(socket);
io.emit('pong');
});
vuejs code
export default {
data () {
return {
isRegistered: false,
isConnected: false,
user: '',
message: '',
id: '',
channel: '',
usersList: []
}
},
mounted() {
this.pingServer()
this.updateUsersList()
io.on('pong', (data) => {
console.log(data);
})
io.on('channelStatus', (data) => {
if( data.status === 'connected' ){
this.id = data.IHMnumber;
this.isConnected = true;
console.log(data);
}
})
io.on('message', (from, msg) => {
console.log(from)
console.log(msg)
})
},
methods: {
pingServer(){
io.emit('ping')
},
connect(){
if( this.isRegistered === false){
this.user = this.user;
this.isRegistered = true;
console.log(this.isRegistered);
console.log(this.user);
io.open()
return this.user;
}
},
updateUsersList(){
io.on('usersList', (users) => {
console.log(users);
this.usersList.push(users);
})
},
sendMessage(){
console.log(this.message)
io.emit('message', this.id ,this.message)
}
}
}
Whit this code, the user that is creating the room will not recive the users list. For the users that will join the room instead, in the console I'm able to see that they are added to the array of users, but if someone joined the room, the event will not be logged in console with the updated info. Maybe I need to refactor the code, maybe I'm calling the updateUsersList in the wrong place?
First of all, if you divide users by rooms, send messages to their rooms https://socket.io/docs/server-api/#socket-to-room . Here I show you one of ways to solve your problem. Change type of 'connectedUsers', for example, to map, where keys will be room-name, value - array of users:
const connectedUsers = new Map();
To handle changes of user lists create method for join
joinToRoom(room, user) {
// create users array, if key not exists
if (!connectedUsers.has(room)) {
connectedUsers.set(room, []);
}
// add user to room array
connectedUsers.get(room).push(user);
// call update function
updateUsersList(room);
}
and leave room:
leaveRoom(room, user) {
let userList = connectedUsers.get(room);
// delete user
userList = userList.filter(u => u !== user);
// update user list
if (!userList.length) {
// delete key if no more users in room
connectedUsers.delete(room);
} else
{
connectedUsers.set(room, userList);
// call update function
updateUsersList(room);
}
}
ALso change your method updateUsersList:
function updateUsersList(room){
socket.to(room).emit('usersList', {
room: room,
users: connectedUsers.get(room)
});
}
Pay attention, that we emit to event 'usersList' an object with room field for clarify, what room you should update at frontend.
The last step is call our methods:
when user joins room:
socket.join(room, (err) => {
const user = resolve user from request;
socket.emit('accessStatus', 'done');
joinToRoom(room, user);
console.log(this);
})
when user leave room:
i don`t see the event for leave room, so you just call there leaveRoom(room, user)
Also you should create some map to compare socketId and user, to correctly handle disconnect event and call leaveRoom(room, user) method.
I am having some issues using socket.io is modules. I have changed the way I do it quite drastically, however everything seems to be working, except being able to send userdata back to my socket connection:
Here is my io.js file: /config/io
/*jshint esversion: 6*/
var io = require('socket.io')();
const moment = require('moment');
// Socket stuff
io.on('connection', function (socket) {
socket.on('login', function (userdata) {
socket.handshake.session.userdata = userdata;
socket.handshake.session.save();
console.log(socket.handshake.session.userdata);
});
// Server Time
var interval = setInterval(function () {
var momentNow = moment();
var data = momentNow.format('LT');
socket.emit('time', data);
}, 60000);
// Chat - Needs work
socket.on('chat', function (msg) {
console.log(msg);
var username = 'Message'; //socket.handshake.session.userdata.username;
var message = '[' + moment().format('LT') + '] ' + username + ': ' + msg;
io.emit('message', message, username);
});
socket.on('disconnect', function () {
if (socket.handshake.session.userdata) {
delete socket.handshake.session.userdata;
socket.handshake.session.save();
}
console.log('user disconnected');
});
});
module.exports = io;
Here is where I'm trying to emit the data /config/passport: (please note that userdata does indeed contain the right information!)
/*jshint esversion: 6 */
const LocalStrategy = require('passport-local').Strategy;
const db = require('../config/db');
const bcrypt = require('bcryptjs');
var io = require('./io');
module.exports = function(passport) {
// Local Strategy login
passport.use(new LocalStrategy(function(username, password, done) {
// Match Username
let sql = 'SELECT * FROM users WHERE username = ?';
db.query(sql, [username], function(err, rows) {
if (err)
return done(err);
if (!rows.length) {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
// Match Password
bcrypt.compare(password, rows[0].password, function(err, isMatch) {
if (err)
return done(err);
if (isMatch) {
var userdata = rows[0];
io.emit('login', userdata); // HERE IS WHERE I TRY TO EMIT IT
// console.log(rows[0]);
return done(null, rows[0]);
} else {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
});
});
}));
Now here is my main app file: (leaving out a bunch of stuff)
var io = require('./config/io');
// Init App
const app = express();
// Init http server
const server = http.createServer(app);
// Attach IO
io.attach(server);
// Listen
server.listen(8080, function () {
console.log('Server listening on port 8080...');
});
Now, everything seems to be working fine, except being able to emit the data. Now I tried logging it client side as well (just in case it was emitting on client-side and not server-side) but it is not doing that as well.
Okay, so here is an actual working answer. It s a work-around, and I completely abandoned trying to do it from the passport login handler itself. But here is how I did it:
IO code:
var session = socket.handshake.session;
socket.on('login', function () {
if (socket.handshake.session.passport === undefined) {
var destination = '/';
socket.emit('not logged', destination);
} else {
console.log('user logged in');
var userId = session.passport.user;
var sql = 'SELECT * FROM users WHERE id = ?';
var query = db.query(sql, userId, function (err, rows) {
session.userdata = rows[0];
session.save();
var dataObj = session.userdata;
socket.emit('sart up', dataObj);
});
}
});
And jQuery:
// Connection Successful
socket.on('connect', function () {
connected = true;
socket.emit('login');
});
socket.on('disconnect', function () {
connected = false;
});
socket.on('not logged', function (destination) {
window.location.href = destination;
});
socket.on('start up', function (dataObj) {
});
I'm not a huge fan of having to do it this way, I would have liked to handle everything sever-sided, but for now this is working, and will use until I figure out how to do it the way I'd like to.
let app = require('express')(),
http = require('http').Server(app),
User = require('../models/users');
http.listen(8080, function () {
console.log('Socket IO server has been started on port 8080');
});
let io = require('socket.io')(http);
var socketRoom = {};
io.of('/chat').on('connection', function (socket) {
var ioRooms = io.of('/chat').adapter.rooms;
console.log(ioRooms) // I got length 1 when I print it.
socket.on('requestRandomChat', function (data) {
User.findOne({userToken : data.userToken}, function(err, user){
if(err) socket.emit('db error');
if(!user) socket.emit('db error : user not found');
else {
socket.emit('responseUserProfile', user);
for (var key in rooms){
if (key == ''){
continue;
}
if (rooms[key].length == 1){
var roomKey = key.replace('/', '');
socket.join(roomKey);
io.sockets.in(roomKey).emit('completeMatch', {});
socketRoom[socket.id] = roomKey;
return;
}
}
socket.join(user.userToken);
socketRoom[user.userToken] = socket.userToken;
}
});
});
socket.on('cancelRequest', function (data) {
socket.leave(socketRoom[socket.id]);
console.log(ioRooms);
});
socket.on('sendMessage', function (data) {
console.log(ioRooms);
console.log('sendMessage!');
io.of('/chat').in(socketRoom[socket.id]).emit('receiveMessage', data);
});
socket.on('disconnect', function (data) {
console.log(ioRooms);
var key = socketRoom[socket.id];
socket.leave(key);
io.of('/chat').in(key).emit('disconnect');
var clients = io.of('/chat').clients(key);
for (var i = 0; i < clients.length; i++) {
clients[i].leave(key);
}
});
});
when I trying to get a length of rooms, I get a length 1 with a socketId.
but I've never connected to the room. Is that default room? can somebody explain what's happening with the room socket?
//{ '/chat#2HAMaavywIwZ1B5fAAAD': Room { sockets: {
'/chat#2HAMaavywIwZ1B5fAAAD': true }, length: 1 } }
In socket io each socket is automatically connected to a default room identified by socket ID.
That's why you get length 1.
I hope you find it helpful.
http://socket.io/docs/rooms-and-namespaces/
Now I'm using express4 and socket.io 0.9 to build online chat room.The problem is I don't want same user join the same room when they open another tab in browser(or other browser).I can get the user session(to visit those room,user must logging in,when they logged,the 'uid' will set into the session).
With the help of #Jujuleder I figured out how to solve my issue.
Server:
var socketio = require('socket.io'),
cookieParser = require('cookie-parser')('YOUR-SERECT-KEY'),
session = require('express-session'),
RedisStore = require('connect-redis')(session),
redis = require("redis"),
client = redis.createClient(6379, "127.0.0.1"),
store = new RedisStore;
module.exports.listen = function(app){
var io = socketio.listen(app);
var pubRoom = new PublicRoom(io);
pubRoom.listen();
return io
};
function PublicRoom(io){
this._baseUrl = '/room/public/';
this.publicRoom = io.of(this._baseUrl);
}
PublicRoom.prototype.listen = function(){
this.publicRoom.authorization(function(handshakeData, callback){
function cookieParserWrapper (handshakeData, next) {
cookieParser(handshakeData,{}, next);
}
cookieParserWrapper(handshakeData,function(){});
var sid = handshakeData.signedCookies.sid;
//get the UID from redis
store.get(sid,function(err,data){
if(err){
console.log(err);
}else{
handshakeData.uid = data.uid;//set UID into handshake
callback(null, true);
}
});
});
this.publicRoom.on('connection', function(socket){
socket.on('authReq',function(room,user){
socket.myID = user;
var uid = socket.handshake.uid; // We can get the UID here.
var onlineUsers = self.publicRoom.clients(room);//get users in this room
if(onlineUsers.length === 0){
socket.to(room).emit('authRes',true);// Tell the client you can Join
socket.auth = true;
}else{
for(var i= 0,l=onlineUsers.length;i<l;i++){
if(onlineUsers[i].myID===uid){
socket.auth = false;
socket.to(room).emit('authRes',false);
// Tell the client you can't Join
}else{
socket.auth = true;
socket.to(room).emit('authRes',true);// Tell the client you can Join
}
}
}
});
});
}
client:
socket.on('connect', function() {
socket.emit('authReq',roomID,userID);
});
What is clearly your problem ? You have trouble accessing the current users in a room ? If this is that, you can get the sockets from a room like this and then check for your user.
First add a parameter to your first socket to be able to identify later.
socket.sessionid=your_user_session_id;
Then, for every connection, check in the sockets of the room if you have already a socket with this session ID
var socketsOfTheRoom = io.sockets.clients(your_room_name);
for(var i=0;i<socketsOfTheRoom.length;i++){
sock = socketsOfTheRoom[i]
if(sock===sessionid_of_new_connection){
return true
}
}
return false
//SERVER.JS
socket.on('authReq',function(room,user, callback){
if (io.nsps['/'].adapter.rooms[room])
{
var onlineUsers = io.sockets.adapter.rooms[room].sockets;
var totalUsers = io.sockets.adapter.rooms[room];
if(totalUsers.length === 0){
callback(true);
}
else
{
var userinroom = [];
for (var id in onlineUsers) {
if (io.of('/').adapter.nsp.connected[id].username==user)
userinroom.push(user);
}
if (userinroom.length==0) callback(true);
else callback(false);
}
}
else
callback(true);
});
// CLIENT.JS
socket.emit('authReq',roomname,username, function(callback) {
if(callback)
if (username) socket.emit('adduser', username, rooname);
});
I am using webrtc to make a audio, video and chat application where I need keep all the users in a user list in the serverside. Need help how to get this done.
Also, how can I remove users from the list when they logout from the system.
Need help to implement this.
webRTC.rtc.on('connect', function(rtc) {
//Client connected
});
webRTC.rtc.on('send answer', function(rtc) {
//answer sent
});
webRTC.rtc.on('disconnect', function(rtc) {
//Client disconnect
//console.log(webRTC);
});
webRTC.rtc.on('chat_msg', function(data, socket) {
var roomList = webRTC.rtc.rooms[data.room] || [];
for (var i = 0; i < roomList.length; i++) {
var socketId = roomList[i];
if (socketId !== socket.id) {
var soc = webRTC.rtc.getSocket(socketId);
if (soc) {
soc.send(JSON.stringify({
"eventName": "receive_chat_msg",
"data": {
"messages": data.messages,
"id": data.id,
"from": data.from,
"status": data.status,
"email": data.email
}
}), function(error) {
if (error) {
console.log(error);
}
});
}
}
}
});
As I was using webrtc.io module, so below are the methods that helped me to create the userlist and maintain the presence.
webRTC.rtc.on('join_room', function(data, socket) {
// Will get info who joined along with his socket id
}
And
webRTC.rtc.on('room_leave', function(room, socketid) {
// Will get info who left the room
}
Node.js code:
var users = {};
io.sockets.on('connection', function (socket) {
socket.emit('connect', true);
socket.on('message', function (data) {
socket.broadcast.emit('message', data);
});
socket.on('new-user', function (username) {
users[username] = username;
});
socket.on('check-presence', function (username) {
var isUserPresent = !! users[username];
socket.emit('presence', isUserPresent);
});
socket.on('remove-user', function (username) {
var user = users[username];
if (user) delete users[username];
});
});
This may also work (node.js):
var users = {};
io.sockets.on('connection', function (socket) {
var UserName;
socket.emit('connect', true);
socket.on('message', function (data) {
socket.broadcast.emit('message', data);
});
socket.on('new-user', function (username) {
users[username] = username;
UserName = username;
});
socket.on('check-presence', function (username) {
var isUserPresent = !! users[username];
socket.emit('presence', isUserPresent);
});
// removing user on "disconnect"
socket.on('disconnect', function () {
var user = users[UserName];
if (user) delete users[UserName];
});
});
For 1st case; client-side code:
var socket = io.connect();
socket.on('connect', function () {
socket.emit('new-user', 'username');
});
function removeUser() {
socket.emit('remove-user', 'username');
}
window.onbeforeunload = function () {
removeUser();
};
// if someone pressed "F5" key to refresh the page
window.onkeyup = function (e) {
if (e.keyCode == 116)
removeUser();
};
// if someone leaves via <a href>
var anchors = document.querySelectorAll('a'),
length = anchors.length;
for (var i = 0; i < length; i++) {
var a = anchors[i];
if (a.href.indexOf('#') !== 0 && a.getAttribute('target') != '_blank')
a.onclick = function () {
removeUser();
};
}
For 2nd case; client side code:
var socket = io.connect();
socket.on('connect', function () {
socket.emit('new-user', 'username');
});
You can check presence too:
socket.on('presence', isUserPresent) {
// boolean: user is present or not
});
socket.emit('check-presence', 'username');