diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 47b28d72..8b40ff71 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -53,6 +53,49 @@ jobs: run: yarn test working-directory: "front" + continuous-integration-pusher: + name: "Continuous Integration Pusher" + + runs-on: "ubuntu-latest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2.0.0" + + - name: "Setup NodeJS" + uses: actions/setup-node@v1 + with: + node-version: '12.x' + + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + version: '3.x' + + - name: "Install dependencies" + run: yarn install + working-directory: "pusher" + + - name: "Install messages dependencies" + run: yarn install + working-directory: "messages" + + - name: "Build proto messages" + run: yarn run proto && yarn run copy-to-pusher + working-directory: "messages" + + - name: "Build" + run: yarn run tsc + working-directory: "pusher" + + - name: "Lint" + run: yarn run lint + working-directory: "pusher" + + - name: "Jasmine" + run: yarn test + working-directory: "pusher" + continuous-integration-back: name: "Continuous Integration Back" diff --git a/back/src/Model/Admin.ts b/back/src/Model/Admin.ts index aa4bd328..a121d105 100644 --- a/back/src/Model/Admin.ts +++ b/back/src/Model/Admin.ts @@ -9,7 +9,7 @@ import { PusherToBackMessage, ServerToAdminClientMessage, ServerToClientMessage, - SubMessage + SubMessage, UserJoinedRoomMessage, UserLeftRoomMessage } from "../Messages/generated/messages_pb"; import {CharacterLayer} from "_Model/Websocket/CharacterLayer"; import {AdminSocket} from "../RoomManager"; @@ -21,16 +21,26 @@ export class Admin { ) { } - public sendUserJoin(uuid: string): void { + public sendUserJoin(uuid: string, name: string, ip: string): void { const serverToAdminClientMessage = new ServerToAdminClientMessage(); - serverToAdminClientMessage.setUseruuidjoinedroom(uuid); + + const userJoinedRoomMessage = new UserJoinedRoomMessage(); + userJoinedRoomMessage.setUuid(uuid); + userJoinedRoomMessage.setName(name); + userJoinedRoomMessage.setIpaddress(ip); + + serverToAdminClientMessage.setUserjoinedroom(userJoinedRoomMessage); this.socket.write(serverToAdminClientMessage); } - public sendUserLeft(uuid: string): void { + public sendUserLeft(uuid: string/*, name: string, ip: string*/): void { const serverToAdminClientMessage = new ServerToAdminClientMessage(); - serverToAdminClientMessage.setUseruuidleftroom(uuid); + + const userLeftRoomMessage = new UserLeftRoomMessage(); + userLeftRoomMessage.setUuid(uuid); + + serverToAdminClientMessage.setUserleftroom(userLeftRoomMessage); this.socket.write(serverToAdminClientMessage); } diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 129b0ac8..41c215ad 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -102,7 +102,17 @@ export class GameRoom { } const position = ProtobufUtils.toPointInterface(positionMessage); - const user = new User(this.nextUserId, joinRoomMessage.getUseruuid(), position, false, this.positionNotifier, socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList())); + const user = new User(this.nextUserId, + joinRoomMessage.getUseruuid(), + joinRoomMessage.getIpaddress(), + 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); @@ -112,7 +122,7 @@ export class GameRoom { // Notify admins for (const admin of this.admins) { - admin.sendUserJoin(user.uuid); + admin.sendUserJoin(user.uuid, user.name, user.IPAddress); } return user; @@ -135,7 +145,7 @@ export class GameRoom { // Notify admins for (const admin of this.admins) { - admin.sendUserLeft(user.uuid); + admin.sendUserLeft(user.uuid/*, user.name, user.IPAddress*/); } } @@ -318,7 +328,7 @@ export class GameRoom { // Let's send all connected users for (const user of this.users.values()) { - admin.sendUserJoin(user.uuid); + admin.sendUserJoin(user.uuid, user.name, user.IPAddress); } } diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 2bc3ee22..51a1a617 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -16,6 +16,7 @@ export class User implements Movable { public constructor( public id: number, public readonly uuid: string, + public readonly IPAddress: string, private position: PointInterface, public silent: boolean, private positionNotifier: PositionNotifier, diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 3c038998..b6a82850 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -2,25 +2,22 @@ import {IRoomManagerServer} from "./Messages/generated/messages_grpc_pb"; import { AdminGlobalMessage, AdminMessage, - AdminPusherToBackMessage, BanMessage, - ClientToServerMessage, EmptyMessage, + AdminPusherToBackMessage, + BanMessage, + EmptyMessage, ItemEventMessage, JoinRoomMessage, PlayGlobalMessage, PusherToBackMessage, QueryJitsiJwtMessage, - ReportPlayerMessage, - RoomJoinedMessage, ServerToAdminClientMessage, ServerToClientMessage, SilentMessage, UserMovesMessage, - ViewportMessage, WebRtcSignalToServerMessage, ZoneMessage } from "./Messages/generated/messages_pb"; -import grpc, {sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream} from "grpc"; -import {Empty} from "google-protobuf/google/protobuf/empty_pb"; +import {sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream} from "grpc"; import {socketManager} from "./Services/SocketManager"; import {emitError} from "./Services/MessageHelpers"; import {User, UserSocket} from "./Model/User"; @@ -74,6 +71,16 @@ const roomManager: IRoomManagerServer = { socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage);*/ } else if (message.hasQueryjitsijwtmessage()){ socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage); + }else if (message.hasSendusermessage()) { + const sendUserMessage = message.getSendusermessage(); + if(sendUserMessage !== undefined) { + socketManager.handlerSendUserMessage(user, sendUserMessage); + } + }else if (message.hasBanusermessage()) { + const banUserMessage = message.getBanusermessage(); + if(banUserMessage !== undefined) { + socketManager.handlerBanUserMessage(room, user, banUserMessage); + } } else { throw new Error('Unhandled message type'); } @@ -196,8 +203,8 @@ const roomManager: IRoomManagerServer = { callback(null, new EmptyMessage()); }, ban(call: ServerUnaryCall, callback: sendUnaryData): void { - - socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid()); + // FIXME Work in progress + socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid(), 'foo bar TODO change this'); callback(null, new EmptyMessage()); }, diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index bfc43050..c90b51cf 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -1,24 +1,16 @@ import {GameRoom} from "../Model/GameRoom"; import {CharacterLayer} from "_Model/Websocket/CharacterLayer"; import { - GroupDeleteMessage, - GroupUpdateMessage, ItemEventMessage, ItemStateMessage, PlayGlobalMessage, PointMessage, - PositionMessage, RoomJoinedMessage, ServerToClientMessage, - SetPlayerDetailsMessage, SilentMessage, SubMessage, - ReportPlayerMessage, - UserJoinedMessage, - UserLeftMessage, UserMovedMessage, UserMovesMessage, - ViewportMessage, WebRtcDisconnectMessage, WebRtcSignalToClientMessage, WebRtcSignalToServerMessage, @@ -28,24 +20,23 @@ import { SendUserMessage, JoinRoomMessage, Zone as ProtoZone, - BatchMessage, BatchToPusherMessage, SubToPusherMessage, - UserJoinedZoneMessage, GroupUpdateZoneMessage, GroupLeftZoneMessage, UserLeftZoneMessage, AdminMessage, BanMessage + UserJoinedZoneMessage, GroupUpdateZoneMessage, GroupLeftZoneMessage, UserLeftZoneMessage, BanUserMessage } from "../Messages/generated/messages_pb"; import {User, UserSocket} from "../Model/User"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; import {Group} from "../Model/Group"; import {cpuTracker} from "./CpuTracker"; -import {ADMIN_API_URL, GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable"; +import {GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable"; import {Movable} from "../Model/Movable"; import {PositionInterface} from "../Model/PositionInterface"; -import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "./AdminApi"; +import {adminApi, CharacterTexture} from "./AdminApi"; import Jwt from "jsonwebtoken"; import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {clientEventsEmitter} from "./ClientEventsEmitter"; import {gaugeManager} from "./GaugeManager"; -import {AdminSocket, ZoneSocket} from "../RoomManager"; +import {ZoneSocket} from "../RoomManager"; import {Zone} from "_Model/Zone"; import Debug from "debug"; import {Admin} from "_Model/Admin"; @@ -119,7 +110,7 @@ export class SocketManager { //const things = room.setViewport(client, viewport); const roomJoinedMessage = new RoomJoinedMessage(); - + roomJoinedMessage.setTagList(joinRoomMessage.getTagList()); /*for (const thing of things) { if (thing instanceof User) { const player: ExSocketInterface|undefined = this.sockets.get(thing.id); @@ -626,6 +617,33 @@ export class SocketManager { user.socket.write(serverToClientMessage); } + public handlerSendUserMessage(user: User, sendUserMessageToSend: SendUserMessage){ + const sendUserMessage = new SendUserMessage(); + sendUserMessage.setMessage(sendUserMessageToSend.getMessage()); + sendUserMessage.setType(sendUserMessageToSend.getType()); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendusermessage(sendUserMessage); + user.socket.write(serverToClientMessage); + } + + public handlerBanUserMessage(room: GameRoom, user: User, banUserMessageToSend: BanUserMessage){ + const banUserMessage = new BanUserMessage(); + banUserMessage.setMessage(banUserMessageToSend.getMessage()); + banUserMessage.setType(banUserMessageToSend.getType()); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendusermessage(banUserMessage); + user.socket.write(serverToClientMessage); + + setTimeout(() => { + // Let's leave the room now. + room.leave(user); + // Let's close the connection when the user is banned. + user.socket.end(); + }, 10000); + } + /** * Merges the characterLayers received from the front (as an array of string) with the custom textures from the back. */ @@ -748,7 +766,7 @@ export class SocketManager { recipient.socket.write(subToPusherMessage); } - public banUser(roomId: string, recipientUuid: string): void { + public banUser(roomId: string, recipientUuid: string, message: 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?"); @@ -765,6 +783,7 @@ export class SocketManager { room.leave(recipient); const sendUserMessage = new SendUserMessage(); + sendUserMessage.setMessage(message); sendUserMessage.setType('banned'); const subToPusherMessage = new SubToPusherMessage(); diff --git a/back/tests/GameRoomTest.ts b/back/tests/GameRoomTest.ts index 3dc9aada..45721334 100644 --- a/back/tests/GameRoomTest.ts +++ b/back/tests/GameRoomTest.ts @@ -26,6 +26,7 @@ function createJoinRoomMessage(uuid: string, x: number, y: number): JoinRoomMess positionMessage.setMoving(false); const joinRoomMessage = new JoinRoomMessage(); joinRoomMessage.setUseruuid('1'); + joinRoomMessage.setIpaddress('10.0.0.2'); joinRoomMessage.setName('foo'); joinRoomMessage.setRoomid('_/global/test.json'); joinRoomMessage.setPositionmessage(positionMessage); diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 94b49ab7..5901202f 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -25,14 +25,14 @@ describe("PositionNotifier", () => { leaveTriggered = true; }); - const user1 = new User(1, 'test', { + const user1 = new User(1, 'test', '10.0.0.2', { x: 500, y: 500, moving: false, direction: 'down' }, false, positionNotifier, {} as UserSocket, [], 'foo', []); - const user2 = new User(2, 'test', { + const user2 = new User(2, 'test', '10.0.0.2', { x: -9999, y: -9999, moving: false, @@ -100,14 +100,14 @@ describe("PositionNotifier", () => { leaveTriggered = true; }); - const user1 = new User(1, 'test', { + const user1 = new User(1, 'test', '10.0.0.2', { x: 500, y: 500, moving: false, direction: 'down' }, false, positionNotifier, {} as UserSocket, [], 'foo', []); - const user2 = new User(2, 'test', { + const user2 = new User(2, 'test', '10.0.0.2', { x: 0, y: 0, moving: false, diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index d9bfe905..72c3b42e 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -25,7 +25,7 @@ export class Room { this.id = this.id.substr(0, indexOfHash); } } - + public static getIdFromIdentifier(identifier: string, baseUrl: string, currentInstance: string): {roomId: string, hash: string} { let roomId = ''; let hash = ''; @@ -72,8 +72,9 @@ export class Room { console.log('Map ', this.id, ' resolves to URL ', data.mapUrl); resolve(data.mapUrl); return; + }).catch((reason) => { + reject(reason); }); - } }); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index d25c2893..c2cb0950 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -45,7 +45,6 @@ import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import {GameMap} from "./GameMap"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {mediaManager} from "../../WebRtc/MediaManager"; -import {FourOFourSceneName} from "../Reconnecting/FourOFourScene"; import {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; import {ActionableItem} from "../Items/ActionableItem"; import {UserInputManager} from "../UserInput/UserInputManager"; @@ -67,6 +66,7 @@ import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene"; import {TextureError} from "../../Exception/TextureError"; import {addLoader} from "../Components/Loader"; +import {ErrorSceneName} from "../Reconnecting/ErrorScene"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -185,8 +185,10 @@ export class GameScene extends ResizableScene implements CenterListener { this.load.image(openChatIconName, 'resources/objects/talk.png'); this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { - this.scene.start(FourOFourSceneName, { - file: file.src + this.scene.start(ErrorSceneName, { + title: 'Network error', + subTitle: 'An error occurred while loading resource:', + message: file.src }); }); this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => { @@ -375,7 +377,7 @@ export class GameScene extends ResizableScene implements CenterListener { //notify game manager can to create currentUser in map this.createCurrentPlayer(); this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted - + this.initCamera(); this.initCirclesCanvas(); @@ -1039,7 +1041,7 @@ export class GameScene extends ResizableScene implements CenterListener { event: addPlayerData }); } - + private doAddPlayer(addPlayerData : AddPlayerInterface): void { //check if exist player, if exist, move position if(this.MapPlayersByKey.has(addPlayerData.userId)){ @@ -1192,7 +1194,7 @@ export class GameScene extends ResizableScene implements CenterListener { // Let's put this in Game coordinates by applying the zoom level: xCenter /= ZOOM_LEVEL * RESOLUTION; yCenter /= ZOOM_LEVEL * RESOLUTION; - + this.cameras.main.startFollow(this.CurrentPlayer, true, 1, 1, xCenter - this.game.renderer.width / 2, yCenter - this.game.renderer.height / 2); } diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index c7527349..75ee0272 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -1,7 +1,7 @@ import {gameManager} from "../Game/GameManager"; import {Scene} from "phaser"; -import {LoginSceneName} from "./LoginScene"; -import {FourOFourSceneName} from "../Reconnecting/FourOFourScene"; +import {ErrorScene} from "../Reconnecting/ErrorScene"; +import {WAError} from "../Reconnecting/WAError"; export const EntrySceneName = "EntryScene"; @@ -20,10 +20,11 @@ export class EntryScene extends Scene { gameManager.init(this.scene).then((nextSceneName) => { this.scene.start(nextSceneName); }).catch((err) => { - console.error(err) - this.scene.start(FourOFourSceneName, { - url: window.location.pathname.toString() - }); + if (err.response && err.response.status == 404) { + ErrorScene.showError(new WAError('Page Not Found', 'Could not find map', window.location.pathname), this.scene); + } else { + ErrorScene.showError(err, this.scene); + } }); } } diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts new file mode 100644 index 00000000..921eeb58 --- /dev/null +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -0,0 +1,120 @@ +import {TextField} from "../Components/TextField"; +import Image = Phaser.GameObjects.Image; +import Sprite = Phaser.GameObjects.Sprite; +import Text = Phaser.GameObjects.Text; +import ScenePlugin = Phaser.Scenes.ScenePlugin; +import {WAError} from "./WAError"; + +export const ErrorSceneName = "ErrorScene"; +enum Textures { + icon = "icon", + mainFont = "main_font" +} + +export class ErrorScene extends Phaser.Scene { + private titleField!: TextField; + private subTitleField!: TextField; + private messageField!: Text; + private logo!: Image; + private cat!: Sprite; + private title!: string; + private subTitle!: string; + private message!: string; + + constructor() { + super({ + key: ErrorSceneName + }); + } + + init({title, subTitle, message}: { title?: string, subTitle?: string, message?: string }) { + this.title = title ? title : ''; + this.subTitle = subTitle ? subTitle : ''; + this.message = message ? message : ''; + } + + preload() { + this.load.image(Textures.icon, "resources/logos/tcm_full.png"); + // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap + this.load.bitmapFont(Textures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + this.load.spritesheet( + 'cat', + 'resources/characters/pipoya/Cat 01-1.png', + {frameWidth: 32, frameHeight: 32} + ); + } + + create() { + this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, Textures.icon); + this.add.existing(this.logo); + + this.titleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, this.title); + + this.subTitleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, this.subTitle); + + this.messageField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 38, this.message, { + fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif', + fontSize: '10px' + }); + this.messageField.setOrigin(0.5, 0.5); + + this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat', 6); + this.cat.flipY = true; + } + + /** + * Displays the error page, with an error message matching the "error" parameters passed in. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public static showError(error: any, scene: ScenePlugin): void { + console.error(error); + + if (typeof error === 'string' || error instanceof String) { + scene.start(ErrorSceneName, { + title: 'An error occurred', + subTitle: error + }); + } else if (error instanceof WAError) { + scene.start(ErrorSceneName, { + title: error.title, + subTitle: error.subTitle, + message: error.details + }); + } else if (error.response) { + // Axios HTTP error + // client received an error response (5xx, 4xx) + scene.start(ErrorSceneName, { + title: 'HTTP ' + error.response.status + ' - ' + error.response.statusText, + subTitle: 'An error occurred while accessing URL:', + message: error.response.config.url + }); + } else if (error.request) { + // Axios HTTP error + // client never received a response, or request never left + scene.start(ErrorSceneName, { + title: 'Network error', + subTitle: error.message + }); + } else if (error instanceof Error) { + // Error + scene.start(ErrorSceneName, { + title: 'An error occurred', + subTitle: error.name, + message: error.message + }); + } else { + throw error; + } + } + + /** + * Displays the error page, with an error message matching the "error" parameters passed in. + */ + public static startErrorPage(title: string, subTitle: string, message: string, scene: ScenePlugin): void { + scene.start(ErrorSceneName, { + title, + subTitle, + message + }); + } +} diff --git a/front/src/Phaser/Reconnecting/FourOFourScene.ts b/front/src/Phaser/Reconnecting/FourOFourScene.ts deleted file mode 100644 index 36106796..00000000 --- a/front/src/Phaser/Reconnecting/FourOFourScene.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {TextField} from "../Components/TextField"; -import Image = Phaser.GameObjects.Image; -import Sprite = Phaser.GameObjects.Sprite; -import Text = Phaser.GameObjects.Text; - -export const FourOFourSceneName = "FourOFourScene"; -enum Textures { - icon = "icon", - mainFont = "main_font" -} - -export class FourOFourScene extends Phaser.Scene { - private mapNotFoundField!: TextField; - private couldNotFindField!: TextField; - private fileNameField!: Text; - private logo!: Image; - private cat!: Sprite; - private file: string|undefined; - private url: string|undefined; - - constructor() { - super({ - key: FourOFourSceneName - }); - } - - init({ file, url }: { file?: string, url?: string }) { - this.file = file; - this.url = url; - } - - preload() { - this.load.image(Textures.icon, "resources/logos/tcm_full.png"); - // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap - this.load.bitmapFont(Textures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); - this.load.spritesheet( - 'cat', - 'resources/characters/pipoya/Cat 01-1.png', - {frameWidth: 32, frameHeight: 32} - ); - } - - create() { - this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, Textures.icon); - this.add.existing(this.logo); - - this.mapNotFoundField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, "404 - File not found"); - - let text: string = ''; - if (this.file !== undefined) { - text = "Could not load map" - } - if (this.url !== undefined) { - text = "Invalid URL" - } - - this.couldNotFindField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, text); - - const url = this.file ? this.file : this.url; - if (url !== undefined) { - this.fileNameField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 38, url, { fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif', fontSize: '10px' }); - this.fileNameField.setOrigin(0.5, 0.5); - } - - this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat', 6); - this.cat.flipY=true; - } -} diff --git a/front/src/Phaser/Reconnecting/WAError.ts b/front/src/Phaser/Reconnecting/WAError.ts new file mode 100644 index 00000000..cdc433b6 --- /dev/null +++ b/front/src/Phaser/Reconnecting/WAError.ts @@ -0,0 +1,26 @@ +export class WAError extends Error { + private _title: string; + private _subTitle: string; + private _details: string; + + constructor (title: string, subTitle: string, details: string) { + super(title+' - '+subTitle+' - '+details); + this._title = title; + this._subTitle = subTitle; + this._details = details; + // Set the prototype explicitly. + Object.setPrototypeOf (this, WAError.prototype); + } + + get title(): string { + return this._title; + } + + get subTitle(): string { + return this._subTitle; + } + + get details(): string { + return this._details; + } +} diff --git a/front/src/index.ts b/front/src/index.ts index abca6036..acf66cf8 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -6,7 +6,6 @@ import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; -import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene"; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; @@ -15,6 +14,7 @@ import {EntryScene} from "./Phaser/Login/EntryScene"; import {coWebsiteManager} from "./WebRtc/CoWebsiteManager"; import {MenuScene} from "./Phaser/Menu/MenuScene"; import {localUserStore} from "./Connexion/LocalUserStore"; +import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene"; // Load Jitsi if the environment variable is set. if (JITSI_URL) { @@ -59,7 +59,7 @@ const config: GameConfig = { width: width / RESOLUTION, height: height / RESOLUTION, parent: "game", - scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, FourOFourScene, CustomizeScene, MenuScene], + scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, ErrorScene, CustomizeScene, MenuScene], zoom: RESOLUTION, fps: fps, dom: { diff --git a/maps/Floor0/floor0.json b/maps/Floor0/floor0.json index 6a88f78d..3bbd67cd 100644 --- a/maps/Floor0/floor0.json +++ b/maps/Floor0/floor0.json @@ -278,7 +278,7 @@ { "name":"exitUrl", "type":"string", - "value":"..\/Floor1\/floor1.json" + "value":"\/@\/tcm\/workadventure\/floor1" }], "type":"tilelayer", "visible":true, diff --git a/maps/Floor1/floor1.json b/maps/Floor1/floor1.json index 99f53093..6deb3bf5 100644 --- a/maps/Floor1/floor1.json +++ b/maps/Floor1/floor1.json @@ -85,7 +85,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"..\/Floor0\/floor0.json#down-the-stairs" + "value":"\/@\/tcm\/workadventure\/floor0#down-the-stairs" }], "type":"tilelayer", "visible":true, @@ -103,7 +103,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"..\/Floor2\/floor2.json#down-the-stairs" + "value":"\/@\/tcm\/workadventure\/floor2#down-the-stairs" }], "type":"tilelayer", "visible":true, @@ -121,7 +121,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"..\/Floor2\/floor2.json#down-the-stairs-secours" + "value":"\/@\/tcm\/workadventure\/floor2#down-the-stairs-secours" }], "type":"tilelayer", "visible":true, diff --git a/maps/Floor2/floor2.json b/maps/Floor2/floor2.json index a256e5f3..35454404 100644 --- a/maps/Floor2/floor2.json +++ b/maps/Floor2/floor2.json @@ -103,7 +103,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"..\/Floor1\/floor1.json#down-the-stairs" + "value":"\/@\/tcm\/workadventure\/floor1#down-the-stairs" }], "type":"tilelayer", "visible":true, @@ -139,7 +139,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"..\/Floor1\/floor1.json#down-the-stairs-secours" + "value":"\/@\/tcm\/workadventure\/floor1#down-the-stairs-secours" }], "type":"tilelayer", "visible":true, diff --git a/maps/Lyon/lyon.json b/maps/Lyon/lyon.json index 0ed62a9c..ddaf8ea8 100644 --- a/maps/Lyon/lyon.json +++ b/maps/Lyon/lyon.json @@ -37,7 +37,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"..\/Floor0\/floor0.json" + "value":"\/@\/tcm\/workadventure\/floor0" }], "type":"tilelayer", "visible":true, diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 24db45d5..39f575be 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -193,6 +193,11 @@ message SendUserMessage{ string message = 2; } +message BanUserMessage{ + string type = 1; + string message = 2; +} + message ServerToClientMessage { oneof message { BatchMessage batchMessage = 1; @@ -207,6 +212,7 @@ message ServerToClientMessage { TeleportMessageMessage teleportMessageMessage = 10; SendJitsiJwtMessage sendJitsiJwtMessage = 11; SendUserMessage sendUserMessage = 12; + BanUserMessage banUserMessage = 13; } } @@ -220,6 +226,7 @@ message JoinRoomMessage { string userUuid = 4; string roomId = 5; repeated string tag = 6; + string IPAddress = 7; } message UserJoinedZoneMessage { @@ -272,6 +279,8 @@ message PusherToBackMessage { StopGlobalMessage stopGlobalMessage = 9; ReportPlayerMessage reportPlayerMessage = 10; QueryJitsiJwtMessage queryJitsiJwtMessage = 11; + SendUserMessage sendUserMessage = 12; + BanUserMessage banUserMessage = 13; } } @@ -288,6 +297,7 @@ message SubToPusherMessage { UserLeftZoneMessage userLeftZoneMessage = 5; ItemEventMessage itemEventMessage = 6; SendUserMessage sendUserMessage = 7; + BanUserMessage banUserMessage = 8; } } @@ -306,10 +316,20 @@ message ServerToAdminClientMessage { repeated SubToAdminPusherMessage payload = 2; }*/ +message UserJoinedRoomMessage { + string uuid = 1; + string ipAddress = 2; + string name = 3; +} + +message UserLeftRoomMessage { + string uuid = 1; +} + message ServerToAdminClientMessage { oneof message { - string userUuidJoinedRoom = 1; - string userUuidLeftRoom = 2; + UserJoinedRoomMessage userJoinedRoom = 1; + UserLeftRoomMessage userLeftRoom = 2; } } diff --git a/pusher/package.json b/pusher/package.json index 09d6794a..7edf3d5d 100644 --- a/pusher/package.json +++ b/pusher/package.json @@ -10,8 +10,8 @@ "runprod": "node --max-old-space-size=4096 ./dist/server.js", "profile": "tsc && node --prof ./dist/server.js", "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", - "lint": "node_modules/.bin/eslint src/ . --ext .ts", - "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts" + "lint": "DEBUG= node_modules/.bin/eslint src/ . --ext .ts", + "fix": "DEBUG= node_modules/.bin/eslint --fix src/ . --ext .ts" }, "repository": { "type": "git", diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index bf68768d..f82974b6 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -60,10 +60,7 @@ export class AuthenticateController extends BaseController { })); } catch (e) { - console.error("An error happened", e) - res.writeStatus(e.status || "500 Internal Server Error"); - this.addCorsHeaders(res); - res.end('An error happened'); + this.errorToResponse(e, res); } diff --git a/pusher/src/Controller/BaseController.ts b/pusher/src/Controller/BaseController.ts index 0b744082..673ffd3c 100644 --- a/pusher/src/Controller/BaseController.ts +++ b/pusher/src/Controller/BaseController.ts @@ -8,4 +8,21 @@ export class BaseController { res.writeHeader('access-control-allow-methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); res.writeHeader('access-control-allow-origin', '*'); } + + /** + * Turns any exception into a HTTP response (and logs the error) + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + protected errorToResponse(e: any, res: HttpResponse): void { + console.error("An error happened", e); + if (e.response) { + res.writeStatus(e.response.status+" "+e.response.statusText); + this.addCorsHeaders(res); + res.end("An error occurred: "+e.response.status+" "+e.response.statusText); + } else { + res.writeStatus("500 Internal Server Error") + this.addCorsHeaders(res); + res.end("An error occurred"); + } + } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 58cf05fb..a43f43ba 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -91,18 +91,11 @@ export class IoSocketController { if(message.event === 'user-message') { const messageToEmit = (message.message as { message: string, type: string, userUuid: string }); - switch (message.message.type) { - case 'ban': { - socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, roomId); - break; - } - case 'banned': { - socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, roomId); - break; - } - default: { - break; - } + if(messageToEmit.type === 'banned'){ + socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type); + } + if(messageToEmit.type === 'ban') { + socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type); } } }catch (err) { @@ -148,6 +141,7 @@ export class IoSocketController { const websocketKey = req.getHeader('sec-websocket-key'); const websocketProtocol = req.getHeader('sec-websocket-protocol'); const websocketExtensions = req.getHeader('sec-websocket-extensions'); + const IPAddress = req.getHeader('x-forwarded-for'); const roomId = query.roomId; if (typeof roomId !== 'string') { @@ -176,7 +170,7 @@ export class IoSocketController { characterLayers = [ characterLayers ]; } - const userUuid = await jwtTokenManager.getUserUuidFromToken(token); + const userUuid = await jwtTokenManager.getUserUuidFromToken(token, IPAddress, roomId); let memberTags: string[] = []; let memberTextures: CharacterTexture[] = []; @@ -217,6 +211,7 @@ export class IoSocketController { url, token, userUuid, + IPAddress, roomId, name, characterLayers: characterLayerObjs, @@ -336,6 +331,7 @@ export class IoSocketController { client.userId = this.nextUserId; this.nextUserId++; client.userUuid = ws.userUuid; + client.IPAddress = ws.IPAddress; client.token = ws.token; client.batchedMessages = new BatchMessage(); client.batchTimeout = null; diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index c111ba64..1ce04265 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -59,10 +59,7 @@ export class MapController extends BaseController{ this.addCorsHeaders(res); res.end(JSON.stringify(mapDetails)); } catch (e) { - console.error(e.message || e); - res.writeStatus("500 Internal Server Error") - this.addCorsHeaders(res); - res.end("An error occurred"); + this.errorToResponse(e, res); } })(); diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index b8ee4bc8..85cfad48 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -24,6 +24,7 @@ export interface ExSocketInterface extends WebSocket, Identificable { roomId: string; //userId: number; // A temporary (autoincremented) identifier for this user userUuid: string; // A unique identifier for this user + IPAddress: string; // IP address name: string; characterLayers: CharacterLayer[]; position: PointInterface; diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 9c46a41b..48e8a1a4 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -14,6 +14,11 @@ export interface AdminApiData { textures: CharacterTexture[] } +export interface AdminBannedData { + is_banned: boolean, + message: string +} + export interface CharacterTexture { id: number, level: number, @@ -110,6 +115,18 @@ class AdminApi { headers: {"Authorization": `${ADMIN_API_TOKEN}`} }); } + + async verifyBanUser(organizationMemberToken: string, ipAddress: string, organization: string, world: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case. + return Axios.get(ADMIN_API_URL + '/api/check-moderate-user/'+organization+'/'+world+'?ipAddress='+ipAddress+'&token='+organizationMemberToken, + {headers: {"Authorization": `${ADMIN_API_TOKEN}`}} + ).then((data) => { + return data.data; + }); + } } export const adminApi = new AdminApi(); diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 8abb0e45..68d5488a 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -2,7 +2,7 @@ import {ADMIN_API_URL, ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVar import {uuid} from "uuidv4"; import Jwt from "jsonwebtoken"; import {TokenInterface} from "../Controller/AuthenticateController"; -import {adminApi, AdminApiData} from "../Services/AdminApi"; +import {adminApi, AdminBannedData} from "../Services/AdminApi"; class JWTTokenManager { @@ -10,7 +10,7 @@ class JWTTokenManager { return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token } - public async getUserUuidFromToken(token: unknown): Promise { + public async getUserUuidFromToken(token: unknown, ipAddress?: string, room?: string): Promise { if (!token) { throw new Error('An authentication error happened, a user tried to connect without a token.'); @@ -50,14 +50,22 @@ class JWTTokenManager { if (ADMIN_API_URL) { //verify user in admin - adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { - resolve(tokenInterface.userUuid); - }).catch((err) => { - //anonymous user - if(err.response && err.response.status && err.response.status === 404){ + let promise = new Promise((resolve) => resolve()); + if(ipAddress && room) { + promise = this.verifyBanUser(tokenInterface.userUuid, ipAddress, room); + } + promise.then(() => { + adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { resolve(tokenInterface.userUuid); - return; - } + }).catch((err) => { + //anonymous user + if (err.response && err.response.status && err.response.status === 404) { + resolve(tokenInterface.userUuid); + return; + } + reject(err); + }); + }).catch((err) => { reject(err); }); } else { @@ -67,6 +75,27 @@ class JWTTokenManager { }); } + private verifyBanUser(userUuid: string, ipAddress: string, room: string): Promise { + const parts = room.split('/'); + if (parts.length < 3 || parts[0] !== '@') { + return Promise.resolve({ + is_banned: false, + message: '' + }); + } + + const organization = parts[1]; + const world = parts[2]; + return adminApi.verifyBanUser(userUuid, ipAddress, organization, world).then((data: AdminBannedData) => { + if (data && data.is_banned) { + throw new Error('User was banned'); + } + return data; + }).catch((err) => { + throw err; + }); + } + private isValidToken(token: object): token is TokenInterface { return !(typeof((token as TokenInterface).userUuid) !== 'string'); } diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 6255ca20..2f86ae19 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -2,11 +2,8 @@ import {PusherRoom} from "../Model/PusherRoom"; import {CharacterLayer, ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; import { GroupDeleteMessage, - GroupUpdateMessage, ItemEventMessage, - ItemStateMessage, PlayGlobalMessage, - PointMessage, PositionMessage, RoomJoinedMessage, ServerToClientMessage, @@ -14,23 +11,19 @@ import { SilentMessage, SubMessage, ReportPlayerMessage, - UserJoinedMessage, UserLeftMessage, - UserMovedMessage, UserMovesMessage, ViewportMessage, - WebRtcDisconnectMessage, - WebRtcSignalToClientMessage, WebRtcSignalToServerMessage, - WebRtcStartMessage, QueryJitsiJwtMessage, SendJitsiJwtMessage, - SendUserMessage, JoinRoomMessage, CharacterLayerMessage, PusherToBackMessage, AdminPusherToBackMessage, - ServerToAdminClientMessage, AdminMessage, BanMessage + ServerToAdminClientMessage, + SendUserMessage, + BanUserMessage, UserJoinedRoomMessage, UserLeftRoomMessage } from "../Messages/generated/messages_pb"; import {PointInterface} from "../Model/Websocket/PointInterface"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; @@ -79,23 +72,33 @@ export class SocketManager implements ZoneEventListener { } async handleAdminRoom(client: ExAdminSocketInterface, roomId: string): Promise { - console.log('Calling adminRoom') const apiClient = await apiClientRepository.getClient(roomId); const adminRoomStream = apiClient.adminRoom(); client.adminConnection = adminRoomStream; adminRoomStream.on('data', (message: ServerToAdminClientMessage) => { - if (message.hasUseruuidjoinedroom()) { - const userUuid = message.getUseruuidjoinedroom(); - + if (message.hasUserjoinedroom()) { + const userJoinedRoomMessage = message.getUserjoinedroom() as UserJoinedRoomMessage; if (!client.disconnecting) { - client.send('MemberJoin:'+userUuid+';'+roomId); + client.send(JSON.stringify({ + type: 'MemberJoin', + data: { + uuid: userJoinedRoomMessage.getUuid(), + name: userJoinedRoomMessage.getName(), + ipAddress: userJoinedRoomMessage.getIpaddress(), + roomId: roomId, + } + })); } - } else if (message.hasUseruuidleftroom()) { - const userUuid = message.getUseruuidleftroom(); - + } else if (message.hasUserleftroom()) { + const userLeftRoomMessage = message.getUserleftroom() as UserLeftRoomMessage; if (!client.disconnecting) { - client.send('MemberLeave:'+userUuid+';'+roomId); + client.send(JSON.stringify({ + type: 'MemberLeave', + data: { + uuid: userLeftRoomMessage.getUuid() + } + })); } } else { throw new Error('Unexpected admin message'); @@ -145,15 +148,16 @@ export class SocketManager implements ZoneEventListener { } async handleJoinRoom(client: ExSocketInterface): Promise { - const position = client.position; const viewport = client.viewport; try { const joinRoomMessage = new JoinRoomMessage(); joinRoomMessage.setUseruuid(client.userUuid); + joinRoomMessage.setIpaddress(client.IPAddress); joinRoomMessage.setRoomid(client.roomId); joinRoomMessage.setName(client.name); joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position)); + joinRoomMessage.setTagList(client.tags); for (const characterLayer of client.characterLayers) { const characterLayerMessage = new CharacterLayerMessage(); characterLayerMessage.setName(characterLayer.name); @@ -540,51 +544,54 @@ export class SocketManager implements ZoneEventListener { client.send(serverToClientMessage.serializeBinary().buffer, true); } - public async emitSendUserMessage(userUuid: string, message: string, roomId: string): Promise { + public emitSendUserMessage(userUuid: string, message: string, type: string): void { + const client = this.searchClientByUuid(userUuid); + if(!client){ + throw Error('client not found'); + } - const backConnection = await apiClientRepository.getClient(roomId); - - const adminMessage = new AdminMessage(); - adminMessage.setRecipientuuid(userUuid); + const adminMessage = new SendUserMessage(); adminMessage.setMessage(message); - adminMessage.setRoomid(roomId); + adminMessage.setType(type); + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setSendusermessage(adminMessage); + client.backConnection.write(pusherToBackMessage); + /*const backConnection = await apiClientRepository.getClient(client.roomId); + const adminMessage = new AdminMessage(); + adminMessage.setMessage(message); + adminMessage.setRoomid(client.roomId); + adminMessage.setRecipientuuid(client.userUuid); 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'; - } - - 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;*/ + });*/ } - public async emitBan(userUuid: string, message: string, roomId: string): Promise { - const backConnection = await apiClientRepository.getClient(roomId); + public emitBan(userUuid: string, message: string, type: string): void { + const client = this.searchClientByUuid(userUuid); + if(!client){ + throw Error('client not found'); + } - const banMessage = new BanMessage(); - banMessage.setRecipientuuid(userUuid); - banMessage.setRoomid(roomId); + const banUserMessage = new BanUserMessage(); + banUserMessage.setMessage(message); + banUserMessage.setType(type); + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setBanusermessage(banUserMessage); + client.backConnection.write(pusherToBackMessage); - backConnection.ban(banMessage, (error) => { + /*const backConnection = await apiClientRepository.getClient(client.roomId); + const adminMessage = new AdminMessage(); + adminMessage.setMessage(message); + adminMessage.setRoomid(client.roomId); + adminMessage.setRecipientuuid(client.userUuid); + backConnection.sendAdminMessage(adminMessage, (error) => { if (error !== null) { - console.error('Error while sending ban message', error); + console.error('Error while sending admin message', error); } - }); + });*/ } /**