From ea3aa3d128e84bc91d713f8152aefc8c19d4b5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 11 Dec 2020 12:23:50 +0100 Subject: [PATCH] Reimplementing ban/unban/messages --- back/src/Model/GameRoom.ts | 12 ++- back/src/RoomManager.ts | 42 ++++++++-- back/src/Services/SocketManager.ts | 92 ++++++++++++--------- messages/protos/messages.proto | 24 ++++++ pusher/src/Controller/IoSocketController.ts | 28 ++++--- pusher/src/Services/ApiClientRepository.ts | 4 + pusher/src/Services/SocketManager.ts | 34 +++++++- 7 files changed, 172 insertions(+), 64 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 4e1dc1d7..129b0ac8 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -17,7 +17,7 @@ export type ConnectCallback = (user: User, group: Group) => void; export type DisconnectCallback = (user: User, group: Group) => void; export enum GameRoomPolicyTypes { - ANONYMUS_POLICY = 1, + ANONYMOUS_POLICY = 1, MEMBERS_ONLY_POLICY, USE_TAGS_POLICY, } @@ -28,6 +28,7 @@ export class GameRoom { // Users, sorted by ID private readonly users: Map; + private readonly usersByUuid: Map; private readonly groups: Set; private readonly admins: Set; @@ -58,7 +59,7 @@ export class GameRoom { this.roomId = roomId; this.anonymous = isRoomAnonymous(roomId); this.tags = []; - this.policyType = GameRoomPolicyTypes.ANONYMUS_POLICY; + this.policyType = GameRoomPolicyTypes.ANONYMOUS_POLICY; if (this.anonymous) { this.roomSlug = extractRoomSlugPublicRoomId(this.roomId); @@ -71,6 +72,7 @@ export class GameRoom { this.users = new Map(); + this.usersByUuid = new Map(); this.admins = new Set(); this.groups = new Set(); this.connectCallback = connectCallback; @@ -89,6 +91,10 @@ export class GameRoom { return this.users; } + public getUserByUuid(uuid: string): User|undefined { + return this.usersByUuid.get(uuid); + } + public join(socket : UserSocket, joinRoomMessage: JoinRoomMessage): User { const positionMessage = joinRoomMessage.getPositionmessage(); if (positionMessage === undefined) { @@ -99,6 +105,7 @@ export class GameRoom { const user = new User(this.nextUserId, joinRoomMessage.getUseruuid(), position, false, this.positionNotifier, socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList())); this.nextUserId++; this.users.set(user.id, user); + this.usersByUuid.set(user.uuid, user); // Let's call update position to trigger the join / leave room //this.updatePosition(socket, userPosition); this.updateUserGroup(user); @@ -120,6 +127,7 @@ export class GameRoom { this.leaveGroup(userObj); } this.users.delete(user.id); + this.usersByUuid.delete(user.uuid); if (userObj !== undefined) { this.positionNotifier.leave(userObj); diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 90e85b61..3c038998 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -1,12 +1,25 @@ import {IRoomManagerServer} from "./Messages/generated/messages_grpc_pb"; import { - AdminPusherToBackMessage, - ClientToServerMessage, ItemEventMessage, - JoinRoomMessage, PlayGlobalMessage, PusherToBackMessage, QueryJitsiJwtMessage, ReportPlayerMessage, - RoomJoinedMessage, ServerToAdminClientMessage, - ServerToClientMessage, SilentMessage, UserMovesMessage, ViewportMessage, WebRtcSignalToServerMessage, ZoneMessage + AdminGlobalMessage, + AdminMessage, + AdminPusherToBackMessage, BanMessage, + ClientToServerMessage, EmptyMessage, + ItemEventMessage, + JoinRoomMessage, + PlayGlobalMessage, + PusherToBackMessage, + QueryJitsiJwtMessage, + ReportPlayerMessage, + RoomJoinedMessage, + ServerToAdminClientMessage, + ServerToClientMessage, + SilentMessage, + UserMovesMessage, + ViewportMessage, + WebRtcSignalToServerMessage, + ZoneMessage } from "./Messages/generated/messages_pb"; -import grpc, {ServerDuplexStream, ServerWritableStream} from "grpc"; +import grpc, {sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream} from "grpc"; import {Empty} from "google-protobuf/google/protobuf/empty_pb"; import {socketManager} from "./Services/SocketManager"; import {emitError} from "./Services/MessageHelpers"; @@ -171,6 +184,23 @@ const roomManager: IRoomManagerServer = { console.error('An error occurred in joinAdminRoom stream:', err); }); }, + sendAdminMessage(call: ServerUnaryCall, callback: sendUnaryData): void { + + socketManager.sendAdminMessage(call.request.getRoomid(), call.request.getRecipientuuid(), call.request.getMessage()); + + callback(null, new EmptyMessage()); + }, + sendGlobalAdminMessage(call: ServerUnaryCall, callback: sendUnaryData): void { + throw new Error('Not implemented yet'); + // TODO + callback(null, new EmptyMessage()); + }, + ban(call: ServerUnaryCall, callback: sendUnaryData): void { + + socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid()); + + callback(null, new EmptyMessage()); + }, }; export {roomManager}; diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 7014d52d..9c5afa00 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -31,7 +31,7 @@ import { BatchMessage, BatchToPusherMessage, SubToPusherMessage, - UserJoinedZoneMessage, GroupUpdateZoneMessage, GroupLeftZoneMessage, UserLeftZoneMessage + UserJoinedZoneMessage, GroupUpdateZoneMessage, GroupLeftZoneMessage, UserLeftZoneMessage, AdminMessage, BanMessage } from "../Messages/generated/messages_pb"; import {User, UserSocket} from "../Model/User"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; @@ -164,25 +164,6 @@ export class SocketManager { console.log('SENDING MESSAGE roomJoinedMessage'); socket.write(serverToClientMessage); - //get data information and show messages - if (ADMIN_API_URL) { - adminApi.fetchMemberDataByUuid(user.uuid).then((res: FetchMemberDataByUuidResponse) => { - if (!res.messages) { - return; - } - res.messages.forEach((c: unknown) => { - const messageToSend = c as { type: string, message: string }; - socketManager.emitSendUserMessage({ - userUuid: user.uuid, - type: messageToSend.type, - message: messageToSend.message - }) - }); - }).catch((err) => { - console.error('fetchMemberDataByUuid => err', err); - }); - } - return { room, user @@ -643,27 +624,6 @@ export class SocketManager { user.socket.write(serverToClientMessage); } - public emitSendUserMessage(messageToSend: {userUuid: string, message: string, type: string}): void { - // TODO: move this to room (findByUuid) - throw new Error("Not yet reimplemented"); - /*const socket = this.searchClientByUuid(messageToSend.userUuid); - if(!socket){ - throw 'socket was not found'; - } - - const sendUserMessage = new SendUserMessage(); - sendUserMessage.setMessage(messageToSend.message); - sendUserMessage.setType(messageToSend.type); - - const serverToClientMessage = new ServerToClientMessage(); - serverToClientMessage.setSendusermessage(sendUserMessage); - - if (!socket.disconnecting) { - socket.send(serverToClientMessage.serializeBinary().buffer, true); - } - return socket;*/ - } - /** * Merges the characterLayers received from the front (as an array of string) with the custom textures from the back. */ @@ -762,6 +722,56 @@ export class SocketManager { } } + public sendAdminMessage(roomId: string, recipientUuid: string, message: string): void { + const room = this.rooms.get(roomId); + if (!room) { + console.error("In sendAdminMessage, could not find room with id '" + roomId + "'. Maybe the room was closed a few milliseconds ago and there was a race condition?"); + return; + } + + const recipient = room.getUserByUuid(recipientUuid); + if (recipient === undefined) { + console.error("In sendAdminMessage, could not find user with id '" + recipientUuid + "'. Maybe the user left the room a few milliseconds ago and there was a race condition?"); + return; + } + + const sendUserMessage = new SendUserMessage(); + sendUserMessage.setMessage(message); + sendUserMessage.setType('ban'); + + const subToPusherMessage = new SubToPusherMessage(); + subToPusherMessage.setSendusermessage(sendUserMessage); + + recipient.socket.write(subToPusherMessage); + } + + public banUser(roomId: string, recipientUuid: string): void { + const room = this.rooms.get(roomId); + if (!room) { + console.error("In banUser, could not find room with id '" + roomId + "'. Maybe the room was closed a few milliseconds ago and there was a race condition?"); + return; + } + + const recipient = room.getUserByUuid(recipientUuid); + if (recipient === undefined) { + console.error("In banUser, could not find user with id '" + recipientUuid + "'. Maybe the user left the room a few milliseconds ago and there was a race condition?"); + return; + } + + // Let's leave the room now. + room.leave(recipient); + + const sendUserMessage = new SendUserMessage(); + sendUserMessage.setType('banned'); + + const subToPusherMessage = new SubToPusherMessage(); + subToPusherMessage.setSendusermessage(sendUserMessage); + + recipient.socket.write(subToPusherMessage); + + // Let's close the connection when the user is banned. + recipient.socket.end(); + } } export const socketManager = new SocketManager(); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 17f5b730..c954bb95 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -288,6 +288,7 @@ message SubToPusherMessage { GroupLeftZoneMessage groupLeftZoneMessage = 4; UserLeftZoneMessage userLeftZoneMessage = 5; ItemEventMessage itemEventMessage = 6; + SendUserMessage sendUserMessage = 7; } } @@ -320,9 +321,32 @@ message AdminPusherToBackMessage { } } +// A message sent by an administrator to a recipient +message AdminMessage { + string message = 1; + string recipientUuid = 2; + string roomId = 3; +} + +// A message sent by an administrator to absolutely everybody +message AdminGlobalMessage { + string message = 1; +} + +message BanMessage { + string recipientUuid = 1; + string roomId = 2; +} + +message EmptyMessage { + +} service RoomManager { rpc joinRoom(stream PusherToBackMessage) returns (stream ServerToClientMessage); rpc listenZone(ZoneMessage) returns (stream BatchToPusherMessage); rpc adminRoom(stream AdminPusherToBackMessage) returns (stream ServerToAdminClientMessage); + rpc sendAdminMessage(AdminMessage) returns (EmptyMessage); + rpc sendGlobalAdminMessage(AdminGlobalMessage) returns (EmptyMessage); + rpc ban(BanMessage) returns (EmptyMessage); } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 08e829a3..58cf05fb 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -12,7 +12,7 @@ import { WebRtcSignalToServerMessage, PlayGlobalMessage, ReportPlayerMessage, - QueryJitsiJwtMessage + QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {TemplatedApp} from "uWebSockets.js" @@ -61,7 +61,6 @@ export class IoSocketController { }, open: (ws) => { console.log('Admin socket connect for room: '+ws.roomId); - const roomId = ws.roomId; ws.disconnecting = false; socketManager.handleAdminRoom(ws as ExAdminSocketInterface, ws.roomId as string); @@ -84,6 +83,8 @@ export class IoSocketController { }, message: (ws, arrayBuffer, isBinary): void => { try { + const roomId = ws.roomId as string; + //TODO refactor message type and data const message: {event: string, message: {type: string, message: unknown, userUuid: string}} = JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); @@ -92,14 +93,11 @@ export class IoSocketController { const messageToEmit = (message.message as { message: string, type: string, userUuid: string }); switch (message.message.type) { case 'ban': { - socketManager.emitSendUserMessage(messageToEmit); + socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, roomId); break; } case 'banned': { - const socketUser = socketManager.emitSendUserMessage(messageToEmit); - setTimeout(() => { - socketUser.close(); - }, 10000); + socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, roomId); break; } default: { @@ -269,11 +267,17 @@ export class IoSocketController { } res.messages.forEach((c: unknown) => { const messageToSend = c as { type: string, message: string }; - socketManager.emitSendUserMessage({ - userUuid: client.userUuid, - type: messageToSend.type, - message: messageToSend.message - }) + + const sendUserMessage = new SendUserMessage(); + sendUserMessage.setType(messageToSend.type); + sendUserMessage.setMessage(messageToSend.message); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendusermessage(sendUserMessage); + + if (!client.disconnecting) { + client.send(serverToClientMessage.serializeBinary().buffer, true); + } }); }).catch((err) => { console.error('fetchMemberDataByUuid => err', err); diff --git a/pusher/src/Services/ApiClientRepository.ts b/pusher/src/Services/ApiClientRepository.ts index be8f14ff..d5af9e4b 100644 --- a/pusher/src/Services/ApiClientRepository.ts +++ b/pusher/src/Services/ApiClientRepository.ts @@ -15,6 +15,10 @@ class ApiClientRepository { } return Promise.resolve(this.roomManagerClient); } + + public async getAllClients(): Promise { + return [await this.getClient('')]; + } } const apiClientRepository = new ApiClientRepository(); diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 5ab6688c..f0bc250a 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -30,7 +30,7 @@ import { CharacterLayerMessage, PusherToBackMessage, AdminPusherToBackMessage, - ServerToAdminClientMessage + ServerToAdminClientMessage, AdminMessage, BanMessage } from "../Messages/generated/messages_pb"; import {PointInterface} from "../Model/Websocket/PointInterface"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; @@ -593,7 +593,21 @@ export class SocketManager implements ZoneEventListener { client.send(serverToClientMessage.serializeBinary().buffer, true); } - public emitSendUserMessage(messageToSend: {userUuid: string, message: string, type: string}): ExSocketInterface { + public async emitSendUserMessage(userUuid: string, message: string, roomId: string): Promise { + + const backConnection = await apiClientRepository.getClient(roomId); + + const adminMessage = new AdminMessage(); + adminMessage.setRecipientuuid(userUuid); + adminMessage.setMessage(message); + adminMessage.setRoomid(roomId); + + backConnection.sendAdminMessage(adminMessage, (error) => { + if (error !== null) { + console.error('Error while sending admin message', error); + } + }); +/* const socket = this.searchClientByUuid(messageToSend.userUuid); if(!socket){ throw 'socket was not found'; @@ -609,7 +623,21 @@ export class SocketManager implements ZoneEventListener { if (!socket.disconnecting) { socket.send(serverToClientMessage.serializeBinary().buffer, true); } - return socket; + return socket;*/ + } + + public async emitBan(userUuid: string, message: string, roomId: string): Promise { + const backConnection = await apiClientRepository.getClient(roomId); + + const banMessage = new BanMessage(); + banMessage.setRecipientuuid(userUuid); + banMessage.setRoomid(roomId); + + backConnection.ban(banMessage, (error) => { + if (error !== null) { + console.error('Error while sending ban message', error); + } + }); } /**