Users blocking now rely on UUID rather than ID

This way, if a user A blocks another user B, if user B refreshes the browser or leaves and re-enters the room, user B will still be blocked.
As a side effect, this allows us to completely remove the "sockets" property in the SocketManager on the Pusher.
This commit is contained in:
David Négrier 2021-07-07 11:24:51 +02:00
parent 28e4f59e50
commit 34cb0ebf39
15 changed files with 143 additions and 141 deletions

View file

@ -16,6 +16,7 @@
- Use `WA.room.getCurrentRoom(): Promise<Room>` to get the ID, JSON map file, url of the map of the current room and the layer where the current player started
- Use `WA.ui.registerMenuCommand(): void` to add a custom menu
- Use `WA.room.setTiles(): void` to change an array of tiles
- Users blocking now relies on UUID rather than ID. A blocked user that leaves a room and comes back will stay blocked.
## Version 1.4.3 - 1.4.4 - 1.4.5

View file

@ -308,6 +308,7 @@ export class SocketManager {
throw new Error("clientUser.userId is not an integer " + thing.id);
}
userJoinedZoneMessage.setUserid(thing.id);
userJoinedZoneMessage.setUseruuid(thing.uuid);
userJoinedZoneMessage.setName(thing.name);
userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
@ -612,6 +613,7 @@ export class SocketManager {
if (thing instanceof User) {
const userJoinedMessage = new UserJoinedZoneMessage();
userJoinedMessage.setUserid(thing.id);
userJoinedMessage.setUseruuid(thing.uuid);
userJoinedMessage.setName(thing.name);
userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));

View file

