diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index b12f0542..8736a856 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -3,7 +3,6 @@ const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false; const ADMIN_API_URL = process.env.ADMIN_API_URL || ''; const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || 'myapitoken'; -const MAX_USERS_PER_ROOM = parseInt(process.env.MAX_USERS_PER_ROOM || '') || 600; const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80; const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; const JITSI_ISS = process.env.JITSI_ISS || ''; @@ -19,7 +18,6 @@ export { ADMIN_API_TOKEN, HTTP_PORT, GRPC_PORT, - MAX_USERS_PER_ROOM, GROUP_RADIUS, ALLOW_ARTILLERY, CPU_OVERHEAT_THRESHOLD, diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 41c215ad..6a592ed0 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -7,7 +7,6 @@ import {PositionNotifier} from "./PositionNotifier"; import {Movable} from "_Model/Movable"; import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier"; import {arrayIntersect} from "../Services/ArrayHelper"; -import {MAX_USERS_PER_ROOM} from "../Enum/EnvironmentVariable"; import {JoinRoomMessage} from "../Messages/generated/messages_pb"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; import {ZoneSocket} from "src/RoomManager"; @@ -116,8 +115,6 @@ export class GameRoom { 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); // Notify admins @@ -149,10 +146,6 @@ export class GameRoom { } } - get isFull(): boolean { - return this.users.size >= MAX_USERS_PER_ROOM; - } - public isEmpty(): boolean { return this.users.size === 0 && this.admins.size === 0; } diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 4302a141..256c904e 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -176,7 +176,7 @@ const roomManager: IRoomManagerServer = { }, ban(call: ServerUnaryCall, callback: sendUnaryData): void { // FIXME Work in progress - socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid(), 'foo bar TODO change this'); + socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid(), call.request.getMessage()); callback(null, new EmptyMessage()); }, diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 907036e1..6317a46d 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -690,7 +690,7 @@ 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?"); + 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; } @@ -704,10 +704,10 @@ export class SocketManager { sendUserMessage.setMessage(message); sendUserMessage.setType('ban'); //todo: is the type correct? - const subToPusherMessage = new SubToPusherMessage(); - subToPusherMessage.setSendusermessage(sendUserMessage); + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendusermessage(sendUserMessage); - recipient.socket.write(subToPusherMessage); + recipient.socket.write(serverToClientMessage); } public banUser(roomId: string, recipientUuid: string, message: string): void { @@ -726,16 +726,15 @@ export class SocketManager { // Let's leave the room now. room.leave(recipient); - const sendUserMessage = new SendUserMessage(); - sendUserMessage.setMessage(message); - sendUserMessage.setType('banned'); + const banUserMessage = new BanUserMessage(); + banUserMessage.setMessage(message); + banUserMessage.setType('banned'); - const subToPusherMessage = new SubToPusherMessage(); - subToPusherMessage.setSendusermessage(sendUserMessage); - - recipient.socket.write(subToPusherMessage); + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setBanusermessage(banUserMessage); // Let's close the connection when the user is banned. + recipient.socket.write(serverToClientMessage); recipient.socket.end(); } diff --git a/front/src/Administration/TypeMessage.ts b/front/src/Administration/TypeMessage.ts index 61891604..182f7e01 100644 --- a/front/src/Administration/TypeMessage.ts +++ b/front/src/Administration/TypeMessage.ts @@ -77,8 +77,10 @@ export class TypeMessageExt implements TypeMessageInterface{ } } } -export class Ban extends TypeMessageExt { -} + +export class Message extends TypeMessageExt {} + +export class Ban extends TypeMessageExt {} export class Banned extends TypeMessageExt { showMessage(message: string){ diff --git a/front/src/Connexion/AdminMessagesService.ts b/front/src/Connexion/AdminMessagesService.ts index a1e7fc21..7f96a673 100644 --- a/front/src/Connexion/AdminMessagesService.ts +++ b/front/src/Connexion/AdminMessagesService.ts @@ -1,5 +1,5 @@ import {Subject} from "rxjs"; -import {SendUserMessage} from "../Messages/generated/messages_pb"; +import {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb"; export enum AdminMessageEventTypes { admin = 'message', @@ -23,7 +23,7 @@ class AdminMessagesService { this.messageStream.subscribe((event) => console.log('message', event)) } - onSendusermessage(message: SendUserMessage) { + onSendusermessage(message: SendUserMessage|BanUserMessage) { this._messageStream.next({ type: message.getType() as unknown as AdminMessageEventTypes, text: message.getMessage(), diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index de8cd234..0b6e9ff1 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -6,11 +6,22 @@ import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; import {localUserStore} from "./LocalUserStore"; import {LocalUser} from "./LocalUser"; import {Room} from "./Room"; +import {Subject} from "rxjs"; + +export enum ConnexionMessageEventTypes { + worldFull = 1, +} + +export interface ConnexionMessageEvent { + type: ConnexionMessageEventTypes, +} class ConnectionManager { private localUser!:LocalUser; private connexionType?: GameConnexionTypes + + public _connexionMessageStream:Subject = new Subject(); /** * Tries to login to the node server and return the starting map url to be loaded */ diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index a0ae3119..519afcd3 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -1,4 +1,4 @@ -import {PlayerAnimationNames} from "../Phaser/Player/Animation"; +import {PlayerAnimationDirections} from "../Phaser/Player/Animation"; import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import {SignalData} from "simple-peer"; import {RoomConnection} from "./RoomConnection"; @@ -42,14 +42,6 @@ export interface PointInterface { moving: boolean; } -export class Point implements PointInterface{ - constructor(public x : number, public y : number, public direction : string = PlayerAnimationNames.WalkDown, public moving : boolean = false) { - if(x === null || y === null){ - throw Error("position x and y cannot be null"); - } - } -} - export interface MessageUserPositionInterface { userId: number; name: string; @@ -80,20 +72,10 @@ export interface GroupCreatedUpdatedMessageInterface { groupSize: number } -export interface WebRtcStartMessageInterface { - roomId: string, - clients: UserSimplePeerInterface[] -} - export interface WebRtcDisconnectMessageInterface { userId: number } -export interface WebRtcSignalSentMessageInterface { - receiverId: number, - signal: SignalData -} - export interface WebRtcSignalReceivedMessageInterface { userId: number, signal: SignalData, @@ -113,11 +95,6 @@ export interface ViewportInterface { bottom: number, } -export interface BatchedMessageInterface { - event: string, - payload: unknown -} - export interface ItemEventMessageInterface { itemId: number, event: string, diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 7d48946b..5951d078 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -27,7 +27,7 @@ import { SendJitsiJwtMessage, CharacterLayerMessage, PingMessage, - SendUserMessage + SendUserMessage, BanUserMessage } from "../Messages/generated/messages_pb" import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; @@ -43,6 +43,7 @@ import { } from "./ConnexionModels"; import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; import {adminMessagesService} from "./AdminMessagesService"; +import {connectionManager, ConnexionMessageEventTypes} from "./ConnectionManager"; const manualPingDelay = 20000; @@ -101,7 +102,7 @@ export class RoomConnection implements RoomConnection { } // If we are not connected yet (if a JoinRoomMessage was not sent), we need to retry. - if (this.userId === null) { + if (this.userId === null && !this.closed) { this.dispatch(EventMessage.CONNECTING_ERROR, event); } }); @@ -156,7 +157,8 @@ export class RoomConnection implements RoomConnection { } as RoomJoinedMessageInterface }); } else if (message.hasErrormessage()) { - console.error(EventMessage.MESSAGE_ERROR, message.getErrormessage()?.getMessage()); + connectionManager._connexionMessageStream.next({type: ConnexionMessageEventTypes.worldFull}); //todo: generalize this behavior to all messages + this.closed = true; } else if (message.hasWebrtcsignaltoclientmessage()) { this.dispatch(EventMessage.WEBRTC_SIGNAL, message.getWebrtcsignaltoclientmessage()); } else if (message.hasWebrtcscreensharingsignaltoclientmessage()) { @@ -175,6 +177,8 @@ export class RoomConnection implements RoomConnection { this.dispatch(EventMessage.START_JITSI_ROOM, message.getSendjitsijwtmessage()); } else if (message.hasSendusermessage()) { adminMessagesService.onSendusermessage(message.getSendusermessage() as SendUserMessage); + } else if (message.hasBanusermessage()) { + adminMessagesService.onSendusermessage(message.getSendusermessage() as BanUserMessage); } else { throw new Error('Unknown message received'); } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 67e21f10..797616f8 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -1,4 +1,4 @@ -import {PlayerAnimationNames} from "../Player/Animation"; +import {PlayerAnimationDirections, PlayerAnimationTypes} from "../Player/Animation"; import {SpeechBubble} from "./SpeechBubble"; import BitmapText = Phaser.GameObjects.BitmapText; import Container = Phaser.GameObjects.Container; @@ -10,8 +10,7 @@ interface AnimationData { frameRate: number; repeat: number; frameModel: string; //todo use an enum - frameStart: number; - frameEnd: number; + frames : number[] } export abstract class Character extends Container { @@ -19,7 +18,7 @@ export abstract class Character extends Container { private readonly playerName: BitmapText; public PlayerValue: string; public sprites: Map; - private lastDirection: string = PlayerAnimationNames.WalkDown; + private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; //private teleportation: Sprite; private invisible: boolean; @@ -28,7 +27,7 @@ export abstract class Character extends Container { y: number, texturesPromise: Promise, name: string, - direction: string, + direction: PlayerAnimationDirections, moving: boolean, frame?: string | number ) { @@ -81,7 +80,7 @@ export abstract class Character extends Container { this.getPlayerAnimations(texture).forEach(d => { this.scene.anims.create({ key: d.key, - frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}), + frames: this.scene.anims.generateFrameNumbers(d.frameModel, {frames: d.frames}), frameRate: d.frameRate, repeat: d.repeat }); @@ -96,37 +95,57 @@ export abstract class Character extends Container { private getPlayerAnimations(name: string): AnimationData[] { return [{ - key: `${name}-${PlayerAnimationNames.WalkDown}`, + key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`, frameModel: name, - frameStart: 0, - frameEnd: 2, + frames: [0, 1, 2, 1], frameRate: 10, repeat: -1 }, { - key: `${name}-${PlayerAnimationNames.WalkLeft}`, + key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`, frameModel: name, - frameStart: 3, - frameEnd: 5, + frames: [3, 4, 5, 4], frameRate: 10, repeat: -1 }, { - key: `${name}-${PlayerAnimationNames.WalkRight}`, + key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`, frameModel: name, - frameStart: 6, - frameEnd: 8, + frames: [6, 7, 8, 7], frameRate: 10, repeat: -1 }, { - key: `${name}-${PlayerAnimationNames.WalkUp}`, + key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`, frameModel: name, - frameStart: 9, - frameEnd: 11, + frames: [9, 10, 11, 10], frameRate: 10, repeat: -1 + },{ + key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [1], + frameRate: 10, + repeat: 1 + }, { + key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [4], + frameRate: 10, + repeat: 1 + }, { + key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [7], + frameRate: 10, + repeat: 1 + }, { + key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [10], + frameRate: 10, + repeat: 1 }]; } - protected playAnimation(direction : string, moving: boolean): void { + protected playAnimation(direction : PlayerAnimationDirections, moving: boolean): void { if (this.invisible) return; for (const [texture, sprite] of this.sprites.entries()) { if (!sprite.anims) { @@ -134,10 +153,9 @@ export abstract class Character extends Container { return; } if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) { - sprite.play(texture+'-'+direction, true); + sprite.play(texture+'-'+direction+'-'+PlayerAnimationTypes.Walk, true); } else if (!moving) { - sprite.anims.play(texture + '-' + direction, true); - sprite.anims.stop(); + sprite.anims.play(texture + '-' + direction + '-'+PlayerAnimationTypes.Idle, true); } } } @@ -157,17 +175,17 @@ export abstract class Character extends Container { // up or down animations are prioritized over left and right if (body.velocity.y < 0) { //moving up - this.lastDirection = PlayerAnimationNames.WalkUp; - this.playAnimation(PlayerAnimationNames.WalkUp, true); + this.lastDirection = PlayerAnimationDirections.Up; + this.playAnimation(PlayerAnimationDirections.Up, true); } else if (body.velocity.y > 0) { //moving down - this.lastDirection = PlayerAnimationNames.WalkDown; - this.playAnimation(PlayerAnimationNames.WalkDown, true); + this.lastDirection = PlayerAnimationDirections.Down; + this.playAnimation(PlayerAnimationDirections.Down, true); } else if (body.velocity.x > 0) { //moving right - this.lastDirection = PlayerAnimationNames.WalkRight; - this.playAnimation(PlayerAnimationNames.WalkRight, true); + this.lastDirection = PlayerAnimationDirections.Right; + this.playAnimation(PlayerAnimationDirections.Right, true); } else if (body.velocity.x < 0) { //moving left - this.lastDirection = PlayerAnimationNames.WalkLeft; - this.playAnimation(PlayerAnimationNames.WalkLeft, true); + this.lastDirection = PlayerAnimationDirections.Left; + this.playAnimation(PlayerAnimationDirections.Left, true); } this.setDepth(this.y); diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 81ea00e1..a6bd4f40 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -1,6 +1,7 @@ import {GameScene} from "../Game/GameScene"; import {PointInterface} from "../../Connexion/ConnexionModels"; import {Character} from "../Entity/Character"; +import {PlayerAnimationDirections} from "../Player/Animation"; /** * Class representing the sprite of a remote player (a player that plays on another computer) @@ -15,22 +16,17 @@ export class RemotePlayer extends Character { y: number, name: string, texturesPromise: Promise, - direction: string, + direction: PlayerAnimationDirections, moving: boolean ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1); //set data this.userId = userId; - - //todo: implement on click action - /*this.playerName.setInteractive(); - this.playerName.on('pointerup', () => { - });*/ } updatePosition(position: PointInterface): void { - this.playAnimation(position.direction, position.moving); + this.playAnimation(position.direction as PlayerAnimationDirections, position.moving); this.setX(position.x); this.setY(position.y); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index de7abd46..7efc2cab 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -3,27 +3,17 @@ import { GroupCreatedUpdatedMessageInterface, MessageUserJoined, MessageUserMovedInterface, - MessageUserPositionInterface, OnConnectInterface, + MessageUserPositionInterface, + OnConnectInterface, PointInterface, PositionInterface, RoomJoinedMessageInterface } from "../../Connexion/ConnexionModels"; import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player"; -import { - DEBUG_MODE, - JITSI_PRIVATE_MODE, - POSITION_DELAY, - RESOLUTION, - ZOOM_LEVEL -} from "../../Enum/EnvironmentVariable"; -import { - ITiledMap, - ITiledMapLayer, - ITiledMapLayerProperty, ITiledMapObject, - ITiledTileSet -} from "../Map/ITiledMap"; +import {DEBUG_MODE, JITSI_PRIVATE_MODE, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; +import {ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapObject, ITiledTileSet} from "../Map/ITiledMap"; import {AddPlayerInterface} from "./AddPlayerInterface"; -import {PlayerAnimationNames} from "../Player/Animation"; +import {PlayerAnimationDirections} from "../Player/Animation"; import {PlayerMovement} from "./PlayerMovement"; import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; import {RemotePlayer} from "../Entity/RemotePlayer"; @@ -41,11 +31,6 @@ import { TRIGGER_WEBSITE_PROPERTIES, WEBSITE_MESSAGE_PROPERTIES } from "../../WebRtc/LayoutManager"; -import Texture = Phaser.Textures.Texture; -import Sprite = Phaser.GameObjects.Sprite; -import CanvasTexture = Phaser.Textures.CanvasTexture; -import GameObject = Phaser.GameObjects.GameObject; -import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import {GameMap} from "./GameMap"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {mediaManager} from "../../WebRtc/MediaManager"; @@ -54,7 +39,7 @@ import {ActionableItem} from "../Items/ActionableItem"; import {UserInputManager} from "../UserInput/UserInputManager"; import {UserMovedMessage} from "../../Messages/generated/messages_pb"; import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils"; -import {connectionManager} from "../../Connexion/ConnectionManager"; +import {connectionManager, ConnexionMessageEvent, ConnexionMessageEventTypes} from "../../Connexion/ConnectionManager"; import {RoomConnection} from "../../Connexion/RoomConnection"; import {GlobalMessageManager} from "../../Administration/GlobalMessageManager"; import {userMessageManager} from "../../Administration/UserMessageManager"; @@ -76,6 +61,12 @@ import {iframeListener} from "../../Api/IframeListener"; import DOMElement = Phaser.GameObjects.DOMElement; import Tween = Phaser.Tweens.Tween; import {HtmlUtils} from "../../WebRtc/HtmlUtils"; +import Texture = Phaser.Textures.Texture; +import Sprite = Phaser.GameObjects.Sprite; +import CanvasTexture = Phaser.Textures.CanvasTexture; +import GameObject = Phaser.GameObjects.GameObject; +import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; +import {Subscription} from "rxjs"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -161,11 +152,12 @@ export class GameScene extends ResizableScene implements CenterListener { // The item that can be selected by pressing the space key. private outlinedItem: ActionableItem|null = null; public userInputManager!: UserInputManager; - private isReconnecting: boolean = false; + private isReconnecting: boolean|undefined = undefined; private startLayerName!: string | null; private openChatIcon!: OpenChatIcon; private playerName!: string; private characterLayers!: string[]; + private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { @@ -299,25 +291,6 @@ export class GameScene extends ResizableScene implements CenterListener { } }); }); - - // import(/* webpackIgnore: true */ scriptUrl).then(result => { - // - // result.default.preload(this.load); - // - // this.load.start(); // Let's manually start the loader because the import might be over AFTER the loading ends. - // this.load.on('complete', () => { - // // FIXME: the factory might fail because the resources might not be loaded yet... - // // We would need to add a loader ended event in addition to the createPromise - // this.createPromise.then(() => { - // result.default.create(this); - // - // for (let object of objectsOfType) { - // // TODO: we should pass here a factory to create sprites (maybe?) - // let objectSprite = result.default.factory(this, object); - // } - // }); - // }); - // }); } // Now, let's load the script, if any @@ -343,6 +316,8 @@ export class GameScene extends ResizableScene implements CenterListener { urlManager.pushRoomIdToUrl(this.room); this.startLayerName = urlManager.getStartLayerNameFromUrl(); + this.messageSubscription = connectionManager._connexionMessageStream.subscribe((event) => this.onConnexionMessage(event)) + const playerName = gameManager.getPlayerName(); if (!playerName) { throw 'playerName is not set'; @@ -413,13 +388,13 @@ export class GameScene extends ResizableScene implements CenterListener { this.scene.launch(ReconnectingSceneName); }, 0); } else if (this.connection === undefined) { - // Let's wait 0.5 seconds before printing the "connecting" screen to avoid blinking + // Let's wait 1 second before printing the "connecting" screen to avoid blinking setTimeout(() => { if (this.connection === undefined) { this.scene.sleep(); this.scene.launch(ReconnectingSceneName); } - }, 500); + }, 1000); } this.createPromiseResolve(); @@ -582,8 +557,6 @@ export class GameScene extends ResizableScene implements CenterListener { //init user position and play trigger to check layers properties this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y); - - return this.connection; }); } @@ -621,29 +594,6 @@ export class GameScene extends ResizableScene implements CenterListener { this.circleRedTexture.refresh(); } - private playAudio(url: string|number|boolean|undefined, loop=false): void { - if (url === undefined) { - audioManager.unloadAudio(); - } else { - const audioPath = url as string; - let realAudioPath = ''; - - if (audioPath.indexOf('://') > 0) { - // remote file or stream - realAudioPath = audioPath; - } else { - // local file, include it relative to map directory - const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); - realAudioPath = mapDirUrl + '/' + url; - } - - audioManager.loadAudio(realAudioPath); - - if (loop) { - audioManager.loop(); - } - } - } private safeParseJSONstring(jsonString: string|undefined, propertyName: string) { try { @@ -724,11 +674,11 @@ export class GameScene extends ResizableScene implements CenterListener { } }); this.gameMap.onPropertyChange('playAudio', (newValue, oldValue) => { - this.playAudio(newValue); + newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl()); }); this.gameMap.onPropertyChange('playAudioLoop', (newValue, oldValue) => { - this.playAudio(newValue, true); + newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl()); }); this.gameMap.onPropertyChange('zone', (newValue, oldValue) => { @@ -797,6 +747,10 @@ ${escapedMessage} }); } + private getMapDirUrl(): string { + return this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); + } + private onMapExit(exitKey: string) { const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey); @@ -828,14 +782,11 @@ ${escapedMessage} } this.stopJitsi(); - this.playAudio(undefined); + audioManager.unloadAudio(); // We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map. - if(this.connection) { - this.connection.closeConnection(); - } - if(this.simplePeer) { - this.simplePeer.unregister(); - } + this.connection?.closeConnection(); + this.simplePeer?.unregister(); + this.messageSubscription?.unsubscribe(); } private removeAllRemotePlayers(): void { @@ -1009,7 +960,7 @@ ${escapedMessage} this.startY, this.playerName, texturesPromise, - PlayerAnimationNames.WalkDown, + PlayerAnimationDirections.Down, false, this.userInputManager ); @@ -1058,16 +1009,16 @@ ${escapedMessage} let x = event.x; let y = event.y; switch (event.direction) { - case PlayerAnimationNames.WalkUp: + case PlayerAnimationDirections.Up: y -= 32; break; - case PlayerAnimationNames.WalkDown: + case PlayerAnimationDirections.Down: y += 32; break; - case PlayerAnimationNames.WalkLeft: + case PlayerAnimationDirections.Left: x -= 32; break; - case PlayerAnimationNames.WalkRight: + case PlayerAnimationDirections.Right: x += 32; break; default: @@ -1203,7 +1154,7 @@ ${escapedMessage} addPlayerData.position.y, addPlayerData.name, texturesPromise, - addPlayerData.position.direction, + addPlayerData.position.direction as PlayerAnimationDirections, addPlayerData.position.moving ); this.MapPlayers.add(player); @@ -1362,21 +1313,34 @@ ${escapedMessage} } public stopJitsi(): void { - this.connection.setSilent(false); + this.connection?.setSilent(false); jitsiFactory.stop(); mediaManager.showGameOverlay(); mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi'); } + //todo: into onConnexionMessage private bannedUser(){ this.cleanupClosingScene(); this.userInputManager.clearAllKeys(); this.scene.start(ErrorSceneName, { title: 'Banned', - subTitle: 'You was banned of WorkAdventure', - message: 'If you want more information, you can contact us: workadventure@thecodingmachine.com' + subTitle: 'You were banned from WorkAdventure', + message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com' }); } + private onConnexionMessage(event: ConnexionMessageEvent) { + if (event.type === ConnexionMessageEventTypes.worldFull) { + this.cleanupClosingScene(); + this.scene.stop(ReconnectingSceneName); + this.userInputManager.clearAllKeys(); + this.scene.start(ErrorSceneName, { + title: 'Connection rejected', + subTitle: 'The world you are trying to join is full. Try again later.', + message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com' + }); + } + } } diff --git a/front/src/Phaser/Menu/ReportMenu.ts b/front/src/Phaser/Menu/ReportMenu.ts index 1c3e8fd8..0795debe 100644 --- a/front/src/Phaser/Menu/ReportMenu.ts +++ b/front/src/Phaser/Menu/ReportMenu.ts @@ -104,8 +104,7 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement { gamePError.innerText = ''; gamePError.style.display = 'none'; const gameTextArea = this.getChildByID('gameReportInput') as HTMLInputElement; - const gameIdUserReported = this.getChildByID('idUserReported') as HTMLInputElement; - if(!gameTextArea || !gameTextArea.value ){ + if(!gameTextArea || !gameTextArea.value){ gamePError.innerText = 'Report message cannot to be empty.'; gamePError.style.display = 'block'; return; @@ -116,4 +115,4 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement { ); this.close(); } -} +} \ No newline at end of file diff --git a/front/src/Phaser/Player/Animation.ts b/front/src/Phaser/Player/Animation.ts index 471f6d5f..ea8d2308 100644 --- a/front/src/Phaser/Player/Animation.ts +++ b/front/src/Phaser/Player/Animation.ts @@ -1,9 +1,13 @@ -export enum PlayerAnimationNames { - WalkDown = 'down', - WalkLeft = 'left', - WalkUp = 'up', - WalkRight = 'right', +export enum PlayerAnimationDirections { + Down = 'down', + Left = 'left', + Up = 'up', + Right = 'right', +} +export enum PlayerAnimationTypes { + Walk = 'walk', + Idle = 'idle', } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index a5096ebb..64ba56d0 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -1,4 +1,4 @@ -import {PlayerAnimationNames} from "./Animation"; +import {PlayerAnimationDirections} from "./Animation"; import {GameScene} from "../Game/GameScene"; import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {Character} from "../Entity/Character"; @@ -11,7 +11,7 @@ export interface CurrentGamerInterface extends Character{ } export class Player extends Character implements CurrentGamerInterface { - private previousDirection: string = PlayerAnimationNames.WalkDown; + private previousDirection: string = PlayerAnimationDirections.Down; private wasMoving: boolean = false; constructor( @@ -20,7 +20,7 @@ export class Player extends Character implements CurrentGamerInterface { y: number, name: string, texturesPromise: Promise, - direction: string, + direction: PlayerAnimationDirections, moving: boolean, private userInputManager: UserInputManager ) { @@ -43,20 +43,20 @@ export class Player extends Character implements CurrentGamerInterface { let y = 0; if (activeEvents.get(UserInputEvent.MoveUp)) { y = - moveAmount; - direction = PlayerAnimationNames.WalkUp; + direction = PlayerAnimationDirections.Up; moving = true; } else if (activeEvents.get(UserInputEvent.MoveDown)) { y = moveAmount; - direction = PlayerAnimationNames.WalkDown; + direction = PlayerAnimationDirections.Down; moving = true; } if (activeEvents.get(UserInputEvent.MoveLeft)) { x = -moveAmount; - direction = PlayerAnimationNames.WalkLeft; + direction = PlayerAnimationDirections.Left; moving = true; } else if (activeEvents.get(UserInputEvent.MoveRight)) { x = moveAmount; - direction = PlayerAnimationNames.WalkRight; + direction = PlayerAnimationDirections.Right; moving = true; } if (x !== 0 || y !== 0) { diff --git a/front/src/WebRtc/AudioManager.ts b/front/src/WebRtc/AudioManager.ts index 8ae3a9c0..24fb74a1 100644 --- a/front/src/WebRtc/AudioManager.ts +++ b/front/src/WebRtc/AudioManager.ts @@ -38,6 +38,25 @@ class AudioManager { HtmlUtils.getElementByIdOrFail('audioplayer_volume').value = '' + this.volume; } + public playAudio(url: string|number|boolean, mapDirUrl: string, loop=false): void { + const audioPath = url as string; + let realAudioPath = ''; + + if (audioPath.indexOf('://') > 0) { + // remote file or stream + realAudioPath = audioPath; + } else { + // local file, include it relative to map directory + realAudioPath = mapDirUrl + '/' + url; + } + + this.loadAudio(realAudioPath); + + if (loop) { + this.loop(); + } + } + private close(): void { this.audioPlayerCtrl.classList.remove('loading'); this.audioPlayerCtrl.classList.add('hidden'); @@ -75,7 +94,7 @@ class AudioManager { } - public loadAudio(url: string): void { + private loadAudio(url: string): void { this.load(); /* Solution 1, remove whole audio player */ @@ -125,7 +144,7 @@ class AudioManager { this.open(); } - public loop(): void { + private loop(): void { if (this.audioPlayerElem !== undefined) { this.audioPlayerElem.loop = true; } diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 36247aef..6c747768 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -350,6 +350,7 @@ message AdminMessage { string message = 1; string recipientUuid = 2; string roomId = 3; + string type = 4; } // A message sent by an administrator to everyone in a specific room @@ -366,6 +367,8 @@ message AdminGlobalMessage { message BanMessage { string recipientUuid = 1; string roomId = 2; + string type = 3; + string message = 4; } message EmptyMessage { diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 2391aaa3..82d0707a 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -1,5 +1,5 @@ import {CharacterLayer, ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." -import {GameRoomPolicyTypes} from "../Model/PusherRoom"; +import {GameRoomPolicyTypes, PusherRoom} from "../Model/PusherRoom"; import {PointInterface} from "../Model/Websocket/PointInterface"; import { SetPlayerDetailsMessage, @@ -20,7 +20,7 @@ import {parse} from "query-string"; import {jwtTokenManager} from "../Services/JWTTokenManager"; import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "../Services/AdminApi"; import {SocketManager, socketManager} from "../Services/SocketManager"; -import {emitInBatch} from "../Services/IoSocketHelpers"; +import {emitError, emitInBatch} from "../Services/IoSocketHelpers"; import {ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER} from "../Enum/EnvironmentVariable"; import {Zone} from "_Model/Zone"; import {ExAdminSocketInterface} from "_Model/Websocket/ExAdminSocketInterface"; @@ -76,10 +76,10 @@ export class IoSocketController { if(message.event === 'user-message') { const messageToEmit = (message.message as { message: string, type: string, userUuid: string }); if(messageToEmit.type === 'banned'){ - socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type); + socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type, ws.roomId as string); } if(messageToEmit.type === 'ban') { - socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type); + socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type, ws.roomId as string); } } }catch (err) { @@ -108,7 +108,6 @@ export class IoSocketController { maxBackpressure: 65536, // Maximum 64kB of data in the buffer. //idleTimeout: 10, upgrade: (res, req, context) => { - //console.log('An Http connection wants to become WebSocket, URL: ' + req.getUrl() + '!'); (async () => { /* Keep track of abortions */ const upgradeAborted = {aborted: false}; @@ -156,12 +155,9 @@ export class IoSocketController { const userUuid = await jwtTokenManager.getUserUuidFromToken(token, IPAddress, roomId); let memberTags: string[] = []; + let memberMessages: unknown; let memberTextures: CharacterTexture[] = []; const room = await socketManager.getOrCreateRoom(roomId); - // TODO: make sure the room isFull is ported in the back part. - /*if(room.isFull){ - throw new Error('Room is full'); - }*/ if (ADMIN_API_URL) { try { let userData : FetchMemberDataByUuidResponse = { @@ -172,15 +168,26 @@ export class IoSocketController { anonymous: true }; try { - userData = await adminApi.fetchMemberDataByUuid(userUuid); + userData = await adminApi.fetchMemberDataByUuid(userUuid, roomId); }catch (err){ if (err?.response?.status == 404) { // If we get an HTTP 404, the token is invalid. Let's perform an anonymous login! console.warn('Cannot find user with uuid "'+userUuid+'". Performing an anonymous login instead.'); + } else if(err?.response?.status == 403) { + // If we get an HTTP 404, the world is full. We need to broadcast a special error to the client. + // we finish immediatly the upgrade then we will close the socket as soon as it starts opening. + res.upgrade({ + rejected: true, + }, websocketKey, + websocketProtocol, + websocketExtensions, + context); + return; }else{ throw err; } } + memberMessages = userData.messages; memberTags = userData.tags; memberTextures = userData.textures; if (!room.anonymous && room.policyType === GameRoomPolicyTypes.USE_TAGS_POLICY && (userData.anonymous === true || !room.canAccess(memberTags))) { @@ -215,6 +222,7 @@ export class IoSocketController { roomId, name, characterLayers: characterLayerObjs, + messages: memberMessages, tags: memberTags, textures: memberTextures, position: { @@ -241,7 +249,6 @@ export class IoSocketController { console.log(e.message); res.writeStatus("401 Unauthorized").end(e.message); } else { - console.log(e); res.writeStatus("500 Internal Server Error").end('An error occurred'); } return; @@ -250,32 +257,30 @@ export class IoSocketController { }, /* Handlers */ open: (ws) => { + if(ws.rejected === true) { + emitError(ws, 'World is full'); + ws.close(); + } + // Let's join the room const client = this.initClient(ws); //todo: into the upgrade instead? socketManager.handleJoinRoom(client); //get data information and show messages - if (ADMIN_API_URL) { - adminApi.fetchMemberDataByUuid(client.userUuid).then((res: FetchMemberDataByUuidResponse) => { - if (!res.messages) { - return; + if (client.messages && Array.isArray(client.messages)) { + client.messages.forEach((c: unknown) => { + const messageToSend = c as { type: string, message: string }; + + 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); } - res.messages.forEach((c: unknown) => { - const messageToSend = c as { type: string, message: string }; - - 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); }); } }, @@ -340,6 +345,7 @@ export class IoSocketController { } client.disconnecting = false; + client.messages = ws.messages; client.name = ws.name; client.tags = ws.tags; client.textures = ws.textures; diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 85cfad48..56e7e5ca 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -36,6 +36,7 @@ export interface ExSocketInterface extends WebSocket, Identificable { batchedMessages: BatchMessage; batchTimeout: NodeJS.Timeout|null; disconnecting: boolean, + messages: unknown, tags: string[], textures: CharacterTexture[], backConnection: BackConnection, diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 06d9239c..d50e2a4f 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -58,12 +58,12 @@ class AdminApi { return res.data; } - async fetchMemberDataByUuid(uuid: string): Promise { + async fetchMemberDataByUuid(uuid: string, roomId: string): Promise { if (!ADMIN_API_URL) { return Promise.reject('No admin backoffice set!'); } - const res = await Axios.get(ADMIN_API_URL+'/api/membership/'+uuid, - { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } + const res = await Axios.get(ADMIN_API_URL+'/api/room/access', + { params: {uuid, roomId}, headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } ) return res.data; } diff --git a/pusher/src/Services/IoSocketHelpers.ts b/pusher/src/Services/IoSocketHelpers.ts index 9c27c59a..e90e0874 100644 --- a/pusher/src/Services/IoSocketHelpers.ts +++ b/pusher/src/Services/IoSocketHelpers.ts @@ -1,5 +1,6 @@ import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; import {BatchMessage, ErrorMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; +import {WebSocket} from "uWebSockets.js"; export function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void { socket.batchedMessages.addPayload(payload); @@ -20,7 +21,7 @@ export function emitInBatch(socket: ExSocketInterface, payload: SubMessage): voi } } -export function emitError(Client: ExSocketInterface, message: string): void { +export function emitError(Client: WebSocket, message: string): void { const errorMessage = new ErrorMessage(); errorMessage.setMessage(message); diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 5c1f25ed..7d5a063e 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -4,7 +4,6 @@ import { GroupDeleteMessage, ItemEventMessage, PlayGlobalMessage, - PositionMessage, RoomJoinedMessage, ServerToClientMessage, SetPlayerDetailsMessage, @@ -23,23 +22,17 @@ import { AdminPusherToBackMessage, ServerToAdminClientMessage, SendUserMessage, - BanUserMessage, UserJoinedRoomMessage, UserLeftRoomMessage + BanUserMessage, UserJoinedRoomMessage, UserLeftRoomMessage, AdminMessage, BanMessage } from "../Messages/generated/messages_pb"; -import {PointInterface} from "../Model/Websocket/PointInterface"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; -import {cpuTracker} from "./CpuTracker"; -import {GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable"; -import {Movable} from "../Model/Movable"; -import {PositionInterface} from "../Model/PositionInterface"; +import {JITSI_ISS, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable"; import {adminApi, CharacterTexture} from "./AdminApi"; -import Direction = PositionMessage.Direction; import {emitError, emitInBatch} from "./IoSocketHelpers"; import Jwt from "jsonwebtoken"; import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {clientEventsEmitter} from "./ClientEventsEmitter"; import {gaugeManager} from "./GaugeManager"; import {apiClientRepository} from "./ApiClientRepository"; -import {ServiceError} from "grpc"; import {GroupDescriptor, UserDescriptor, ZoneEventListener} from "_Model/Zone"; import Debug from "debug"; import {ExAdminSocketInterface} from "_Model/Websocket/ExAdminSocketInterface"; @@ -271,31 +264,6 @@ export class SocketManager implements ZoneEventListener { pusherToBackMessage.setItemeventmessage(itemEventMessage); client.backConnection.write(pusherToBackMessage); - - /*const itemEvent = ProtobufUtils.toItemEvent(itemEventMessage); - - try { - const world = this.Worlds.get(ws.roomId); - if (!world) { - console.error("Could not find world with id '", ws.roomId, "'"); - return; - } - - const subMessage = new SubMessage(); - subMessage.setItemeventmessage(itemEventMessage); - - // Let's send the event without using the SocketIO room. - for (const user of world.getUsers().values()) { - const client = this.searchClientByIdOrFail(user.id); - //client.emit(SocketIoEvent.ITEM_EVENT, itemEvent); - emitInBatch(client, subMessage); - } - - world.setItemState(itemEvent.itemId, itemEvent.state); - } catch (e) { - console.error('An error occurred on "item_event"'); - console.error(e); - }*/ } async handleReportMessage(client: ExSocketInterface, reportPlayerMessage: ReportPlayerMessage) { @@ -317,25 +285,6 @@ export class SocketManager implements ZoneEventListener { pusherToBackMessage.setWebrtcsignaltoservermessage(data); socket.backConnection.write(pusherToBackMessage); - - - //send only at user - /*const client = this.sockets.get(data.getReceiverid()); - if (client === undefined) { - console.warn("While exchanging a WebRTC signal: client with id ", data.getReceiverid(), " does not exist. This might be a race condition."); - return; - } - - const webrtcSignalToClient = new WebRtcSignalToClientMessage(); - webrtcSignalToClient.setUserid(socket.userId); - webrtcSignalToClient.setSignal(data.getSignal()); - - const serverToClientMessage = new ServerToClientMessage(); - serverToClientMessage.setWebrtcsignaltoclientmessage(webrtcSignalToClient); - - if (!client.disconnecting) { - client.send(serverToClientMessage.serializeBinary().buffer, true); - }*/ } emitScreenSharing(socket: ExSocketInterface, data: WebRtcSignalToServerMessage): void { @@ -343,24 +292,6 @@ export class SocketManager implements ZoneEventListener { pusherToBackMessage.setWebrtcscreensharingsignaltoservermessage(data); socket.backConnection.write(pusherToBackMessage); - - //send only at user - /*const client = this.sockets.get(data.getReceiverid()); - if (client === undefined) { - console.warn("While exchanging a WEBRTC_SCREEN_SHARING signal: client with id ", data.getReceiverid(), " does not exist. This might be a race condition."); - return; - } - - const webrtcSignalToClient = new WebRtcSignalToClientMessage(); - webrtcSignalToClient.setUserid(socket.userId); - webrtcSignalToClient.setSignal(data.getSignal()); - - const serverToClientMessage = new ServerToClientMessage(); - serverToClientMessage.setWebrtcscreensharingsignaltoclientmessage(webrtcSignalToClient); - - if (!client.disconnecting) { - client.send(serverToClientMessage.serializeBinary().buffer, true); - }*/ } private searchClientByIdOrFail(userId: number): ExSocketInterface { @@ -408,17 +339,7 @@ export class SocketManager implements ZoneEventListener { //check and create new world for a room let world = this.Worlds.get(roomId) if(world === undefined){ - world = new PusherRoom( - roomId, - this -/* (user: User, group: Group) => this.joinWebRtcRoom(user, group), - (user: User, group: Group) => this.disConnectedUser(user, group), - MINIMUM_DISTANCE, - GROUP_RADIUS, - (thing: Movable, listener: User) => this.onRoomEnter(thing, listener), - (thing: Movable, position:PositionInterface, listener:User) => this.onClientMove(thing, position, listener), - (thing: Movable, listener:User) => this.onClientLeave(thing, listener)*/ - ); + world = new PusherRoom(roomId, this); if (!world.anonymous) { const data = await adminApi.fetchMapDetails(world.organizationSlug, world.worldSlug, world.roomSlug) world.tags = data.tags @@ -429,60 +350,6 @@ export class SocketManager implements ZoneEventListener { return Promise.resolve(world) } -/* private joinRoom(client : ExSocketInterface, position: PointInterface): PusherRoom { - - const roomId = client.roomId; - client.position = position; - - const world = this.Worlds.get(roomId) - if(world === undefined){ - throw new Error('Could not find room for ID: '+client.roomId) - } - - // Dispatch groups position to newly connected user - world.getGroups().forEach((group: Group) => { - this.emitCreateUpdateGroupEvent(client, group); - }); - //join world - world.join(client, client.position); - clientEventsEmitter.emitClientJoin(client.userUuid, client.roomId); - console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)'); - return world; - } - - private onClientMove(thing: Movable, position:PositionInterface, listener:User): void { - const clientListener = this.searchClientByIdOrFail(listener.id); - if (thing instanceof User) { - const clientUser = this.searchClientByIdOrFail(thing.id); - - const userMovedMessage = new UserMovedMessage(); - userMovedMessage.setUserid(clientUser.userId); - userMovedMessage.setPosition(ProtobufUtils.toPositionMessage(clientUser.position)); - - const subMessage = new SubMessage(); - subMessage.setUsermovedmessage(userMovedMessage); - - clientListener.emitInBatch(subMessage); - //console.log("Sending USER_MOVED event"); - } else if (thing instanceof Group) { - this.emitCreateUpdateGroupEvent(clientListener, thing); - } else { - console.error('Unexpected type for Movable.'); - } - } - - private onClientLeave(thing: Movable, listener:User) { - const clientListener = this.searchClientByIdOrFail(listener.id); - if (thing instanceof User) { - const clientUser = this.searchClientByIdOrFail(thing.id); - this.emitUserLeftEvent(clientListener, clientUser.userId); - } else if (thing instanceof Group) { - this.emitDeleteGroupEvent(clientListener, thing.getId()); - } else { - console.error('Unexpected type for Movable.'); - } - }*/ - emitPlayGlobalMessage(client: ExSocketInterface, playglobalmessage: PlayGlobalMessage) { const pusherToBackMessage = new PusherToBackMessage(); pusherToBackMessage.setPlayglobalmessage(playglobalmessage); @@ -505,90 +372,94 @@ export class SocketManager implements ZoneEventListener { public handleQueryJitsiJwtMessage(client: ExSocketInterface, queryJitsiJwtMessage: QueryJitsiJwtMessage) { - const room = queryJitsiJwtMessage.getJitsiroom(); - const tag = queryJitsiJwtMessage.getTag(); // FIXME: this is not secure. We should load the JSON for the current room and check rights associated to room instead. + try { + const room = queryJitsiJwtMessage.getJitsiroom(); + const tag = queryJitsiJwtMessage.getTag(); // FIXME: this is not secure. We should load the JSON for the current room and check rights associated to room instead. - if (SECRET_JITSI_KEY === '') { - throw new Error('You must set the SECRET_JITSI_KEY key to the secret to generate JWT tokens for Jitsi.'); + if (SECRET_JITSI_KEY === '') { + throw new Error('You must set the SECRET_JITSI_KEY key to the secret to generate JWT tokens for Jitsi.'); + } + + // Let's see if the current client has + const isAdmin = client.tags.includes(tag); + + const jwt = Jwt.sign({ + "aud": "jitsi", + "iss": JITSI_ISS, + "sub": JITSI_URL, + "room": room, + "moderator": isAdmin + }, SECRET_JITSI_KEY, { + expiresIn: '1d', + algorithm: "HS256", + header: + { + "alg": "HS256", + "typ": "JWT" + } + }); + + const sendJitsiJwtMessage = new SendJitsiJwtMessage(); + sendJitsiJwtMessage.setJitsiroom(room); + sendJitsiJwtMessage.setJwt(jwt); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSendjitsijwtmessage(sendJitsiJwtMessage); + + client.send(serverToClientMessage.serializeBinary().buffer, true); + } catch (e) { + console.error('An error occured while generating the Jitsi JWT token: ', e); } + } - // Let's see if the current client has - const isAdmin = client.tags.includes(tag); + public async emitSendUserMessage(userUuid: string, message: string, type: string, roomId: string) { + /*const client = this.searchClientByUuid(userUuid); + if(client) { + const adminMessage = new SendUserMessage(); + adminMessage.setMessage(message); + adminMessage.setType(type); + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setSendusermessage(adminMessage); + client.backConnection.write(pusherToBackMessage); + return; + }*/ - const jwt = Jwt.sign({ - "aud": "jitsi", - "iss": JITSI_ISS, - "sub": JITSI_URL, - "room": room, - "moderator": isAdmin - }, SECRET_JITSI_KEY, { - expiresIn: '1d', - algorithm: "HS256", - header: - { - "alg": "HS256", - "typ": "JWT" - } + const backConnection = await apiClientRepository.getClient(roomId); + const backAdminMessage = new AdminMessage(); + backAdminMessage.setMessage(message); + backAdminMessage.setRoomid(roomId); + backAdminMessage.setRecipientuuid(userUuid); + backAdminMessage.setType(type); + backConnection.sendAdminMessage(backAdminMessage, (error) => { + if (error !== null) { + console.error('Error while sending admin message', error); + } }); - - const sendJitsiJwtMessage = new SendJitsiJwtMessage(); - sendJitsiJwtMessage.setJitsiroom(room); - sendJitsiJwtMessage.setJwt(jwt); - - const serverToClientMessage = new ServerToClientMessage(); - serverToClientMessage.setSendjitsijwtmessage(sendJitsiJwtMessage); - - client.send(serverToClientMessage.serializeBinary().buffer, true); } - public emitSendUserMessage(userUuid: string, message: string, type: string): void { - const client = this.searchClientByUuid(userUuid); - if(!client){ - throw Error('client not found'); - } + public async emitBan(userUuid: string, message: string, type: string, roomId: string) { + /*const client = this.searchClientByUuid(userUuid); + if(client) { + const banUserMessage = new BanUserMessage(); + banUserMessage.setMessage(message); + banUserMessage.setType(type); + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setBanusermessage(banUserMessage); + client.backConnection.write(pusherToBackMessage); + return; + }*/ - const adminMessage = new SendUserMessage(); - adminMessage.setMessage(message); - 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) => { + const backConnection = await apiClientRepository.getClient(roomId); + const banMessage = new BanMessage(); + banMessage.setMessage(message); + banMessage.setRoomid(roomId); + banMessage.setRecipientuuid(userUuid); + banMessage.setType(type); + backConnection.ban(banMessage, (error) => { if (error !== null) { console.error('Error while sending admin message', error); } - });*/ - } - - public emitBan(userUuid: string, message: string, type: string): void { - const client = this.searchClientByUuid(userUuid); - if(!client){ - throw Error('client not found'); - } - - const banUserMessage = new BanUserMessage(); - banUserMessage.setMessage(message); - banUserMessage.setType(type); - const pusherToBackMessage = new PusherToBackMessage(); - pusherToBackMessage.setBanusermessage(banUserMessage); - 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); - } - });*/ + }); } /**