diff --git a/CHANGELOG.md b/CHANGELOG.md index d4e7db82..18f26012 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Version develop +### Bugfix +- Moving a discussion over a user will now add this user to the discussion +- Being in a silent zone new forces mediaConstraints to false (#1508) +- Fixes for the emote menu (#1501) + ## Version 1.5.0 ### Updates - Added support for login with OpenID Connect diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 5667146a..74849fe0 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -181,6 +181,7 @@ export class GameRoom { private updateUserGroup(user: User): void { user.group?.updatePosition(); + user.group?.searchForNearbyUsers(); if (user.silent) { return; @@ -206,6 +207,7 @@ export class GameRoom { const group: Group = new Group( this.roomUrl, [user, closestUser], + this.groupRadius, this.connectCallback, this.disconnectCallback, this.positionNotifier diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 5a0f3be6..931ddda5 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,10 +1,10 @@ -import { ConnectCallback, DisconnectCallback } from "./GameRoom"; +import { ConnectCallback, DisconnectCallback, GameRoom } from "./GameRoom"; import { User } from "./User"; import { PositionInterface } from "_Model/PositionInterface"; import { Movable } from "_Model/Movable"; import { PositionNotifier } from "_Model/PositionNotifier"; -import { gaugeManager } from "../Services/GaugeManager"; import { MAX_PER_GROUP } from "../Enum/EnvironmentVariable"; +import type { Zone } from "../Model/Zone"; export class Group implements Movable { private static nextId: number = 1; @@ -13,13 +13,14 @@ export class Group implements Movable { private users: Set; private x!: number; private y!: number; - private hasEditedGauge: boolean = false; private wasDestroyed: boolean = false; private roomId: string; + private currentZone: Zone | null = null; constructor( roomId: string, users: User[], + private groupRadius: number, private connectCallback: ConnectCallback, private disconnectCallback: DisconnectCallback, private positionNotifier: PositionNotifier @@ -28,13 +29,6 @@ export class Group implements Movable { this.users = new Set(); this.id = Group.nextId; Group.nextId++; - //we only send a event for prometheus metrics if the group lives more than 5 seconds - setTimeout(() => { - if (!this.wasDestroyed) { - this.hasEditedGauge = true; - gaugeManager.incNbGroupsPerRoomGauge(roomId); - } - }, 5000); users.forEach((user: User) => { this.join(user); @@ -85,9 +79,22 @@ export class Group implements Movable { this.y = y; if (oldX === undefined) { - this.positionNotifier.enter(this); + this.currentZone = this.positionNotifier.enter(this); } else { - this.positionNotifier.updatePosition(this, { x, y }, { x: oldX, y: oldY }); + this.currentZone = this.positionNotifier.updatePosition(this, { x, y }, { x: oldX, y: oldY }); + } + } + + searchForNearbyUsers(): void { + if (!this.currentZone) return; + + for (const user of this.positionNotifier.getAllUsersInSquareAroundZone(this.currentZone)) { + if (user.group || this.isFull()) return; //we ignore users that are already in a group. + const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), this.getPosition()); + if (distance < this.groupRadius) { + this.join(user); + this.updatePosition(); + } } } @@ -126,7 +133,6 @@ export class Group implements Movable { * Usually used when there is only one user left. */ destroy(): void { - if (this.hasEditedGauge) gaugeManager.decNbGroupsPerRoomGauge(this.roomId); for (const user of this.users) { this.leave(user); } diff --git a/back/src/Model/PositionNotifier.ts b/back/src/Model/PositionNotifier.ts index 4f911637..2052f229 100644 --- a/back/src/Model/PositionNotifier.ts +++ b/back/src/Model/PositionNotifier.ts @@ -12,7 +12,7 @@ import { EmoteCallback, EntersCallback, LeavesCallback, MovesCallback, Zone } fr import { Movable } from "_Model/Movable"; import { PositionInterface } from "_Model/PositionInterface"; import { ZoneSocket } from "../RoomManager"; -import { User } from "_Model/User"; +import { User } from "../Model/User"; import { EmoteEventMessage } from "../Messages/generated/messages_pb"; interface ZoneDescriptor { @@ -20,6 +20,17 @@ interface ZoneDescriptor { j: number; } +export function* getNearbyDescriptorsMatrix(middleZoneDescriptor: ZoneDescriptor): Generator { + for (let n = 0; n < 9; n++) { + const i = middleZoneDescriptor.i + ((n % 3) - 1); + const j = middleZoneDescriptor.j + (Math.floor(n / 3) - 1); + + if (i >= 0 && j >= 0) { + yield { i, j }; + } + } +} + export class PositionNotifier { // TODO: we need a way to clean the zones if no one is in the zone and no one listening (to free memory!) @@ -41,14 +52,15 @@ export class PositionNotifier { }; } - public enter(thing: Movable): void { + public enter(thing: Movable): Zone { const position = thing.getPosition(); const zoneDesc = this.getZoneDescriptorFromCoordinates(position.x, position.y); const zone = this.getZone(zoneDesc.i, zoneDesc.j); zone.enter(thing, null, position); + return zone; } - public updatePosition(thing: Movable, newPosition: PositionInterface, oldPosition: PositionInterface): void { + public updatePosition(thing: Movable, newPosition: PositionInterface, oldPosition: PositionInterface): Zone { // Did we change zone? const oldZoneDesc = this.getZoneDescriptorFromCoordinates(oldPosition.x, oldPosition.y); const newZoneDesc = this.getZoneDescriptorFromCoordinates(newPosition.x, newPosition.y); @@ -62,9 +74,11 @@ export class PositionNotifier { // Enter new zone newZone.enter(thing, oldZone, newPosition); + return newZone; } else { const zone = this.getZone(oldZoneDesc.i, oldZoneDesc.j); zone.move(thing, newPosition); + return zone; } } @@ -106,4 +120,16 @@ export class PositionNotifier { const zone = this.getZone(zoneDesc.i, zoneDesc.j); zone.emitEmoteEvent(emoteEventMessage); } + + public *getAllUsersInSquareAroundZone(zone: Zone): Generator { + const zoneDescriptor = this.getZoneDescriptorFromCoordinates(zone.x, zone.y); + for (const d of getNearbyDescriptorsMatrix(zoneDescriptor)) { + const zone = this.getZone(d.i, d.j); + for (const thing of zone.getThings()) { + if (thing instanceof User) { + yield thing; + } + } + } + } } diff --git a/back/src/Services/GaugeManager.ts b/back/src/Services/GaugeManager.ts index 6d2183d8..740692a8 100644 --- a/back/src/Services/GaugeManager.ts +++ b/back/src/Services/GaugeManager.ts @@ -52,15 +52,6 @@ class GaugeManager { this.nbClientsGauge.dec(); this.nbClientsPerRoomGauge.dec({ room: roomId }); } - - incNbGroupsPerRoomGauge(roomId: string): void { - this.nbGroupsPerRoomCounter.inc({ room: roomId }); - this.nbGroupsPerRoomGauge.inc({ room: roomId }); - } - - decNbGroupsPerRoomGauge(roomId: string): void { - this.nbGroupsPerRoomGauge.dec({ room: roomId }); - } } export const gaugeManager = new GaugeManager(); diff --git a/back/tests/getNearbyDescriptorsMatrixTest.ts b/back/tests/getNearbyDescriptorsMatrixTest.ts new file mode 100644 index 00000000..6c81db76 --- /dev/null +++ b/back/tests/getNearbyDescriptorsMatrixTest.ts @@ -0,0 +1,67 @@ +import "jasmine"; +import { getNearbyDescriptorsMatrix } from "../src/Model/PositionNotifier"; + +describe("getNearbyDescriptorsMatrix", () => { + it("should create a matrix of coordinates in a square around the parameter", () => { + const matrix = []; + for (const d of getNearbyDescriptorsMatrix({ i: 1, j: 1 })) { + matrix.push(d); + } + + expect(matrix).toEqual([ + { i: 0, j: 0 }, + { i: 1, j: 0 }, + { i: 2, j: 0 }, + { i: 0, j: 1 }, + { i: 1, j: 1 }, + { i: 2, j: 1 }, + { i: 0, j: 2 }, + { i: 1, j: 2 }, + { i: 2, j: 2 }, + ]); + }); + + it("should create a matrix of coordinates in a square around the parameter bis", () => { + const matrix = []; + for (const d of getNearbyDescriptorsMatrix({ i: 8, j: 3 })) { + matrix.push(d); + } + + expect(matrix).toEqual([ + { i: 7, j: 2 }, + { i: 8, j: 2 }, + { i: 9, j: 2 }, + { i: 7, j: 3 }, + { i: 8, j: 3 }, + { i: 9, j: 3 }, + { i: 7, j: 4 }, + { i: 8, j: 4 }, + { i: 9, j: 4 }, + ]); + }); + + it("should not create a matrix with negative coordinates", () => { + const matrix = []; + for (const d of getNearbyDescriptorsMatrix({ i: 0, j: 0 })) { + matrix.push(d); + } + + expect(matrix).toEqual([ + { i: 0, j: 0 }, + { i: 1, j: 0 }, + { i: 0, j: 1 }, + { i: 1, j: 1 }, + ]); + }); + + /*it("should not create a matrix with coordinates bigger than its dimmensions", () => { + const matrix = getNearbyDescriptorsMatrix({i: 4, j: 4}, 5, 5); + + expect(matrix).toEqual([ + {i: 3,j: 3}, + {i: 4,j: 3}, + {i: 3,j: 4}, + {i: 4,j: 4}, + ]) + });*/ +}); diff --git a/front/dist/resources/objects/talk.png b/front/dist/resources/objects/talk.png index bc06d3b0..794a6e9d 100644 Binary files a/front/dist/resources/objects/talk.png and b/front/dist/resources/objects/talk.png differ diff --git a/front/package.json b/front/package.json index 8fd4f4c3..ec81d8a7 100644 --- a/front/package.json +++ b/front/package.json @@ -50,7 +50,7 @@ "phaser": "^3.54.0", "phaser-animated-tiles": "workadventure/phaser-animated-tiles#da68bbededd605925621dd4f03bd27e69284b254", "phaser3-rex-plugins": "^1.1.42", - "posthog-js": "^1.13.12", + "posthog-js": "^1.14.1", "queue-typescript": "^1.0.1", "quill": "1.3.6", "quill-delta-to-html": "^0.12.0", diff --git a/front/src/Administration/AnalyticsClient.ts b/front/src/Administration/AnalyticsClient.ts index f73a1981..64548515 100644 --- a/front/src/Administration/AnalyticsClient.ts +++ b/front/src/Administration/AnalyticsClient.ts @@ -1,4 +1,6 @@ import { POSTHOG_API_KEY, POSTHOG_URL } from "../Enum/EnvironmentVariable"; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +declare let window: any; class AnalyticsClient { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -8,6 +10,8 @@ class AnalyticsClient { if (POSTHOG_API_KEY && POSTHOG_URL) { this.posthogPromise = import("posthog-js").then(({ default: posthog }) => { posthog.init(POSTHOG_API_KEY, { api_host: POSTHOG_URL, disable_cookie: true }); + //the posthog toolbar need a reference in window to be able to work + window.posthog = posthog; return posthog; }); } else { @@ -15,10 +19,10 @@ class AnalyticsClient { } } - identifyUser(uuid: string) { + identifyUser(uuid: string, email: string | null) { this.posthogPromise .then((posthog) => { - posthog.identify(uuid, { uuid, wa: true }); + posthog.identify(uuid, { uuid, email, wa: true }); }) .catch(); } @@ -39,10 +43,10 @@ class AnalyticsClient { .catch(); } - enteredRoom(roomId: string) { + enteredRoom(roomId: string, roomGroup: string | null) { this.posthogPromise .then((posthog) => { - posthog.capture("$pageView", { roomId }); + posthog.capture("$pageView", { roomId, roomGroup }); }) .catch(); } diff --git a/front/src/Components/CameraControls.svelte b/front/src/Components/CameraControls.svelte index 6dc2726a..728c84e9 100644 --- a/front/src/Components/CameraControls.svelte +++ b/front/src/Components/CameraControls.svelte @@ -15,6 +15,7 @@ import {onDestroy} from "svelte"; function screenSharingClick(): void { + if (isSilent) return; if ($requestedScreenSharingState === true) { requestedScreenSharingState.disableScreenSharing(); } else { @@ -23,6 +24,7 @@ } function cameraClick(): void { + if (isSilent) return; if ($requestedCameraState === true) { requestedCameraState.disableWebcam(); } else { @@ -31,6 +33,7 @@ } function microphoneClick(): void { + if (isSilent) return; if ($requestedMicrophoneState === true) { requestedMicrophoneState.disableMicrophone(); } else { diff --git a/front/src/Components/Menu/MenuIcon.svelte b/front/src/Components/Menu/MenuIcon.svelte index 92a52ba3..02d6eb53 100644 --- a/front/src/Components/Menu/MenuIcon.svelte +++ b/front/src/Components/Menu/MenuIcon.svelte @@ -1,11 +1,16 @@