From a1d52b42655a954ab3f159a1b09f312a439f62af Mon Sep 17 00:00:00 2001 From: kharhamel Date: Wed, 31 Mar 2021 11:21:06 +0200 Subject: [PATCH] FEATURE: added the possibility toplay emotes --- back/src/Model/GameRoom.ts | 15 ++-- back/src/Model/PositionNotifier.ts | 15 +++- back/src/Model/Zone.ts | 54 +++--------- back/src/RoomManager.ts | 3 + back/src/Services/SocketManager.ts | 24 ++++- back/tests/GameRoomTest.ts | 10 ++- back/tests/PositionNotifierTest.ts | 4 +- .../resources/emotes/pipo-popupemotes001.png | Bin 0 -> 747 bytes .../resources/emotes/pipo-popupemotes002.png | Bin 0 -> 920 bytes .../resources/emotes/pipo-popupemotes021.png | Bin 0 -> 810 bytes .../dist/resources/emotes/taba-clap-emote.png | Bin 0 -> 1305 bytes .../emotes/taba-thumbsdown-emote.png | Bin 0 -> 1981 bytes .../resources/emotes/taba-thumbsup-emote.png | Bin 0 -> 1931 bytes front/src/Connexion/EmoteEventStream.ts | 19 ++++ front/src/Connexion/RoomConnection.ts | 22 ++++- front/src/Phaser/Entity/Character.ts | 24 ++++- .../Entity/PlayerTexturesLoadingManager.ts | 21 +++-- front/src/Phaser/Game/EmoteManager.ts | 83 ++++++++++++++++++ front/src/Phaser/Game/GameScene.ts | 9 ++ messages/protos/messages.proto | 14 +++ pusher/src/Controller/IoSocketController.ts | 5 +- pusher/src/Model/Zone.ts | 17 +++- pusher/src/Services/SocketManager.ts | 19 +++- 23 files changed, 286 insertions(+), 72 deletions(-) create mode 100644 front/dist/resources/emotes/pipo-popupemotes001.png create mode 100644 front/dist/resources/emotes/pipo-popupemotes002.png create mode 100644 front/dist/resources/emotes/pipo-popupemotes021.png create mode 100644 front/dist/resources/emotes/taba-clap-emote.png create mode 100644 front/dist/resources/emotes/taba-thumbsdown-emote.png create mode 100644 front/dist/resources/emotes/taba-thumbsup-emote.png create mode 100644 front/src/Connexion/EmoteEventStream.ts create mode 100644 front/src/Phaser/Game/EmoteManager.ts 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..5d5dcf03 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"; @@ -73,6 +74,9 @@ export class SocketManager { clientEventsEmitter.registerToClientLeave((clientUUid: string, roomId: string) => { gaugeManager.decNbClientPerRoomGauge(roomId); }); + + + //zoneMessageStream.stream.subscribe(myMessage); } public async handleJoinRoom(socket: UserSocket, joinRoomMessage: JoinRoomMessage): Promise<{ room: GameRoom; user: User }> { @@ -263,7 +267,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 +344,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 +764,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/front/dist/resources/emotes/pipo-popupemotes001.png b/front/dist/resources/emotes/pipo-popupemotes001.png new file mode 100644 index 0000000000000000000000000000000000000000..a3db6d6d088107ed91cbcc4e6259c278dd729ff9 GIT binary patch literal 747 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 P00000NkvXXu0mjf%DsGb literal 0 HcmV?d00001 diff --git a/front/dist/resources/emotes/taba-thumbsdown-emote.png b/front/dist/resources/emotes/taba-thumbsdown-emote.png new file mode 100644 index 0000000000000000000000000000000000000000..86e89c7b0e46be5f6e452563cee04cf1510b0c25 GIT binary patch literal 1981 zcmV;u2SWIXP)Px+cS%G+RCt{2oIh;hNEF7uU2Q>$A`0B1YhvjN$SM+4tCVTBpbP1|thPXR^=T%9o;Hrw3U&f#S|x*j@BunRZ6D7U&(-vj*if1G*GM6 z0{g61D_EAr_7ydx;1~So%#RH~I#{>cMXgptrE(CtZ>4g8bgC81z1V280 zEo*nc5CGDRQ1HX~G6B&1I919YcUsVO4fR6}08r9eK(&lwD0``2Fh zFDudh4+NkneDtXGfm`p8qS{l;myeJE7u92|0uqdCmflmU>leULV6?i}!m1@R08~ph0J3PYbWjK9TeS?^c038uqiPxJ#g#IC zn0D!Zl(P0e3&5z8Pu9Sj0N{g=uY~lTs$!I)%6t+ZxA!mxeHMhBc7r*~f={3d%kYzw zW&ot+s|o;FzA-V`d635j-mXJUcmO^%|Kp^h|Ap=aC8v!+AG6CbgYpX@6pV4fmsWh^ z`Y%2Br^9(vVc_8^fa(BHVMBA%w7RarwjH+6;U9qXU6t&I@SkyV=zpOAWKIESo*8HY zo@y929aK5>^Yh330qb;H6ar{EY$aA<89jyp@Q?uzX2aSs-DUoSbD(j5z!o4>vG*+j zumD`GR*@&6fyM>cbdW8;0GM4*BQJ*YoT$%5`BaIi;XUVo3Bi|!uW|sS;rngK5&&uW zQDVL?eX|ZHeE+BDf1dp>R{{B`u9jIK(4~N_A!_B}Z&sk1X9ny|0s!EtzyOq2;fJgM zagq&yETT35fI6;N5+)n`{h#*&n1`9O|9SQQOTc|(Vl*1z?(Sz`2fCUcs4AOaY@Qha zHn)M%9WoKRyZedJXcTxvLxk_)a*06KHR0e(!%vcJJ6J3o%&wW3QE zi>uJnlRWtWdv@r59{tazpqni4EA08Fz|?X%WiP2CNF}Wm8iY~VCyviM?5TvyDVis3 zmIcFZi1jNCUqqqtoFr!)&1{TgrO_|An-^= zT!h=5?Zt3i*HDLvPP@TAySe!hn|+e_ezyN+>pHd#5mtS(^1~!k0rKF>sNg@|{tv8z z@u;q8}i^s@j;`J&=}AEe+H{7`;i3d$LQ)oLZZ zGGt6lG(S%9_RBZ?{^#FP&dc)o)wapha#zP3lTAfs8()?mr!<}aRQNOvqsM@vRy_CB zf0g{m>wtVFkoj>M9!$rPH(!qLU#y`O(=?5rhRB{QtiE;khB;xA&hTZOF>!&qIxg`6&D^ z_`Bn)>VKgC^c!<@bc7Ed-m}lXe*G8o`CKa7$TUE~FZlb)4?PEr)A{+?i}`$xo0}g1 z+{_~j&!U1~@b`%?bT4R_5i~!}a-Nk|1;60$3tzed`i$7Q^98@)?-T!j$6lZfW5~OD P00000NkvXXu0mjfvI^`T literal 0 HcmV?d00001 diff --git a/front/dist/resources/emotes/taba-thumbsup-emote.png b/front/dist/resources/emotes/taba-thumbsup-emote.png new file mode 100644 index 0000000000000000000000000000000000000000..46bfc7b445d09672589ad410a0adb26abb30a73a GIT binary patch literal 1931 zcmV;62Xy#}P)Px+MM*?KRCt{2oUw1)I26Xe79BhWfrn@{a6`4(DT0%y!edr#aW>Vx4hC*MBxCX) zWaypYEzYJjW6>!-7_qat!vV%^&JYwCv*iZ)F%(5oq)a)@RUZ&ER%QL(qu%3tq~!pW zDpjgf>F3ld0Qma#tI^5l=NFH8=Nuj$yfV4g`}gnJ?-jq|KNtUv4S08V7p+zcjYcDL z&SJ5^Y&K)(ni^8^EB-BnQG#aSax0UDC>)Y7ddxyKb?^rAr7z_q$4@AY+ z^M~Cw`#l<6{Z#&oz8B>y6X5*(;?cx$QT6&Z>h*0j8VzPhkwJJ#eEwzt$qgx+KkT*< z_4{~OUg753GI_lUgiZM`1|hJS@mKj?gfC41g759Uv$dOv3;|$02rrROH)Isgo`o-} zY`!iCH^I-7DgSjDa9(?R@6hY@j2192kuL_qruliQ_Ho=+fDgo94U9G6^U^W9VT-jSy6c+q*HM}L z=35W*dA9m&itqBewtN%cb=PGr5M`fk!4viS=!PxSn)|4EZQOiYqUJpXn;4+c^}0;sZtf(if&cmN1GA%EHe&Q}Ft5k6g0dfYMy zZTT*JSMhT{38oeTxA*BJSOtC5Z?j;md2JSq{H#bbWi-0NWOAK{WDH8iVpIUPtOED$+ApD!_=r)Pe? z?HPYbT7u~07!Nm>EFg-`2(0>W;**apgwM|4`yPUWh_xfW?;$vf3vmIi`mEL*e2XR8 z5^%>%Zh|SFs!!knK_>~in%7RXmi!v0`PO4=Ef+SF6@+=(r0+%vKu~@LrUfWJ)AGUZ zOslqw@A>(*i}=Ao1R%K)6tIE(iq@jppO`AqO>nAJ^~3Ew+kl`z5VrtpUK>qZIZ1*a zAptkxJ4=AYVj*1wCA0n#-LRGFz4^Y7W?$Y|Hfst-EEWr?Au2vqAL8;eKWndOA;y(y zm8|*Px<_1OkK$Qs5lA-yQ1`+%{F>KJRUnyIm0a1$2ZD-Y%4gRFoCTpNzYO1H{M-r< zjjk~4wlRulED0$1$N)b0fut33q2MS^J!(KnK(+vO!Cyqw%E?-wO^GUyI0FO7v|uh( zRuI~#Y~No4$s?nK!2ox6-=!x}7Rwfv;x<@nD^$q-ySwig3Fa6jog01-ueOK|{ z0LiywK79D7E`vx{0w5ds;tVY7!Byoik1tjsvF|sPEr0waM)R%52A%(GBh~(_eCI!W zvuS^{{@>oGt}80FO`W4cd`$~v8-&(;hrX-$tOZ(S;iJj(@6pMzb<3g3ep9|Q0RF8Va1>*F8YY7uCyQTdyHYov3x1t!`M&S5E#6vlA3+lcIDh#>A^yVtQ&Ei|frz9ZpEg)Y3^L#JD4~}BsG)z?? z@dk@L-5A*NT?e5J-?i^LekuVd3jrt}Nu?m91+ffH=aD9pYi;%y;qzP>pVvD4;3!6b z?Bf99;~@N}qvGHf<;$v&D5`|I04TzD8H7doE_~19vjhA$^KHKlmoaQ*0QddO44nU~ z0518w^ZzG)>fyT_xg7t0{_Fuzf8whPLRbD5@!iyaX#&WMu$2zZ8&L6I2H!RaUyA?A z&jI*yWMc>qrt4T+U-5r9{x81`5E$~Q<#5R_%YW?$Vf1D)g#IpztUsSjt}&aAVtH@hko&_?f>7k*CAMgICk( zls(r!nOs}L*HOi<_%8Wok3&n0pt5ALz2#aJzv8>%TUP+Vh>bg6@hiSd{(n|7Zf<$@ RovZ)=002ovPDHLkV1fW^zxn_G literal 0 HcmV?d00001 diff --git a/front/src/Connexion/EmoteEventStream.ts b/front/src/Connexion/EmoteEventStream.ts new file mode 100644 index 00000000..97d0d213 --- /dev/null +++ b/front/src/Connexion/EmoteEventStream.ts @@ -0,0 +1,19 @@ +import {Subject} from "rxjs"; + +interface EmoteEvent { + userId: number, + emoteName: string, +} + +class EmoteEventStream { + + private _stream:Subject = new Subject(); + public stream = this._stream.asObservable(); + + + onMessage(userId: number, emoteName:string) { + this._stream.next({userId, emoteName}); + } +} + +export const emoteEventStream = new EmoteEventStream(); \ No newline at end of file diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 6edb9c45..fa462f50 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -27,6 +27,8 @@ import { SendJitsiJwtMessage, CharacterLayerMessage, PingMessage, + EmoteEventMessage, + EmotePromptMessage, SendUserMessage, BanUserMessage } from "../Messages/generated/messages_pb" @@ -47,6 +49,7 @@ import {adminMessagesService} from "./AdminMessagesService"; import {worldFullMessageStream} from "./WorldFullMessageStream"; import {worldFullWarningStream} from "./WorldFullWarningStream"; import {connectionManager} from "./ConnectionManager"; +import {emoteEventStream} from "./EmoteEventStream"; const manualPingDelay = 20000; @@ -124,7 +127,7 @@ export class RoomConnection implements RoomConnection { if (message.hasBatchmessage()) { for (const subMessage of (message.getBatchmessage() as BatchMessage).getPayloadList()) { - let event: string; + let event: string|null = null; let payload; if (subMessage.hasUsermovedmessage()) { event = EventMessage.USER_MOVED; @@ -144,11 +147,16 @@ export class RoomConnection implements RoomConnection { } else if (subMessage.hasItemeventmessage()) { event = EventMessage.ITEM_EVENT; payload = subMessage.getItemeventmessage(); + } else if (subMessage.hasEmoteeventmessage()) { + const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage; + emoteEventStream.onMessage(emoteMessage.getActoruserid(), emoteMessage.getEmote()); } else { throw new Error('Unexpected batch message type'); } - this.dispatch(event, payload); + if (event) { + this.dispatch(event, payload); + } } } else if (message.hasRoomjoinedmessage()) { const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage; @@ -599,4 +607,14 @@ export class RoomConnection implements RoomConnection { public isAdmin(): boolean { return this.hasTag('admin'); } + + public emitEmoteEvent(emoteName: string): void { + const emoteMessage = new EmotePromptMessage(); + emoteMessage.setEmote(emoteName) + + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setEmotepromptmessage(emoteMessage); + + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 9f2bd1fd..bb8c2fb0 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -5,6 +5,9 @@ import Container = Phaser.GameObjects.Container; import Sprite = Phaser.GameObjects.Sprite; import {TextureError} from "../../Exception/TextureError"; import {Companion} from "../Companion/Companion"; +import {getEmoteAnimName} from "../Game/EmoteManager"; + +const playerNameY = - 25; interface AnimationData { key: string; @@ -23,6 +26,7 @@ export abstract class Character extends Container { //private teleportation: Sprite; private invisible: boolean; public companion?: Companion; + private emote: Phaser.GameObjects.Sprite | null = null; constructor(scene: Phaser.Scene, x: number, @@ -54,7 +58,7 @@ export abstract class Character extends Container { }); this.add(this.teleportation);*/ - this.playerName = new BitmapText(scene, 0, - 25, 'main_font', name, 7); + this.playerName = new BitmapText(scene, 0, playerNameY, 'main_font', name, 7); this.playerName.setOrigin(0.5).setCenterAlign().setDepth(99999); this.add(this.playerName); @@ -225,7 +229,23 @@ export abstract class Character extends Container { this.scene.sys.updateList.remove(sprite); } } + this.list.forEach(objectContaining => objectContaining.destroy()) super.destroy(); - this.playerName.destroy(); + } + + playEmote(emoteKey: string) { + if (this.emote) return; + + this.playerName.setVisible(false); + this.emote = new Sprite(this.scene, 0, -40, emoteKey, 1); + this.emote.setDepth(99999); + this.add(this.emote); + this.scene.sys.updateList.add(this.emote); + this.emote.play(getEmoteAnimName(emoteKey)); + this.emote.on(Phaser.Animations.Events.SPRITE_ANIMATION_COMPLETE, () => { + this.emote?.destroy(); + this.emote = null; + this.playerName.setVisible(true); + }); } } diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 6d8b84c2..95f00a9e 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -2,6 +2,10 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import type {CharacterTexture} from "../../Connexion/LocalUser"; import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures"; +export interface FrameConfig { + frameWidth: number, + frameHeight: number, +} export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => { const returnArray:BodyResourceDescriptionInterface[][] = []; @@ -26,7 +30,10 @@ export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptio export const loadCustomTexture = (loaderPlugin: LoaderPlugin, texture: CharacterTexture) : Promise => { const name = 'customCharacterTexture'+texture.id; const playerResourceDescriptor: BodyResourceDescriptionInterface = {name, img: texture.url, level: texture.level} - return createLoadingPromise(loaderPlugin, playerResourceDescriptor); + return createLoadingPromise(loaderPlugin, playerResourceDescriptor, { + frameWidth: 32, + frameHeight: 32 + }); } export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, texturekeys:Array): Promise => { @@ -36,7 +43,10 @@ export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, textur //TODO refactor const playerResourceDescriptor = getRessourceDescriptor(textureKey); if (playerResourceDescriptor && !loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { - promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor)); + promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor, { + frameWidth: 32, + frameHeight: 32 + })); } }catch (err){ console.error(err); @@ -69,15 +79,12 @@ export const getRessourceDescriptor = (textureKey: string|BodyResourceDescriptio throw 'Could not find a data for texture '+textureName; } -const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) => { +export const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface, frameConfig: FrameConfig) => { return new Promise((res) => { if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { return res(playerResourceDescriptor); } - loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, { - frameWidth: 32, - frameHeight: 32 - }); + loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, frameConfig); loadPlugin.once('filecomplete-spritesheet-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor)); }); } diff --git a/front/src/Phaser/Game/EmoteManager.ts b/front/src/Phaser/Game/EmoteManager.ts new file mode 100644 index 00000000..b33952fe --- /dev/null +++ b/front/src/Phaser/Game/EmoteManager.ts @@ -0,0 +1,83 @@ +import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; +import {createLoadingPromise} from "../Entity/PlayerTexturesLoadingManager"; +import {emoteEventStream} from "../../Connexion/EmoteEventStream"; +import {GameScene} from "./GameScene"; + +enum RegisteredEmoteTypes { + short = 1, + long = 2, +} + +interface RegisteredEmote extends BodyResourceDescriptionInterface { + name: string; + img: string; + type: RegisteredEmoteTypes +} + +export const emotes: {[key: string]: RegisteredEmote} = { + 'emote-exclamation': {name: 'emote-exclamation', img: 'resources/emotes/pipo-popupemotes001.png', type: RegisteredEmoteTypes.short}, + 'emote-interrogation': {name: 'emote-interrogation', img: 'resources/emotes/pipo-popupemotes002.png', type: RegisteredEmoteTypes.short}, + 'emote-sleep': {name: 'emote-sleep', img: 'resources/emotes/pipo-popupemotes002.png', type: RegisteredEmoteTypes.short}, + 'emote-clap': {name: 'emote-clap', img: 'resources/emotes/taba-clap-emote.png', type: RegisteredEmoteTypes.short}, + 'emote-thumbsdown': {name: 'emote-thumbsdown', img: 'resources/emotes/taba-thumbsdown-emote.png', type: RegisteredEmoteTypes.long}, + 'emote-thumbsup': {name: 'emote-thumbsup', img: 'resources/emotes/taba-thumbsup-emote.png', type: RegisteredEmoteTypes.long}, +}; + +export const getEmoteAnimName = (emoteKey: string): string => { + return 'anim-'+emoteKey; +} + +export class EmoteManager { + + constructor(private scene: GameScene) { + + //todo: use a radial menu instead? + this.registerEmoteOnKey('keyup-Y', 'emote-clap'); + this.registerEmoteOnKey('keyup-U', 'emote-thumbsup'); + this.registerEmoteOnKey('keyup-I', 'emote-thumbsdown'); + this.registerEmoteOnKey('keyup-O', 'emote-exclamation'); + this.registerEmoteOnKey('keyup-P', 'emote-interrogation'); + this.registerEmoteOnKey('keyup-T', 'emote-sleep'); + + + emoteEventStream.stream.subscribe((event) => { + const actor = this.scene.MapPlayersByKey.get(event.userId); + if (actor) { + this.lazyLoadEmoteTexture(event.emoteName).then(emoteKey => { + actor.playEmote(emoteKey); + }) + } + }) + } + + private registerEmoteOnKey(keyboardKey: string, emoteKey: string) { + this.scene.input.keyboard.on(keyboardKey, () => { + this.scene.connection?.emitEmoteEvent(emoteKey); + this.lazyLoadEmoteTexture(emoteKey).then(emoteKey => { + this.scene.CurrentPlayer.playEmote(emoteKey); + }) + }); + } + + lazyLoadEmoteTexture(textureKey: string): Promise { + const emoteDescriptor = emotes[textureKey]; + if (emoteDescriptor === undefined) { + throw 'Emote not found!'; + } + const loadPromise = createLoadingPromise(this.scene.load, emoteDescriptor, { + frameWidth: 32, + frameHeight: 32, + }); + this.scene.load.start(); + return loadPromise.then(() => { + const frameConfig = emoteDescriptor.type === RegisteredEmoteTypes.short ? {frames: [0,1,2]} : {frames : [0,1,2,3,4,5,6,7]}; + this.scene.anims.create({ + key: getEmoteAnimName(textureKey), + frames: this.scene.anims.generateFrameNumbers(textureKey, frameConfig), + frameRate: 3, + repeat: 2, + }); + return textureKey; + }); + } +} \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 748897c5..d7b635c0 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -91,6 +91,7 @@ import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick"; import {waScaleManager} from "../Services/WaScaleManager"; +import {EmoteManager} from "./EmoteManager"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -189,6 +190,7 @@ export class GameScene extends DirtyScene implements CenterListener { private physicsEnabled: boolean = true; private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time. private onVisibilityChangeCallback: () => void; + private emoteManager!: EmoteManager; constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { super({ @@ -226,6 +228,11 @@ export class GameScene extends DirtyScene implements CenterListener { this.load.image(joystickBaseKey, joystickBaseImg); this.load.image(joystickThumbKey, joystickThumbImg); } + //todo: in an emote manager. + this.load.spritesheet('emote-music', 'resources/emotes/pipo-popupemotes005.png', { + frameHeight: 32, + frameWidth: 32, + }); this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) { @@ -509,6 +516,8 @@ export class GameScene extends DirtyScene implements CenterListener { } document.addEventListener('visibilitychange', this.onVisibilityChangeCallback); + + this.emoteManager = new EmoteManager(this); } /** diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 52ca4d50..3a5afb57 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -66,6 +66,15 @@ message ReportPlayerMessage { string reportComment = 2; } +message EmotePromptMessage { + string emote = 2; +} + +message EmoteEventMessage { + int32 actorUserId = 1; + string emote = 2; +} + message QueryJitsiJwtMessage { string jitsiRoom = 1; string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map! @@ -84,6 +93,7 @@ message ClientToServerMessage { StopGlobalMessage stopGlobalMessage = 10; ReportPlayerMessage reportPlayerMessage = 11; QueryJitsiJwtMessage queryJitsiJwtMessage = 12; + EmotePromptMessage emotePromptMessage = 13; } } @@ -122,6 +132,7 @@ message SubMessage { UserJoinedMessage userJoinedMessage = 4; UserLeftMessage userLeftMessage = 5; ItemEventMessage itemEventMessage = 6; + EmoteEventMessage emoteEventMessage = 7; } } @@ -247,6 +258,7 @@ message ServerToClientMessage { WorldFullMessage worldFullMessage = 16; RefreshRoomMessage refreshRoomMessage = 17; WorldConnexionMessage worldConnexionMessage = 18; + EmoteEventMessage emoteEventMessage = 19; } } @@ -317,6 +329,7 @@ message PusherToBackMessage { QueryJitsiJwtMessage queryJitsiJwtMessage = 11; SendUserMessage sendUserMessage = 12; BanUserMessage banUserMessage = 13; + EmotePromptMessage emotePromptMessage = 14; } } @@ -334,6 +347,7 @@ message SubToPusherMessage { ItemEventMessage itemEventMessage = 6; SendUserMessage sendUserMessage = 7; BanUserMessage banUserMessage = 8; + EmoteEventMessage emoteEventMessage = 9; } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index b3e38e03..15be68c7 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -12,7 +12,8 @@ import { WebRtcSignalToServerMessage, PlayGlobalMessage, ReportPlayerMessage, - QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, CompanionMessage + EmoteEventMessage, + QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, CompanionMessage, EmotePromptMessage } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {TemplatedApp} from "uWebSockets.js" @@ -330,6 +331,8 @@ export class IoSocketController { socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage); } else if (message.hasQueryjitsijwtmessage()){ socketManager.handleQueryJitsiJwtMessage(client, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage); + } else if (message.hasEmotepromptmessage()){ + socketManager.handleEmotePromptMessage(client, message.getEmotepromptmessage() as EmotePromptMessage); } /* Ok is false if backpressure was built up, wait for drain */ diff --git a/pusher/src/Model/Zone.ts b/pusher/src/Model/Zone.ts index 3f39a5ed..5c50ef00 100644 --- a/pusher/src/Model/Zone.ts +++ b/pusher/src/Model/Zone.ts @@ -6,13 +6,11 @@ import { PointMessage, PositionMessage, UserJoinedMessage, UserJoinedZoneMessage, UserLeftZoneMessage, UserMovedMessage, ZoneMessage, + EmoteEventMessage, CompanionMessage } from "../Messages/generated/messages_pb"; -import * as messages_pb from "../Messages/generated/messages_pb"; import {ClientReadableStream} from "grpc"; import {PositionDispatcher} from "_Model/PositionDispatcher"; -import {socketManager} from "../Services/SocketManager"; -import {ProtobufUtils} from "_Model/Websocket/ProtobufUtils"; import Debug from "debug"; const debug = Debug("zone"); @@ -24,6 +22,7 @@ export interface ZoneEventListener { onGroupEnters(group: GroupDescriptor, listener: ExSocketInterface): void; onGroupMoves(group: GroupDescriptor, listener: ExSocketInterface): void; onGroupLeaves(groupId: number, listener: ExSocketInterface): void; + onEmote(emoteMessage: EmoteEventMessage, listener: ExSocketInterface): void; } /*export type EntersCallback = (thing: Movable, listener: User) => void; @@ -184,6 +183,9 @@ export class Zone { userDescriptor.update(userMovedMessage); this.notifyUserMove(userDescriptor); + } else if(message.hasEmoteeventmessage()) { + const emoteEventMessage = message.getEmoteeventmessage() as EmoteEventMessage; + this.notifyEmote(emoteEventMessage); } else { throw new Error('Unexpected message'); } @@ -262,6 +264,15 @@ export class Zone { } } + private notifyEmote(emoteMessage: EmoteEventMessage) { + for (const listener of this.listeners) { + if (listener.userId === emoteMessage.getActoruserid()) { + continue; + } + this.socketListener.onEmote(emoteMessage, listener); + } + } + /** * Notify listeners of this zone that this group left */ diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index d692186a..78bbe330 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -23,7 +23,8 @@ import { WorldConnexionMessage, AdminPusherToBackMessage, ServerToAdminClientMessage, - UserJoinedRoomMessage, UserLeftRoomMessage, AdminMessage, BanMessage, RefreshRoomMessage + EmoteEventMessage, + UserJoinedRoomMessage, UserLeftRoomMessage, AdminMessage, BanMessage, RefreshRoomMessage, EmotePromptMessage } from "../Messages/generated/messages_pb"; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; import {JITSI_ISS, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable"; @@ -254,6 +255,15 @@ export class SocketManager implements ZoneEventListener { this.handleViewport(client, viewport.toObject()) } + + + onEmote(emoteMessage: EmoteEventMessage, listener: ExSocketInterface): void { + const subMessage = new SubMessage(); + subMessage.setEmoteeventmessage(emoteMessage); + + emitInBatch(listener, subMessage); + } + // Useless now, will be useful again if we allow editing details in game handleSetPlayerDetails(client: ExSocketInterface, playerDetailsMessage: SetPlayerDetailsMessage) { const pusherToBackMessage = new PusherToBackMessage(); @@ -578,6 +588,13 @@ export class SocketManager implements ZoneEventListener { this.updateRoomWithAdminData(room); } + + handleEmotePromptMessage(client: ExSocketInterface, emoteEventmessage: EmotePromptMessage) { + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setEmotepromptmessage(emoteEventmessage); + + client.backConnection.write(pusherToBackMessage); + } } export const socketManager = new SocketManager();