Refactoring searchClientById

searchClientById was scanning through all open sockets to find the right one (which is inefficient if we have many).
Instead, I created a new Map that directly maps ids to sockets.
Furthermore, this solves a long-standing issue (when a socket is disconnected, we cannot find it anymore in the sockets list but it is still available in the disconnect callback from the map)
As a result, we should not have any remaining circles any more.
This commit is contained in:
David Négrier 2020-05-18 18:33:04 +02:00
parent 2628373b56
commit e934015d87
2 changed files with 33 additions and 76 deletions

View File

@ -30,6 +30,7 @@ enum SockerIoEvent {
export class IoSocketController { export class IoSocketController {
Io: socketIO.Server; Io: socketIO.Server;
Worlds: Map<string, World> = new Map<string, World>(); Worlds: Map<string, World> = new Map<string, World>();
sockets: Map<string, ExSocketInterface> = new Map<string, ExSocketInterface>();
constructor(server: http.Server) { constructor(server: http.Server) {
this.Io = socketIO(server); this.Io = socketIO(server);
@ -60,10 +61,7 @@ export class IoSocketController {
// Let's get the room of the group. To do this, let's get anyone in the group and find its room. // Let's get the room of the group. To do this, let's get anyone in the group and find its room.
// Note: this is suboptimal // Note: this is suboptimal
let userId = group.getUsers()[0].id; let userId = group.getUsers()[0].id;
let client: ExSocketInterface|null = this.searchClientById(userId); let client: ExSocketInterface = this.searchClientByIdOrFail(userId);
if (client === null) {
return;
}
let roomId = client.roomId; let roomId = client.roomId;
this.Io.in(roomId).emit(SockerIoEvent.GROUP_CREATE_UPDATE, { this.Io.in(roomId).emit(SockerIoEvent.GROUP_CREATE_UPDATE, {
position: group.getPosition(), position: group.getPosition(),
@ -73,19 +71,15 @@ export class IoSocketController {
private sendDeleteGroupEvent(uuid: string, lastUser: UserInterface): void { private sendDeleteGroupEvent(uuid: string, lastUser: UserInterface): void {
// Let's get the room of the group. To do this, let's get anyone in the group and find its room. // Let's get the room of the group. To do this, let's get anyone in the group and find its room.
// Note: this is suboptimal
let userId = lastUser.id; let userId = lastUser.id;
let client: ExSocketInterface|null = this.searchClientById(userId); let client: ExSocketInterface = this.searchClientByIdOrFail(userId);
if (client === null) {
console.warn('Could not find client ', userId, ' in group')
return;
}
let roomId = client.roomId; let roomId = client.roomId;
this.Io.in(roomId).emit(SockerIoEvent.GROUP_DELETE, uuid); this.Io.in(roomId).emit(SockerIoEvent.GROUP_DELETE, uuid);
} }
ioConnection() { ioConnection() {
this.Io.on(SockerIoEvent.CONNECTION, (socket: Socket) => { this.Io.on(SockerIoEvent.CONNECTION, (socket: Socket) => {
this.sockets.set(socket.id, socket as ExSocketInterface);
/*join-rom event permit to join one room. /*join-rom event permit to join one room.
message : message :
userId : user identification userId : user identification
@ -148,20 +142,19 @@ export class IoSocketController {
socket.on(SockerIoEvent.WEBRTC_SIGNAL, (data: any) => { socket.on(SockerIoEvent.WEBRTC_SIGNAL, (data: any) => {
//send only at user //send only at user
let client = this.searchClientById(data.receiverId); let client = this.sockets.get(data.receiverId);
if (!client) { if (client === undefined) {
console.error("While exchanging a WebRTC signal: client doesn't exist for ", data.receiverId); console.warn("While exchanging a WebRTC signal: client with id ", data.receiverId, " does not exist. This might be a race condition.");
return; return;
} }
return client.emit(SockerIoEvent.WEBRTC_SIGNAL, data); return client.emit(SockerIoEvent.WEBRTC_SIGNAL, data);
}); });
socket.on(SockerIoEvent.WEBRTC_OFFER, (data: any) => { socket.on(SockerIoEvent.WEBRTC_OFFER, (data: any) => {
//send only at user //send only at user
let client = this.searchClientById(data.receiverId); let client = this.sockets.get(data.receiverId);
if (!client) { if (client === undefined) {
console.error("client doesn't exist for ", data.receiverId); console.warn("While exchanging a WebRTC offer: client with id ", data.receiverId, " does not exist. This might be a race condition.");
return; return;
} }
client.emit(SockerIoEvent.WEBRTC_OFFER, data); client.emit(SockerIoEvent.WEBRTC_OFFER, data);
@ -170,16 +163,16 @@ export class IoSocketController {
socket.on(SockerIoEvent.DISCONNECT, () => { socket.on(SockerIoEvent.DISCONNECT, () => {
try { try {
let Client = (socket as ExSocketInterface); let Client = (socket as ExSocketInterface);
this.sendDisconnectedEvent(Client); //this.sendDisconnectedEvent(Client);
//refresh position of all user in all rooms in real time //refresh position of all user in all rooms in real time (to remove the disconnected user)
this.refreshUserPosition(Client); this.refreshUserPosition(Client);
//leave room //leave room
this.leaveRoom(Client); this.leaveRoom(Client);
//leave webrtc room //leave webrtc room
socket.leave(Client.webRtcRoomId); //socket.leave(Client.webRtcRoomId);
//delete all socket information //delete all socket information
delete Client.webRtcRoomId; delete Client.webRtcRoomId;
@ -190,6 +183,7 @@ export class IoSocketController {
console.error('An error occurred on "disconnect"'); console.error('An error occurred on "disconnect"');
console.error(e); console.error(e);
} }
this.sockets.delete(socket.id);
}); });
// Let's send the user id to the user // Let's send the user id to the user
@ -202,55 +196,13 @@ export class IoSocketController {
}); });
} }
/** searchClientByIdOrFail(userId: string): ExSocketInterface {
* TODO: each call to this method is suboptimal. It means that instead of passing an ID, we should pass a client object. let client: ExSocketInterface|undefined = this.sockets.get(userId);
* @param userId if (client === undefined) {
*/ throw new Error("Could not find user with id " + userId);
searchClientById(userId: string): ExSocketInterface | null {
let clients: Array<any> = Object.values(this.Io.sockets.sockets);
for (let i = 0; i < clients.length; i++) {
let client: ExSocketInterface = clients[i];
if (client.id !== userId) {
continue;
} }
return client; return client;
} }
console.log("Could not find user with id ", userId);
//throw new Error("Could not find user with id " + userId);
return null;
}
/**
* @param userId
*/
searchClientByToken(userId: string): ExSocketInterface | null {
let clients: Array<any> = Object.values(this.Io.sockets.sockets);
for (let i = 0; i < clients.length; i++) {
let client: ExSocketInterface = clients[i];
if (client.id !== userId) {
continue
}
return client;
}
return null;
}
/**
*
* @param Client: ExSocketInterface
*/
sendDisconnectedEvent(Client: ExSocketInterface) {
Client.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, {
userId: Client.id
});
//disconnect webrtc room
if(!Client.webRtcRoomId){
return;
}
Client.leave(Client.webRtcRoomId);
delete Client.webRtcRoomId;
}
leaveRoom(Client : ExSocketInterface){ leaveRoom(Client : ExSocketInterface){
//lease previous room and world //lease previous room and world
@ -349,12 +301,11 @@ export class IoSocketController {
rooms.refreshUserPosition(rooms, this.Io); rooms.refreshUserPosition(rooms, this.Io);
// update position in the world // update position in the world
let messageUserPosition = new MessageUserPosition(Client.id, Client.name, Client.character, Client.position);
let world = this.Worlds.get(Client.roomId); let world = this.Worlds.get(Client.roomId);
if (!world) { if (!world) {
return; return;
} }
world.updatePosition(Client, messageUserPosition.position); world.updatePosition(Client, Client.position);
this.Worlds.set(Client.roomId, world); this.Worlds.set(Client.roomId, world);
} }
@ -412,19 +363,26 @@ export class IoSocketController {
//connected user //connected user
connectedUser(userId: string, group: Group) { connectedUser(userId: string, group: Group) {
let Client = this.searchClientById(userId); /*let Client = this.sockets.get(userId);
if (!Client) { if (Client === undefined) {
return; return;
} }*/
let Client = this.searchClientByIdOrFail(userId);
this.joinWebRtcRoom(Client, group.getId()); this.joinWebRtcRoom(Client, group.getId());
} }
//disconnect user //disconnect user
disConnectedUser(userId: string, group: Group) { disConnectedUser(userId: string, group: Group) {
let Client = this.searchClientById(userId); let Client = this.searchClientByIdOrFail(userId);
if (!Client) { Client.to(group.getId()).emit(SockerIoEvent.WEBRTC_DISCONNECT, {
userId: userId
});
//disconnect webrtc room
if(!Client.webRtcRoomId){
return; return;
} }
this.sendDisconnectedEvent(Client) Client.leave(Client.webRtcRoomId);
delete Client.webRtcRoomId;
} }
} }

View File

@ -60,7 +60,6 @@ export class World {
public leave(user : Identificable){ public leave(user : Identificable){
let userObj = this.users.get(user.id); let userObj = this.users.get(user.id);
if (userObj === undefined) { if (userObj === undefined) {
// FIXME: this seems always wrong. I guess user.id is different from userPosition.userId
console.warn('User ', user.id, 'does not belong to world! It should!'); console.warn('User ', user.id, 'does not belong to world! It should!');
} }
if (userObj !== undefined && typeof userObj.group !== 'undefined') { if (userObj !== undefined && typeof userObj.group !== 'undefined') {