diff --git a/CHANGELOG.md b/CHANGELOG.md index 2028e3b7..e5d9138a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ### Updates +- Added the emote feature to Workadventure. (@Kharhamel, @Tabascoeye) + - The emote menu can be opened by clicking on your character. + - Clicking on one of its element will close the menu and play an emote above your character. + - This emote can be seen by other players. - Mobile support has been improved - WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used - Mouse wheel support to zoom in / out diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 4436fb60..be3e5cd3 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -2,12 +2,12 @@ import {PointInterface} from "./Websocket/PointInterface"; import {Group} from "./Group"; import {User, UserSocket} from "./User"; import {PositionInterface} from "_Model/PositionInterface"; -import {EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone"; +import {EmoteCallback, EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone"; import {PositionNotifier} from "./PositionNotifier"; import {Movable} from "_Model/Movable"; import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier"; import {arrayIntersect} from "../Services/ArrayHelper"; -import {JoinRoomMessage} from "../Messages/generated/messages_pb"; +import {EmoteEventMessage, JoinRoomMessage} from "../Messages/generated/messages_pb"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; import {ZoneSocket} from "src/RoomManager"; import {Admin} from "../Model/Admin"; @@ -51,8 +51,9 @@ export class GameRoom { groupRadius: number, onEnters: EntersCallback, onMoves: MovesCallback, - onLeaves: LeavesCallback) - { + onLeaves: LeavesCallback, + onEmote: EmoteCallback, + ) { this.roomId = roomId; if (isRoomAnonymous(roomId)) { @@ -74,7 +75,7 @@ export class GameRoom { this.minDistance = minDistance; this.groupRadius = groupRadius; // A zone is 10 sprites wide. - this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves); + this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves, onEmote); } public getGroups(): Group[] { @@ -325,4 +326,8 @@ export class GameRoom { this.versionNumber++ return this.versionNumber; } + + public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) { + this.positionNotifier.emitEmoteEvent(user, emoteEventMessage); + } } diff --git a/back/src/Model/PositionNotifier.ts b/back/src/Model/PositionNotifier.ts index 6eff17a3..275bf9d0 100644 --- a/back/src/Model/PositionNotifier.ts +++ b/back/src/Model/PositionNotifier.ts @@ -8,10 +8,12 @@ * The PositionNotifier is important for performance. It allows us to send the position of players only to a restricted * number of players around the current player. */ -import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone"; +import {EmoteCallback, EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone"; import {Movable} from "_Model/Movable"; import {PositionInterface} from "_Model/PositionInterface"; import {ZoneSocket} from "../RoomManager"; +import {User} from "_Model/User"; +import {EmoteEventMessage} from "../Messages/generated/messages_pb"; interface ZoneDescriptor { i: number; @@ -24,7 +26,7 @@ export class PositionNotifier { private zones: Zone[][] = []; - constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback) { + constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback, private onEmote: EmoteCallback) { } private getZoneDescriptorFromCoordinates(x: number, y: number): ZoneDescriptor { @@ -77,7 +79,7 @@ export class PositionNotifier { let zone = this.zones[j][i]; if (zone === undefined) { - zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, i, j); + zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, this.onEmote, i, j); this.zones[j][i] = zone; } return zone; @@ -93,4 +95,11 @@ export class PositionNotifier { const zone = this.getZone(x, y); zone.removeListener(call); } + + public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) { + const zoneDesc = this.getZoneDescriptorFromCoordinates(user.getPosition().x, user.getPosition().y); + const zone = this.getZone(zoneDesc.i, zoneDesc.j); + zone.emitEmoteEvent(emoteEventMessage); + + } } diff --git a/back/src/Model/Zone.ts b/back/src/Model/Zone.ts index ca695317..ffb172bb 100644 --- a/back/src/Model/Zone.ts +++ b/back/src/Model/Zone.ts @@ -3,21 +3,19 @@ import {PositionInterface} from "_Model/PositionInterface"; import {Movable} from "./Movable"; import {Group} from "./Group"; import {ZoneSocket} from "../RoomManager"; +import {EmoteEventMessage} from "../Messages/generated/messages_pb"; export type EntersCallback = (thing: Movable, fromZone: Zone|null, listener: ZoneSocket) => void; export type MovesCallback = (thing: Movable, position: PositionInterface, listener: ZoneSocket) => void; export type LeavesCallback = (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => void; +export type EmoteCallback = (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => void; export class Zone { private things: Set = new Set(); private listeners: Set = new Set(); - - /** - * @param x For debugging purpose only - * @param y For debugging purpose only - */ - constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, public readonly x: number, public readonly y: number) { - } + + + constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, private onEmote: EmoteCallback, public readonly x: number, public readonly y: number) { } /** * A user/thing leaves the zone @@ -41,9 +39,7 @@ export class Zone { */ private notifyLeft(thing: Movable, newZone: Zone|null) { for (const listener of this.listeners) { - //if (listener !== thing && (newZone === null || !listener.listenedZones.has(newZone))) { - this.onLeaves(thing, newZone, listener); - //} + this.onLeaves(thing, newZone, listener); } } @@ -57,15 +53,6 @@ export class Zone { */ private notifyEnter(thing: Movable, oldZone: Zone|null, position: PositionInterface) { for (const listener of this.listeners) { - - /*if (listener === thing) { - continue; - } - if (oldZone === null || !listener.listenedZones.has(oldZone)) { - this.onEnters(thing, listener); - } else { - this.onMoves(thing, position, listener); - }*/ this.onEnters(thing, oldZone, listener); } } @@ -85,28 +72,6 @@ export class Zone { } } - /*public startListening(listener: User): void { - for (const thing of this.things) { - if (thing !== listener) { - this.onEnters(thing, listener); - } - } - - this.listeners.add(listener); - listener.listenedZones.add(this); - } - - public stopListening(listener: User): void { - for (const thing of this.things) { - if (thing !== listener) { - this.onLeaves(thing, listener); - } - } - - this.listeners.delete(listener); - listener.listenedZones.delete(this); - }*/ - public getThings(): Set { return this.things; } @@ -119,4 +84,11 @@ export class Zone { public removeListener(socket: ZoneSocket): void { this.listeners.delete(socket); } + + public emitEmoteEvent(emoteEventMessage: EmoteEventMessage) { + for (const listener of this.listeners) { + this.onEmote(emoteEventMessage, listener); + } + + } } diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 54215698..19266687 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -5,6 +5,7 @@ import { AdminPusherToBackMessage, AdminRoomMessage, BanMessage, + EmotePromptMessage, EmptyMessage, ItemEventMessage, JoinRoomMessage, @@ -71,6 +72,8 @@ const roomManager: IRoomManagerServer = { socketManager.emitPlayGlobalMessage(room, message.getPlayglobalmessage() as PlayGlobalMessage); } else if (message.hasQueryjitsijwtmessage()){ socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage); + } else if (message.hasEmotepromptmessage()){ + socketManager.handleEmoteEventMessage(room, user, message.getEmotepromptmessage() as EmotePromptMessage); }else if (message.hasSendusermessage()) { const sendUserMessage = message.getSendusermessage(); if(sendUserMessage !== undefined) { diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 647afc95..c58b3d9f 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -26,7 +26,8 @@ import { GroupLeftZoneMessage, WorldFullWarningMessage, UserLeftZoneMessage, - BanUserMessage, RefreshRoomMessage, + EmoteEventMessage, + BanUserMessage, RefreshRoomMessage, EmotePromptMessage, } from "../Messages/generated/messages_pb"; import {User, UserSocket} from "../Model/User"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; @@ -67,6 +68,7 @@ export class SocketManager { private rooms: Map = new Map(); constructor() { + clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => { gaugeManager.incNbClientPerRoomGauge(roomId); }); @@ -263,7 +265,8 @@ export class SocketManager { GROUP_RADIUS, (thing: Movable, fromZone: Zone|null, listener: ZoneSocket) => this.onZoneEnter(thing, fromZone, listener), (thing: Movable, position:PositionInterface, listener: ZoneSocket) => this.onClientMove(thing, position, listener), - (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => this.onClientLeave(thing, newZone, listener) + (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => this.onClientLeave(thing, newZone, listener), + (emoteEventMessage:EmoteEventMessage, listener: ZoneSocket) => this.onEmote(emoteEventMessage, listener), ); gaugeManager.incNbRoomGauge(); this.rooms.set(roomId, world); @@ -339,6 +342,14 @@ export class SocketManager { } } + + private onEmote(emoteEventMessage: EmoteEventMessage, client: ZoneSocket) { + const subMessage = new SubToPusherMessage(); + subMessage.setEmoteeventmessage(emoteEventMessage); + + emitZoneMessage(subMessage, client); + } + private emitCreateUpdateGroupEvent(client: ZoneSocket, fromZone: Zone|null, group: Group): void { const position = group.getPosition(); const pointMessage = new PointMessage(); @@ -751,6 +762,13 @@ export class SocketManager { recipient.socket.write(clientMessage); }); } + + handleEmoteEventMessage(room: GameRoom, user: User, emotePromptMessage: EmotePromptMessage) { + const emoteEventMessage = new EmoteEventMessage(); + emoteEventMessage.setEmote(emotePromptMessage.getEmote()); + emoteEventMessage.setActoruserid(user.id); + room.emitEmoteEvent(user, emoteEventMessage); + } } export const socketManager = new SocketManager(); diff --git a/back/tests/GameRoomTest.ts b/back/tests/GameRoomTest.ts index 45721334..6bdc6912 100644 --- a/back/tests/GameRoomTest.ts +++ b/back/tests/GameRoomTest.ts @@ -5,6 +5,7 @@ import {Group} from "../src/Model/Group"; import {User, UserSocket} from "_Model/User"; import {JoinRoomMessage, PositionMessage} from "../src/Messages/generated/messages_pb"; import Direction = PositionMessage.Direction; +import {EmoteCallback} from "_Model/Zone"; function createMockUser(userId: number): User { return { @@ -33,6 +34,8 @@ function createJoinRoomMessage(uuid: string, x: number, y: number): JoinRoomMess return joinRoomMessage; } +const emote: EmoteCallback = (emoteEventMessage, listener): void => {} + describe("GameRoom", () => { it("should connect user1 and user2", () => { let connectCalledNumber: number = 0; @@ -43,7 +46,8 @@ describe("GameRoom", () => { } - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); @@ -72,7 +76,7 @@ describe("GameRoom", () => { } - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100)); @@ -101,7 +105,7 @@ describe("GameRoom", () => { disconnectCallNumber++; } - const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote); const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100)); diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 5901202f..24b171d9 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -23,7 +23,7 @@ describe("PositionNotifier", () => { moveTriggered = true; }, (thing: Movable) => { leaveTriggered = true; - }); + }, () => {}); const user1 = new User(1, 'test', '10.0.0.2', { x: 500, @@ -98,7 +98,7 @@ describe("PositionNotifier", () => { moveTriggered = true; }, (thing: Movable) => { leaveTriggered = true; - }); + }, () => {}); const user1 = new User(1, 'test', '10.0.0.2', { x: 500, diff --git a/back/yarn.lock b/back/yarn.lock index 9469a69d..8af760c8 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -1251,9 +1251,9 @@ has-values@^1.0.0: kind-of "^4.0.0" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== http-errors@1.7.2: version "1.7.2" diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json index 8d4db6cf..72d0aae4 100644 --- a/benchmark/package-lock.json +++ b/benchmark/package-lock.json @@ -230,9 +230,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "indent-string": { "version": "2.1.0", diff --git a/benchmark/yarn.lock b/benchmark/yarn.lock index d93e3667..f1209dcf 100644 --- a/benchmark/yarn.lock +++ b/benchmark/yarn.lock @@ -169,8 +169,8 @@ graceful-fs@^4.1.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" indent-string@^2.1.0: version "2.1.0" diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index adbbfe44..7ef44116 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -73,7 +73,6 @@ -
-