Socket.IO display connected users list of a room - node.js

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.

Related

wrong behavior of messages socket.io

I use socket.io and socket.io-client to create chat user - operator. I've made connections, all works good, but message sending has weird behavior.
Like I send message from first client to room in socket and don't see it, but other client can
When I send second message, the first sent msg just changes for the second one
I see this all messages in my Map on backend(I don't use DB yet) but can't see it on client side.
server
app.get("/rooms/:id", (req, res) => {
const roomId = req.params.id;
const obj = rooms.has(roomId)
? { messages: [...rooms.get(roomId).get("messages").values()] }
: { messages: [] };
res.send(obj);
});
app.post("/rooms", (req, res) => {
const { roomId, name } = req.body;
if (!rooms.has(roomId)) {
rooms.set(
roomId,
new Map([
["users", new Map()],
["messages", []],
])
);
}
res.json([...rooms.values()]);
});
io.on("connection", (socket) => {
socket.on("ROOM:JOIN", ({ roomId, name }) => {
socket.join(roomId);
rooms.get(roomId).get("users").set(socket.id, name);
const users = rooms && [...rooms.get(roomId).get("users").values()];
socket.to(roomId).emit("ROOM:JOINED", users);
});
socket.on("ROOM:NEW_MESSAGE", ({ roomId, name, text }) => {
const obj = { name, text };
console.log(rooms.get(roomId).get("messages"));
rooms.get(roomId).get("messages").push(obj);
socket.to(roomId).emit("ROOM:NEW_MESSAGE", obj);
});
socket.on("disconnect", () => {
rooms.forEach((value, roomId) => {
if (value.get("users").delete(socket.id)) {
const users = [...value.get("users").values()];
socket.to(roomId).emit("ROOM:JOINED", users);
}
});
});
});
client side
const [messageValue, setMessageValue] = useState<string>("");
const [freshData, setFreshData] = useState<Message[]>([]);
console.log(freshData);
const onSendMessage = () => {
socket.emit("ROOM:NEW_MESSAGE", {
roomId: socketData.roomId,
text: messageValue,
name: socketData.name,
});
setMessageValue("");
};
const getFreshData = async () => {
const { data } = await axios.get(
`http://localhost:4444/rooms/${socketData.roomId}`
);
setFreshData(data.messages);
};
useEffect(() => {
if (join) {
socket.emit("ROOM:JOIN", socketData);
getFreshData();
socket.on("ROOM:JOINED", (users) => {
console.log("new user", users);
});
}
}, [join]);
useEffect(() => {
socket.on("ROOM:NEW_MESSAGE", (message) =>
setFreshData([...freshData, message])
);
socket.on("ROOM:JOINED", (users) => {
console.log("new user", users);
});
}, []);
and just map through all messages, that I should get from server
Where I could make a mistake ? Thank you
So I had a mistake on client, I need to call socket function in other useEffect with dependency onSendMessage like this:
useEffect(() => {
socket.on("ROOM:NEW_MESSAGE", (message) =>
setFreshData([...freshData, message])
);
}, [onSendMessage]);
and to show your sent messages I need to make onSendMessage update state with messages like this:
const onSendMessage = () => {
socket.emit("ROOM:NEW_MESSAGE", {
roomId: socketData.roomId,
text: messageValue,
name: socketData.name,
});
setFreshData([...freshData, { text: messageValue, name: socketData.name }]);
setMessageValue("");
};
And it works !

App stops working when i keep sending multiple messages with socket.io

I have a react native expo CLI app add i am trying to create a in app built messaging app like whatsapp inside. All my api calls work in storing the messages and get all the conversations. I have an all messages screen is shows all the conversation the user has with the last message sent inside the chat and a chat screen that shows all the messages sent inside that conversation.
I am using socket.io-client and everything works accordingly except when I keep sending multiple messages my backend stops connecting to my app, then I need to reload my app again then the same happens. Why is that happening?
Also, something is wrong with my code, the messages are getting emitted multiple times.
Here is my code
NodeJS
const server = http.createServer(app);
const io = socket(server)
io.on('connection', (socket) => {
// connection
console.log('User ' + socket.id + ' connected')
// joining a room
socket.on('subscribe', (room) => {
console.log('user socket', socket.id, 'joining room', room);
socket.join(room);
});
// send message within room
socket.on('message', (data) => {
io.in(data.conversationId).emit('send_message', {
message: data.message,
receiverId: data.to,
senderId: data.from,
id: data.conversationId,
data: data.data
});
})
// seen message
socket.on('markSeen', (data) => {
// Emit 'markedSeen' event
console.log(socket.id, 'has seen your message')
io.emit('markedSeen', data);
});
// main socket listening
socket.on('listening', (data) => {
console.log(data)
console.log('user socket', socket.id, 'is listening', data.conversationId, data.message)
io.emit('socketListening', data)
});
// user typing within room
socket.on('typing', (data) => {
console.log('user socket', socket.id, 'in room', data.chatId, data.text)
io.in(data.chatId).emit('typingResponse', data.text)
});
// waiting to enter room
socket.on('waiting', (data) => {
console.log('user', socket.id, 'is waiting.');
});
// disconnect
socket.on('disconnect',() => {
console.log('USER DISCONNECTED', socket.id)
})
socket.on('unsubscribe', (data) => {
console.log(data)
socket.leave(data.id);
socket.to(data.id).emit('user left', socket.id);
console.log('user left', socket.id)
});
})
React Native
Messages Screen
const [listings, setListings] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const loadListings = async() => {
setLoading(true);
const response = await messagesApi.getMessages();
setLoading(false);
if(refreshing) setRefreshing(false);
if (!response.ok) return setError(true);
setError(false);
setListings(response.data)
};
const { user } = useAuth();
const socketUrl = 'IP_ADDRESS';
let socket = useRef(null);
useEffect(()=>{
loadListings();
socket.current = io.connect(socketUrl)
socket.current.on('connect', msg => {
console.log(`user: ${user.id} is waiting.`)
console.log(`user: ${user.id} is waiting. socketID: ${socket.current.id}`)
socket.current.emit('waiting', user.id);
});
return(() => {
console.log(`user: ${user.id} --- socketid ${socket.current.id} deleted`)
socket.current.removeAllListeners('connect')
});
}, [socketUrl])
useEffect(() => {
socket.current.on("socketListening", (msg) => {
console.log('MSG',msg)
const result = listings.find(e => e.id === msg.conversationId)
if (result !== undefined) {
const resultMessages = result.Messages;
const newMessages = [msg,...resultMessages]
result.Messages = newMessages;
result.lastMessage = msg.message;
const arraywithoutrecord = listings.filter(e=>e.id!=msg.conversationId)
setListings([result,...arraywithoutrecord])
}
})
socket.current.on('markedSeen', (message) => {
const result = listings?.find(e => e.id === message)
if (result !== undefined) {
result.Messages[0].seenByUser = true;
const arraywithoutrecord = listings?.filter(e=>e.id!=message)
setListings([result,...arraywithoutrecord])
}
});
}, [listings]);
Chat Screen
const message = route.params.message;
const [messages, setMessages] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const [text, setText] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const[isAdminTyping, setIsAdminTyping] = useState(false)
const { user } = useAuth();
const socketUrl = 'IP_ADDRESS';
let socket = useRef(null);
useEffect(() => {
setMessages(message.Messages)
socket.current = io.connect(socketUrl)
socket.current.on('connect', msg => {
console.log(`user: ${user.id} has joined conversation ${message.id}`,'connection socket id', socket.current.id)
socket.current.emit('subscribe', message.id);
socket.current.emit('markSeen', message.id);
});
socket.current.on("send_message", (msg) => {
setMessages(messages => [msg, ...messages]);
socket.current.emit('markSeen', message.id);
});
socket.current.on("typingResponse", (msg) => {
if (msg === 'Admin is typing...') {
setIsAdminTyping(true)
} else {
setIsAdminTyping(false)
}
})
return(() => {
console.log(`user: ${user.id} left the room ${message.id} --- socketid ${socket.current.id}`)
socket.current.close()
});
}, [socketUrl]);
const seenByUser = async () => {
const response = await messagesApi.seenByUser({conversationId:message.id});
if (response.ok) {
console.log('all messages seen')
}
}
useEffect(() => {
seenByUser();
},[messages])
const onSend = async(conversationId, senderId, receiverId, message) => {
const response = await messagesApi.sendMessage({ conversationId, senderId, receiverId, message });
if (!response.ok) return setError(true);
console.log("sent")
const to = (user.id === route.params.message.user ?route.params.message.admin : route.params.message.user)
socket.current.emit('message', { to: to, from: user.id, message, conversationId, data: response.data });
setText("")
socket.current.emit('listening',response.data);
};
const onFocus = () => {
socket.current.emit('typing', { chatId: message.id, text: 'User is typing...' });
}
const onBlur = () => {
socket.current.emit('typing', { chatId: message.id, text: 'User is NOT typing...' });
}
My entire Node index.js file
const express = require("express");
const users = require("./routes/users");
const auth = require("./routes/auth");
const listing = require("./routes/listing");
const helmet = require("helmet");
const compression = require("compression");
const config = require("config");
const app = express();
var cors = require('cors')
app.use(cors())
//Socket.io
const http = require("http");
const socket = require("socket.io")
const server=http.createServer(app);
const io =socket(server,
{
cors: {
origin:
[
"http://localhost:3000", /\.localhost\:3000$/,"IP_ADDRESS"
]
,
methods: ["GET", "POST"],
credentials: true
},
maxHttpBufferSize: 4e6 // 4Mb
}
)
io.on('connection',(socket)=>{
// connection
console.log('User '+socket.id+' connected')
// joining a room
socket.on('subscribe', (room)=> {
console.log('user socket',socket.id,'joining room', room);
socket.join(room);
});
// send message within room
socket.on('message', (data) => {
socket.to(data.conversationId).emit('send_message', {
message: data.message, receiverId: data.to,senderId:data.from,id:data.conversationId,data:data.data
});
})
// seen message
socket.on('markSeen', (data)=> {
// Emit 'markedSeen' event
console.log(socket.id,'has seen your message')
io.emit('markedSeen', data);
});
// main socket listening
socket.on('listening', (data) => {
console.log(data)
console.log('user socket',socket.id,'is listening',data.conversationId,data.message)
io.emit('socketListening',data)
}
);
// user typing within room
socket.on('typing', (data) => {
console.log('user socket',socket.id,'in room',data.chatId,data.text)
io.in(data.chatId).emit('typingResponse', data.text)
}
);
// waiting to enter room
socket.on('waiting', (data)=> {
console.log('user',socket.id, 'is waiting.');
});
// disconnect
socket.on('disconnect',(reason)=>{
socket.disconnect()
console.log('socket disconnected',socket.disconnect().disconnected)
console.log('USER DISCONNECTED',socket.id)
})
})
app.use(express.json({limit: '50mb'}));
app.use(express.urlencoded({limit: '50mb', extended: true, parameterLimit: 500000}));
app.use(express.static("public"));
app.use(helmet());
app.use(compression());
app.use("/api/users/", users);
app.use("/api/auth/", auth)
app.use("/api/listings/", listing);
const port = process.env.PORT || config.get("port");
server.listen(port, function() {
console.log(`Server started on port ${port}...`);
});
Cant speak to why it stops connecting, would really need more details on that one. For example, is it 'new' connections, or is your current connection dropping etc.
The whole point of WS is a persistent connection, so, once a user is connected, they should not need to reconnect again (unless they got disconnected)
Without knowing the whole flow of things in your app ( this is just going off what you have posted here )
It looks like you are having a user connect 'each time' they hit that page, if my assumption is correct here, then don't do that :D
This kind of leads into your other question about multiple messages being sent and then ties back into the whole websocket idea in the first place, its a persistent connection so you only need to do thing once.
For example, if you subscribe to a channel or room, you only need to do that one time, once you subscribe to a channel that socketID is tied to those emits, and a socket can subscribe to a channel multiple times. Knowing that, if, when a user hits your messages screen, and your code goes through and subscribes them to that channel each time they will in turn get multiple emits.
Again, without seeing the full flow of data it looks like every time a user hit your chat screen you are attempting to
connect to socket
server setting up the on events
Essentially telling them to connect again and listen for those calls, the more times they join, the more they see right.
What you want to do is make sure you define your socket globally AND only 'listen' for channels or emits 'once'
What I have done in the past is to have a main socket function when a user joins the event, this is done on login and or opening the app. This will perform the socket connect, run through all the connections etc and at the end I call a socketInit function. This function sets up all my .on statements in a conditional block, that ensures it will only run one time. So, something like if(socketInit = false) {run all my on code etc}
This ensures everything is only called one time. Then, inside each route, I do the 'emit' to the socket server telling it to 'join the room' or 'getchats' etc. Because I only have the .on called once, it means when I asked the socket server for the data via the emit what it returns on my .on only gets called once, because that .getChat on is only defined once and it knows what to do with that data and updates the screen accordingly.
Hope that helps!

In socket test case how can i check socket.on() working or not

socket.test.js
var io = socketio;
var socketObj = require('../util/socket.service');
var should = require("chai").should();
var options = {
transports: ['websocket'],
'force new connection': true,
'reconnection': false
};
var socket = null;
describe('chatService', function() {
//Test for send message
it('send message', function() {
var message = {
_id: null,
receiverId: 2,
receiverType: 'personal',
senderId: 1,
picUrl: null,
text: 'hey',
type: 'text',
status: 'avaliable',
contentType: 'text',
contentData: {
data: ['adff']
},
responseData: {
data: ['aggf']
},
createdBy: 'arun',
updatedBy: 'arun'
};
var user = { id: 30 };
var socket = io.connect('http://localhost:3000');
//message is printing on console.
console.log(message);
socket.on('send-message', function(message) {
//message is not printing on console.
console.log(message);
socket.emit('receive-message', msg);
expect(message).to.equal(msg);
});
});
});
Here writing the test case in mocha for send-message,it will send my hardcoded message from here to socket.service and receive-message will get it back from the server.
But the issue is that whatever I am writing console.log() within socket.on() is not printing, so how can I check socket.on working or not.
socket.service.js
import MessageService from '../apis/message/message.service';
import GroupService from '../apis/group/group.service';
import log from '../config/log4js.config';
import UserService from '../apis/user/user.service';
import UserModel from '../apis/user/index';
var messageService = new MessageService();
var groupService = new GroupService();
var userService = new UserService();
exports.connectSocket = (io) => {
io.on('connection', function(socket) {
// get userId from client
socket.on('user-connected', userId => {
console.log('a user connected with ID: ' + userId);
userService.getById(userId, (user) => {
if (user.id === userId) {
userService.updateRegisteredUser({ 'id': userId, 'socketId':
socket.id }, (user) => {});
}
});
//user disconnected
socket.on('disconnect', () => {
console.log('user disconnected with ID: ' + userId);
});
});
/**
* for sending message to group/user which is emitted from client(msg
with an groupId/userId)
*/
socket.on('send-message', (msg) => {
// if it is a group message
if (msg.receiverType === "group") {
userService.getById(msg.senderId, (result) => {
msg.createdBy = result.name;
msg.picUrl = result.picUrl;
messageService.sendMessage(msg, (result) => {
console.log('message: ', msg);
});
});
groupService.getAllUsersByGroupId(msg.receiverId)
.then((users) => {
users.map(user => {
io.in(user.socketId).emit('receive-message', msg);
//emit one-by-one for all users
});
});
}
// if it is a private message
else if (msg.receiverType === "private") {
userService.getById(msg.senderId, (result) => {
msg.createdBy = result.name;
msg.picUrl = result.picUrl;
messageService.sendMessage(msg, (result) => {});
});
userService.getById(msg.receiverId, (result) => {
socket.to(result.socketId).emit('receive-message', msg);
})
}
// if neither group nor user is selected
else {
userService.getById(msg.senderId, (result) => {
socket.to(result.socketId).emit('receive-message', { "text":
'Select a group or an user to chat with.' }); //only to sender
});
}
});
});
}
This is my Server code.

How to identify two users connected to a socket, and join them to a room? Socket.io

So here is the situation, I need to create a room for two people that exist in our database. The problem is that I can't identify with those users based on their sockets.
Let's say there are two random users user1 and user2. Now, user1 wants to initiate chat with user2, so I need to create a room when the user1 initiates and join the two users to that room. Here's the problem that sockets join a room, and socket.id changes with reconnection from the same host. So, I can't figure out how do I join user2 to the room.
Here's the code.
export default function(socketio) {
let sockets = [];
let people = {};
const findUserById = function findUserById(data) {
for (let x in people) {
if (people[x].userId === data) {
return x;
}
}
}
/**
* [createRoom Creates a room for two users]
* #param {[type]} data1 [Mongoose objectId for user]
* #param {[type]} data2 [Mongoose objectId for user]
* #return {[type]} [A string of 6 characters that identifies with the room]
*/
const createRoom = function createRoom(data1, data2) {
const user1 = data1.toString().slice(-3);
const user2 = data2.toString().slice(-3);
return user1 + user2;
}
socketio.on('connection', function(socket) {
console.log(`Connection made from ${socket.id}`);
sockets.push({
socket: socket,
id: socket.id
});
socketio.on('join', (data) => {
people[socket.id] = {
userId: data
}
})
socketio.on('initiate', (data) => {
const receiverSocket = findUserById(data.receiver);
if (receiverSocket) {
let room = createRoom(people[socket.id].userId, receiverSocket.userId)
let chat = new Chat({
room: room
});
/*
Now, we'll add the chats to the respective users.
*/
User.findById(people[socket.id].userId, (err, foundUser) => {
foundUser.chats.push({
room: room,
partner: receiverSocket.userId
})
foundUser.save((err, success) => {
if (!err || success) {
User.findById(receiverSocket.userId, (err, secondUser) => {
secondUser.chats.push({
room: room,
partner: people[socket.id].userId
})
secondUser.save()
})
}
})
})
let user1Socket = sockets.filter((el) => {
return el.id === socket.id
})
let user2Socket = sockets.filter((el) => {
return el.id === receiverSocket
})
user1Socket.socket.join(room);
user2Socket.socket.join(room);
socketio.sockets.in(room).emit('private room created', {
room: room
});
}
})
socketio.on('job', (data) => {
socketio.broadcast.emit('jobPosted', data);
})
socketio.on('message', (data) => {
Chat.findOne({room: data.room})
.then((data)=>{
data.chat.push({'content': data.message, 'userId': data.sender, 'date': new Date()})
data.save()
.then((success)=>{
socketio.sockets.in(data.room).emit('messageReceived', data.message)
})
})
})
});
}
The logic here was that each time a connection is made I would store that socket.id and later on pull it out to create a room. But this fails when socket.id changes.
Can anyone help me with this?

SocketIO Identify what user disconnected

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));
});
}

Resources