@ -1,8 +1,8 @@
import type {SignalData} from "simple-peer";
import type {RoomConnection} from "./RoomConnection";
import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
import type { SignalData } from "simple-peer";
import type { RoomConnection } from "./RoomConnection";
import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures";
export enum EventMessage{
export enum EventMessage {
CONNECT = "connect",
WEBRTC_SIGNAL = "webrtc-signal",
WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal",
@ -17,7 +17,7 @@ export enum EventMessage{
GROUP_CREATE_UPDATE = "group-create-update",
GROUP_DELETE = "group-delete",
SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id.
ITEM_EVENT = 'item-event',
ITEM_EVENT = "item-event",
CONNECT_ERROR = "connect_error",
CONNECTING_ERROR = "connecting_error",
@ -36,7 +36,7 @@ export enum EventMessage{
export interface PointInterface {
x: number;
y: number;
direction : string;
direction: string;
moving: boolean;
}
@ -45,8 +45,9 @@ export interface MessageUserPositionInterface {
name: string;
characterLayers: BodyResourceDescriptionInterface[];
position: PointInterface;
visitCardUrl: string|null;
companion: string|null;
visitCardUrl: string | null;
companion: string | null;
userUuid: string;
}
export interface MessageUserMovedInterface {
@ -60,58 +61,59 @@ export interface MessageUserJoined {
characterLayers: BodyResourceDescriptionInterface[];
position: PointInterface;
visitCardUrl: string | null;
companion: string|null;
companion: string | null;
userUuid: string;
}
export interface PositionInterface {
x: number,
y: number
x: number;
y: number;
}
export interface GroupCreatedUpdatedMessageInterface {
position: PositionInterface,
groupId: number,
groupSize: number
position: PositionInterface;
groupId: number;
groupSize: number;
}
export interface WebRtcDisconnectMessageInterface {
userId: number
userId: number;
}
export interface WebRtcSignalReceivedMessageInterface {
userId: number,
signal: SignalData,
webRtcUser: string | undefined,
webRtcPassword: string | undefined
userId: number;
signal: SignalData;
webRtcUser: string | undefined;
webRtcPassword: string | undefined;
}
export interface ViewportInterface {
left: number,
top: number,
right: number,
bottom: number,
left: number;
top: number;
right: number;
bottom: number;
}
export interface ItemEventMessageInterface {
itemId: number,
event: string,
state: unknown,
parameters: unknown
itemId: number;
event: string;
state: unknown;
parameters: unknown;
}
export interface RoomJoinedMessageInterface {
//users: MessageUserPositionInterface[],
//groups: GroupCreatedUpdatedMessageInterface[],
items: { [itemId: number] : unknown }
items: { [itemId: number]: unknown };
}
export interface PlayGlobalMessageInterface {
id: string
type: string
message: string
id: string;
type: string;
message: string;
}
export interface OnConnectInterface {
connection: RoomConnection,
room: RoomJoinedMessageInterface
connection: RoomConnection;
room: RoomJoinedMessageInterface;
}

View file

@ -365,6 +365,7 @@ export class RoomConnection implements RoomConnection {
visitCardUrl: message.getVisitcardurl(),
position: ProtobufClientUtils.toPointInterface(position),
companion: companion ? companion.getName() : null,
userUuid: message.getUseruuid(),
};
}
@ -591,9 +592,9 @@ export class RoomConnection implements RoomConnection {
this.socket.send(clientToServerMessage.serializeBinary().buffer);
}
public emitReportPlayerMessage(reportedUserId: number, reportComment: string): void {
public emitReportPlayerMessage(reportedUserUuid: string, reportComment: string): void {
const reportPlayerMessage = new ReportPlayerMessage();
reportPlayerMessage.setReporteduserid(reportedUserId);
reportPlayerMessage.setReporteduseruuid(reportedUserUuid);
reportPlayerMessage.setReportcomment(reportComment);
const clientToServerMessage = new ClientToServerMessage();

View file

@ -91,7 +91,7 @@ import { soundManager } from "./SoundManager";
import { peerStore, screenSharingPeerStore } from "../../Stores/PeerStore";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
import {playersStore} from "../../Stores/PlayersStore";
import { playersStore } from "../../Stores/PlayersStore";
export interface GameSceneInitInterface {
initPosition: PointInterface | null;
@ -608,6 +608,7 @@ export class GameScene extends DirtyScene {
position: message.position,
visitCardUrl: message.visitCardUrl,
companion: message.companion,
userUuid: message.userUuid,
};
this.addPlayer(userMessage);
});
@ -1047,7 +1048,7 @@ ${escapedMessage}
})
);
iframeListener.registerAnswerer('getState', () => {
iframeListener.registerAnswerer("getState", () => {
return {
mapUrl: this.MapUrlFile,
startLayerName: this.startPositionCalculator.startLayerName,
@ -1150,7 +1151,7 @@ ${escapedMessage}
this.emoteManager.destroy();
this.peerStoreUnsubscribe();
this.biggestAvailableAreaStoreUnsubscribe();
iframeListener.unregisterAnswerer('getState');
iframeListener.unregisterAnswerer("getState");
mediaManager.hideGameOverlay();

View file

@ -1,9 +1,10 @@
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
export interface PlayerInterface {
userId: number;
name: string;
characterLayers: BodyResourceDescriptionInterface[];
visitCardUrl: string|null;
companion: string|null;
visitCardUrl: string | null;
companion: string | null;
userUuid: string;
}

View file

@ -18,6 +18,7 @@ import { registerMenuCommandStream } from "../../Api/Events/ui/MenuItemRegisterE
import { sendMenuClickedEvent } from "../../Api/iframe/Ui/MenuItem";
import { consoleGlobalMessageManagerVisibleStore } from "../../Stores/ConsoleGlobalMessageManagerStore";
import { get } from "svelte/store";
import { playersStore } from "../../Stores/PlayersStore";
export const MenuSceneName = "MenuScene";
const gameMenuKey = "gameMenu";
@ -120,7 +121,11 @@ export class MenuScene extends Phaser.Scene {
showReportScreenStore.subscribe((user) => {
if (user !== null) {
this.closeAll();
this.gameReportElement.open(user.userId, user.userName);
const uuid = playersStore.getPlayerById(user.userId)?.userUuid;
if (uuid === undefined) {
throw new Error("Could not find UUID for user with ID " + user.userId);
}
this.gameReportElement.open(uuid, user.userName);
}
});

View file

@ -1,15 +1,16 @@
import {MenuScene} from "./MenuScene";
import {gameManager} from "../Game/GameManager";
import {blackListManager} from "../../WebRtc/BlackListManager";
import { MenuScene } from "./MenuScene";
import { gameManager } from "../Game/GameManager";
import { blackListManager } from "../../WebRtc/BlackListManager";
import { playersStore } from "../../Stores/PlayersStore";
export const gameReportKey = 'gameReport';
export const gameReportRessource = 'resources/html/gameReport.html';
export const gameReportKey = "gameReport";
export const gameReportRessource = "resources/html/gameReport.html";
export class ReportMenu extends Phaser.GameObjects.DOMElement {
private opened: boolean = false;
private userId!: number;
private userName!: string|undefined;
private userUuid!: string;
private userName!: string | undefined;
private anonymous: boolean;
constructor(scene: Phaser.Scene, anonymous: boolean) {
@ -18,46 +19,46 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
this.createFromCache(gameReportKey);
if (this.anonymous) {
const divToHide = this.getChildByID('reportSection') as HTMLElement;
const divToHide = this.getChildByID("reportSection") as HTMLElement;
divToHide.hidden = true;
const textToHide = this.getChildByID('askActionP') as HTMLElement;
const textToHide = this.getChildByID("askActionP") as HTMLElement;
textToHide.hidden = true;
}
scene.add.existing(this);
MenuScene.revealMenusAfterInit(this, gameReportKey);
this.addListener('click');
this.on('click', (event:MouseEvent) => {
this.addListener("click");
this.on("click", (event: MouseEvent) => {
event.preventDefault();
if ((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') {
if ((event?.target as HTMLInputElement).id === "gameReportFormSubmit") {
this.submitReport();
} else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') {
} else if ((event?.target as HTMLInputElement).id === "gameReportFormCancel") {
this.close();
} else if((event?.target as HTMLInputElement).id === 'toggleBlockButton') {
} else if ((event?.target as HTMLInputElement).id === "toggleBlockButton") {
this.toggleBlock();
}
});
}
public open(userId: number, userName: string|undefined): void {
public open(userUuid: string, userName: string | undefined): void {
if (this.opened) {
this.close();
return;
}
this.userId = userId;
this.userUuid = userUuid;
this.userName = userName;
const mainEl = this.getChildByID('gameReport') as HTMLElement;
const mainEl = this.getChildByID("gameReport") as HTMLElement;
this.x = this.getCenteredX(mainEl);
this.y = this.getHiddenY(mainEl);
const gameTitleReport = this.getChildByID('nameReported') as HTMLElement;
gameTitleReport.innerText = userName || '';
const gameTitleReport = this.getChildByID("nameReported") as HTMLElement;
gameTitleReport.innerText = userName || "";
const blockButton = this.getChildByID('toggleBlockButton') as HTMLElement;
blockButton.innerText = blackListManager.isBlackListed(this.userId) ? 'Unblock this user' : 'Block this user';
const blockButton = this.getChildByID("toggleBlockButton") as HTMLElement;
blockButton.innerText = blackListManager.isBlackListed(this.userUuid) ? "Unblock this user" : "Block this user";
this.opened = true;
@ -67,19 +68,19 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
targets: this,
y: this.getCenteredY(mainEl),
duration: 1000,
ease: 'Power3'
ease: "Power3",
});
}
public close(): void {
gameManager.getCurrentGameScene(this.scene).userInputManager.restoreControls();
this.opened = false;
const mainEl = this.getChildByID('gameReport') as HTMLElement;
const mainEl = this.getChildByID("gameReport") as HTMLElement;
this.scene.tweens.add({
targets: this,
y: this.getHiddenY(mainEl),
duration: 1000,
ease: 'Power3'
ease: "Power3",
});
}
@ -88,31 +89,32 @@ export class ReportMenu extends Phaser.GameObjects.DOMElement {
return window.innerWidth / 4 - mainEl.clientWidth / 2;
}
private getHiddenY(mainEl: HTMLElement): number {
return - mainEl.clientHeight - 50;
return -mainEl.clientHeight - 50;
}
private getCenteredY(mainEl: HTMLElement): number {
return window.innerHeight / 4 - mainEl.clientHeight / 2;
}
private toggleBlock(): void {
!blackListManager.isBlackListed(this.userId) ? blackListManager.blackList(this.userId) : blackListManager.cancelBlackList(this.userId);
!blackListManager.isBlackListed(this.userUuid)
? blackListManager.blackList(this.userUuid)
: blackListManager.cancelBlackList(this.userUuid);
this.close();
}
private submitReport(): void{
const gamePError = this.getChildByID('gameReportErr') as HTMLParagraphElement;
gamePError.innerText = '';
gamePError.style.display = 'none';
const gameTextArea = this.getChildByID('gameReportInput') as HTMLInputElement;
if(!gameTextArea || !gameTextArea.value){
gamePError.innerText = 'Report message cannot to be empty.';
gamePError.style.display = 'block';
private submitReport(): void {
const gamePError = this.getChildByID("gameReportErr") as HTMLParagraphElement;
gamePError.innerText = "";
gamePError.style.display = "none";
const gameTextArea = this.getChildByID("gameReportInput") as HTMLInputElement;
if (!gameTextArea || !gameTextArea.value) {
gamePError.innerText = "Report message cannot to be empty.";
gamePError.style.display = "block";
return;
}
gameManager.getCurrentGameScene(this.scene).connection?.emitReportPlayerMessage(
this.userId,
gameTextArea.value
);
gameManager
.getCurrentGameScene(this.scene)
.connection?.emitReportPlayerMessage(this.userUuid, gameTextArea.value);
this.close();
}
}

View file

@ -1,6 +1,6 @@
import { writable } from "svelte/store";
import type {PlayerInterface} from "../Phaser/Game/PlayerInterface";
import type {RoomConnection} from "../Connexion/RoomConnection";
import type { PlayerInterface } from "../Phaser/Game/PlayerInterface";
import type { RoomConnection } from "../Connexion/RoomConnection";
/**
* A store that contains the list of players currently known.
@ -23,6 +23,7 @@ function createPlayersStore() {
characterLayers: message.characterLayers,
visitCardUrl: message.visitCardUrl,
companion: message.companion,
userUuid: message.userUuid,
});
return users;
});
@ -34,9 +35,9 @@ function createPlayersStore() {
});
});
},
getPlayerById(userId: number): PlayerInterface|undefined {
getPlayerById(userId: number): PlayerInterface | undefined {
return players.get(userId);
}
},
};
}

View file

@ -1,24 +1,27 @@
import {Subject} from 'rxjs';
import { Subject } from "rxjs";
class BlackListManager {
private list: number[] = [];
public onBlockStream: Subject<number> = new Subject();
public onUnBlockStream: Subject<number> = new Subject();
isBlackListed(userId: number): boolean {
return this.list.find((data) => data === userId) !== undefined;
}
blackList(userId: number): void {
if (this.isBlackListed(userId)) return;
this.list.push(userId);
this.onBlockStream.next(userId);
private list: string[] = [];
public onBlockStream: Subject<string> = new Subject();
public onUnBlockStream: Subject<string> = new Subject();
isBlackListed(userUuid: string): boolean {
return this.list.find((data) => data === userUuid) !== undefined;
}
cancelBlackList(userId: number): void {
this.list.splice(this.list.findIndex(data => data === userId), 1);
this.onUnBlockStream.next(userId);
blackList(userUuid: string): void {
if (this.isBlackListed(userUuid)) return;
this.list.push(userUuid);
this.onBlockStream.next(userUuid);
}
cancelBlackList(userUuid: string): void {
this.list.splice(
this.list.findIndex((data) => data === userUuid),
1
);
this.onUnBlockStream.next(userUuid);
}
}
export const blackListManager = new BlackListManager();
export const blackListManager = new BlackListManager();

View file

@ -11,7 +11,7 @@ import { get } from "svelte/store";
import { localStreamStore, LocalStreamStoreValue, obtainedMediaConstraintStore } from "../Stores/MediaStore";
import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore";
import { discussionManager } from "./DiscussionManager";
import {playersStore} from "../Stores/PlayersStore";
import { playersStore } from "../Stores/PlayersStore";
export interface UserSimplePeerInterface {
userId: number;
@ -199,7 +199,7 @@ export class SimplePeer {
}
private getName(userId: number): string {
return playersStore.getPlayerById(userId)?.name || '';
return playersStore.getPlayerById(userId)?.name || "";
}
/**
@ -364,7 +364,8 @@ export class SimplePeer {
}
private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) {
if (blackListManager.isBlackListed(data.userId)) return;
const uuid = playersStore.getPlayerById(data.userId)?.userUuid || "";
if (blackListManager.isBlackListed(uuid)) return;
console.log("receiveWebrtcScreenSharingSignal", data);
const streamResult = get(screenSharingLocalStreamStore);
let stream: MediaStream | null = null;
@ -465,7 +466,8 @@ export class SimplePeer {
}
private sendLocalScreenSharingStreamToUser(userId: number, localScreenCapture: MediaStream): void {
if (blackListManager.isBlackListed(userId)) return;
const uuid = playersStore.getPlayerById(userId)?.userUuid || "";
if (blackListManager.isBlackListed(uuid)) return;
// If a connection already exists with user (because it is already sharing a screen with us... let's use this connection)
if (this.PeerScreenSharingConnectionArray.has(userId)) {
this.pushScreenSharingToRemoteUser(userId, localScreenCapture);

View file

@ -8,6 +8,7 @@ import type { UserSimplePeerInterface } from "./SimplePeer";
import { get, readable, Readable } from "svelte/store";
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
import { discussionManager } from "./DiscussionManager";
import { playersStore } from "../Stores/PlayersStore";
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
@ -26,6 +27,7 @@ export class VideoPeer extends Peer {
private remoteStream!: MediaStream;
private blocked: boolean = false;
public readonly userId: number;
public readonly userUuid: string;
public readonly uniqueId: string;
private onBlockSubscribe: Subscription;
private onUnBlockSubscribe: Subscription;
@ -60,6 +62,7 @@ export class VideoPeer extends Peer {
});
this.userId = user.userId;
this.userUuid = playersStore.getPlayerById(this.userId)?.userUuid || "";
this.uniqueId = "video_" + this.userId;
this.streamStore = readable<MediaStream | null>(null, (set) => {
@ -181,20 +184,20 @@ export class VideoPeer extends Peer {
});
this.pushVideoToRemoteUser(localStream);
this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userId) => {
if (userId === this.userId) {
this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userUuid) => {
if (userUuid === this.userUuid) {
this.toggleRemoteStream(false);
this.sendBlockMessage(true);
}
});
this.onUnBlockSubscribe = blackListManager.onUnBlockStream.subscribe((userId) => {
if (userId === this.userId) {
this.onUnBlockSubscribe = blackListManager.onUnBlockStream.subscribe((userUuid) => {
if (userUuid === this.userUuid) {
this.toggleRemoteStream(true);
this.sendBlockMessage(false);
}
});
if (blackListManager.isBlackListed(this.userId)) {
if (blackListManager.isBlackListed(this.userUuid)) {
this.sendBlockMessage(true);
}
}
@ -231,7 +234,7 @@ export class VideoPeer extends Peer {
private stream(stream: MediaStream) {
try {
this.remoteStream = stream;
if (blackListManager.isBlackListed(this.userId) || this.blocked) {
if (blackListManager.isBlackListed(this.userUuid) || this.blocked) {
this.toggleRemoteStream(false);
}
} catch (err) {

View file

@ -62,7 +62,7 @@ message WebRtcSignalToServerMessage {
}
message ReportPlayerMessage {
int32 reportedUserId = 1;
string reportedUserUuid = 1;
string reportComment = 2;
}
@ -158,6 +158,7 @@ message UserJoinedMessage {
PositionMessage position = 4;
CompanionMessage companion = 5;
string visitCardUrl = 6;
string userUuid = 7;
}
message UserLeftMessage {
@ -285,6 +286,7 @@ message UserJoinedZoneMessage {
Zone fromZone = 5;
CompanionMessage companion = 6;
string visitCardUrl = 7;
string userUuid = 8;
}
message UserLeftZoneMessage {

View file

@ -39,6 +39,7 @@ export type LeavesCallback = (thing: Movable, listener: User) => void;*/
export class UserDescriptor {
private constructor(
public readonly userId: number,
private userUuid: string,
private name: string,
private characterLayers: CharacterLayerMessage[],
private position: PositionMessage,
@ -57,6 +58,7 @@ export class UserDescriptor {
}
return new UserDescriptor(
message.getUserid(),
message.getUseruuid(),
message.getName(),
message.getCharacterlayersList(),
position,
@ -84,6 +86,7 @@ export class UserDescriptor {
userJoinedMessage.setVisitcardurl(this.visitCardUrl);
}
userJoinedMessage.setCompanion(this.companion);
userJoinedMessage.setUseruuid(this.userUuid);
return userJoinedMessage;
}

View file

@ -61,7 +61,6 @@ export interface AdminSocketData {
export class SocketManager implements ZoneEventListener {
private rooms: Map<string, PusherRoom> = new Map<string, PusherRoom>();
private sockets: Map<number, ExSocketInterface> = new Map<number, ExSocketInterface>();
constructor() {
clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
@ -191,8 +190,6 @@ export class SocketManager implements ZoneEventListener {
.on("data", (message: ServerToClientMessage) => {
if (message.hasRoomjoinedmessage()) {
client.userId = (message.getRoomjoinedmessage() as RoomJoinedMessage).getCurrentuserid();
// TODO: do we need this.sockets anymore?
this.sockets.set(client.userId, client);
// If this is the first message sent, send back the viewport.
this.handleViewport(client, viewport);
@ -302,14 +299,8 @@ export class SocketManager implements ZoneEventListener {
async handleReportMessage(client: ExSocketInterface, reportPlayerMessage: ReportPlayerMessage) {
try {
const reportedSocket = this.sockets.get(reportPlayerMessage.getReporteduserid());
if (!reportedSocket) {
throw "reported socket user not found";
}
//TODO report user on admin application
//todo: move to back because this fail if the reported player is in another pusher.
await adminApi.reportPlayer(
reportedSocket.userUuid,
reportPlayerMessage.getReporteduseruuid(),
reportPlayerMessage.getReportcomment(),
client.userUuid,
client.roomId.split("/")[2]
@ -334,14 +325,6 @@ export class SocketManager implements ZoneEventListener {
socket.backConnection.write(pusherToBackMessage);
}
private searchClientByIdOrFail(userId: number): ExSocketInterface {
const client: ExSocketInterface | undefined = this.sockets.get(userId);
if (client === undefined) {
throw new Error("Could not find user with id " + userId);
}
return client;
}
leaveRoom(socket: ExSocketInterface) {
// leave previous room and world
try {
@ -364,9 +347,8 @@ export class SocketManager implements ZoneEventListener {
//Client.leave(Client.roomId);
} finally {
//delete Client.roomId;
this.sockets.delete(socket.userId);
clientEventsEmitter.emitClientLeave(socket.userUuid, socket.roomId);
console.log("A user left (", this.sockets.size, " connected users)");
console.log("A user left");
}
}
} finally {
@ -410,15 +392,6 @@ export class SocketManager implements ZoneEventListener {
return this.rooms;
}
searchClientByUuid(uuid: string): ExSocketInterface | null {
for (const socket of this.sockets.values()) {
if (socket.userUuid === uuid) {
return socket;
}
}
return null;
}
public handleQueryJitsiJwtMessage(client: ExSocketInterface, queryJitsiJwtMessage: QueryJitsiJwtMessage) {
try {
const room = queryJitsiJwtMessage.getJitsiroom();