From f65491e7098d4875ac506557ede5fb1398062415 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Mon, 6 Dec 2021 14:37:06 +0100 Subject: [PATCH 01/37] Remove unused import --- front/src/Connexion/ConnectionManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 05d84367..883e705e 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -1,5 +1,5 @@ import Axios from "axios"; -import { PUSHER_URL, START_ROOM_URL } from "../Enum/EnvironmentVariable"; +import { PUSHER_URL } from "../Enum/EnvironmentVariable"; import { RoomConnection } from "./RoomConnection"; import type { OnConnectInterface, PositionInterface, ViewportInterface } from "./ConnexionModels"; import { GameConnexionTypes, urlManager } from "../Url/UrlManager"; From f340e8114da5cd8329c5dd5ae340d8ec96d6df21 Mon Sep 17 00:00:00 2001 From: danb Date: Tue, 19 Oct 2021 15:35:19 +0200 Subject: [PATCH 02/37] Implement automatic following of other players. * initiate following by reacting to the interact event * subscribe to remote player and update positions in relation to them instead of reacting to user input * cancel following if the user moves actively again --- front/src/Phaser/Player/Player.ts | 107 ++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index a1924457..0e576235 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -1,15 +1,18 @@ import { PlayerAnimationDirections } from "./Animation"; import type { GameScene } from "../Game/GameScene"; -import { UserInputEvent, UserInputManager } from "../UserInput/UserInputManager"; +import { ActiveEventList, UserInputEvent, UserInputManager } from "../UserInput/UserInputManager"; import { Character } from "../Entity/Character"; +import type { RemotePlayer } from "../Entity/RemotePlayer"; import { userMovingStore } from "../../Stores/GameStore"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; export class Player extends Character { - private previousDirection: string = PlayerAnimationDirections.Down; + private previousDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; private wasMoving: boolean = false; + private timeCounter: number = 0; + private follow: { followPlayer: RemotePlayer; direction: PlayerAnimationDirections } | null = null; constructor( Scene: GameScene, @@ -29,17 +32,17 @@ export class Player extends Character { this.getBody().setImmovable(false); } - moveUser(delta: number): void { + private inputStep(activeEvents: ActiveEventList, delta: number) { //if user client on shift, camera and player speed let direction = null; let moving = false; - const activeEvents = this.userInputManager.getEventListForGameTick(); const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; const moveAmount = speedMultiplier * 20; let x = 0; let y = 0; + if (activeEvents.get(UserInputEvent.MoveUp)) { y = -moveAmount; direction = PlayerAnimationDirections.Up; @@ -49,6 +52,7 @@ export class Player extends Character { direction = PlayerAnimationDirections.Down; moving = true; } + if (activeEvents.get(UserInputEvent.MoveLeft)) { x = -moveAmount; direction = PlayerAnimationDirections.Left; @@ -58,6 +62,7 @@ export class Player extends Character { direction = PlayerAnimationDirections.Right; moving = true; } + moving = moving || activeEvents.get(UserInputEvent.JoystickMove); if (x !== 0 || y !== 0) { @@ -89,10 +94,104 @@ export class Player extends Character { if (direction !== null) { this.previousDirection = direction; } + this.wasMoving = moving; userMovingStore.set(moving); } + private followStep(activeEvents: ActiveEventList, delta: number) { + if (this.follow === null) { + return; + } + + this.timeCounter += delta; + if (this.timeCounter < 128) { + return; + } + this.timeCounter = 0; + + const xDist = this.follow.followPlayer.x - this.x; + const yDist = this.follow.followPlayer.y - this.y; + + const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); + + if (distance < 650) { + this.stop(); + } else { + const moveAmount = 9 * 20; + const xDir = xDist / Math.sqrt(distance); + const yDir = yDist / Math.sqrt(distance); + + this.move(xDir * moveAmount, yDir * moveAmount); + + if (Math.abs(xDist) > Math.abs(yDist)) { + if (xDist < 0) { + this.follow.direction = PlayerAnimationDirections.Left; + } else { + this.follow.direction = PlayerAnimationDirections.Right; + } + } else { + if (yDist < 0) { + this.follow.direction = PlayerAnimationDirections.Up; + } else { + this.follow.direction = PlayerAnimationDirections.Down; + } + } + } + + this.emit(hasMovedEventName, { + moving: true, + direction: this.follow.direction, + x: this.x, + y: this.y, + }); + + this.previousDirection = this.follow.direction; + + this.wasMoving = true; + userMovingStore.set(true); + } + + moveUser(delta: number): void { + const activeEvents = this.userInputManager.getEventListForGameTick(); + + if (activeEvents.get(UserInputEvent.Interact)) { + const sortedPlayers = Array.from(this.scene.MapPlayersByKey.values()).sort((p1, p2) => { + const distToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); + const distToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); + if (distToP1 > distToP2) { + return 1; + } else if (distToP1 < distToP2) { + return -1; + } else { + return 0; + } + }); + + if (typeof sortedPlayers !== "undefined" && sortedPlayers.length > 0) { + this.follow = { + followPlayer: sortedPlayers[0], + direction: this.previousDirection, + }; + } + } + + if ( + activeEvents.get(UserInputEvent.MoveUp) || + activeEvents.get(UserInputEvent.MoveDown) || + activeEvents.get(UserInputEvent.MoveLeft) || + activeEvents.get(UserInputEvent.MoveRight) + ) { + this.follow = null; + } + + if (this.follow === null) { + this.inputStep(activeEvents, delta); + } else { + this.followStep(activeEvents, delta); + } + } + public isMoving(): boolean { return this.wasMoving; } From 7c7144527c09a01d134b154a20bbe0282bd0854b Mon Sep 17 00:00:00 2001 From: danb Date: Thu, 28 Oct 2021 16:20:57 +0200 Subject: [PATCH 03/37] Add minimum distance to enable following --- front/src/Phaser/Player/Player.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 0e576235..5846c10e 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -157,22 +157,26 @@ export class Player extends Character { if (activeEvents.get(UserInputEvent.Interact)) { const sortedPlayers = Array.from(this.scene.MapPlayersByKey.values()).sort((p1, p2) => { - const distToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); - const distToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); - if (distToP1 > distToP2) { + const sdistToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); + const sdistToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); + if (sdistToP1 > sdistToP2) { return 1; - } else if (distToP1 < distToP2) { + } else if (sdistToP1 < sdistToP2) { return -1; } else { return 0; } }); + const minFollowDist = 10000; if (typeof sortedPlayers !== "undefined" && sortedPlayers.length > 0) { - this.follow = { - followPlayer: sortedPlayers[0], - direction: this.previousDirection, - }; + const sdist = Math.pow(sortedPlayers[0].x - this.x, 2) + Math.pow(sortedPlayers[0].y - this.y, 2); + if (sdist < minFollowDist) { + this.follow = { + followPlayer: sortedPlayers[0], + direction: this.previousDirection, + }; + } } } From 372dda792f115814107b1b05013d85425bcc00bf Mon Sep 17 00:00:00 2001 From: danb Date: Thu, 18 Nov 2021 12:23:10 +0100 Subject: [PATCH 04/37] Fix issue of interrupted conversation in follow-mode --- front/src/Phaser/Player/Player.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 5846c10e..17ad1cc1 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -100,6 +100,8 @@ export class Player extends Character { } private followStep(activeEvents: ActiveEventList, delta: number) { + let moving = false; + if (this.follow === null) { return; } @@ -137,10 +139,12 @@ export class Player extends Character { this.follow.direction = PlayerAnimationDirections.Down; } } + + moving = true; } this.emit(hasMovedEventName, { - moving: true, + moving: moving, direction: this.follow.direction, x: this.x, y: this.y, @@ -148,8 +152,8 @@ export class Player extends Character { this.previousDirection = this.follow.direction; - this.wasMoving = true; - userMovingStore.set(true); + this.wasMoving = moving; + userMovingStore.set(moving); } moveUser(delta: number): void { From e7f1395809dc8c088762235e3f0f1caecabc9e5b Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 30 Nov 2021 11:48:59 +0100 Subject: [PATCH 05/37] Stop before running into followed Avatar; stop sprite animation --- front/src/Phaser/Game/PlayerMovement.ts | 2 +- front/src/Phaser/Player/Player.ts | 2 +- front/tests/Phaser/Game/PlayerMovementTest.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Game/PlayerMovement.ts b/front/src/Phaser/Game/PlayerMovement.ts index 7758f010..274cbee1 100644 --- a/front/src/Phaser/Game/PlayerMovement.ts +++ b/front/src/Phaser/Game/PlayerMovement.ts @@ -41,7 +41,7 @@ export class PlayerMovement { oldX: this.startPosition.x, oldY: this.startPosition.y, direction: this.endPosition.direction, - moving: true, + moving: this.endPosition.moving, }; } } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 17ad1cc1..adf7a302 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -117,7 +117,7 @@ export class Player extends Character { const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); - if (distance < 650) { + if (distance < 2000) { this.stop(); } else { const moveAmount = 9 * 20; diff --git a/front/tests/Phaser/Game/PlayerMovementTest.ts b/front/tests/Phaser/Game/PlayerMovementTest.ts index 70f7b95d..bd5f40b4 100644 --- a/front/tests/Phaser/Game/PlayerMovementTest.ts +++ b/front/tests/Phaser/Game/PlayerMovementTest.ts @@ -74,7 +74,7 @@ describe("Interpolation / Extrapolation", () => { }); }); - it("should should keep moving until it stops", () => { + it("should keep moving until it stops", () => { const playerMovement = new PlayerMovement({ x: 100, y: 200 }, 42000, @@ -95,7 +95,7 @@ describe("Interpolation / Extrapolation", () => { oldX: 100, oldY: 200, direction: 'up', - moving: true + moving: false }); }); }) From b30d0989c86a45e9c438959759433291ecb9cb89 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 7 Dec 2021 22:14:51 +0100 Subject: [PATCH 06/37] Fix indentation in messages.proto --- messages/protos/messages.proto | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 117ab582..2dfd9aa2 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -67,12 +67,12 @@ message ReportPlayerMessage { } message EmotePromptMessage { - string emote = 2; + string emote = 2; } message EmoteEventMessage { - int32 actorUserId = 1; - string emote = 2; + int32 actorUserId = 1; + string emote = 2; } message QueryJitsiJwtMessage { @@ -236,14 +236,14 @@ message SendUserMessage{ message WorldFullWarningMessage{ } message WorldFullWarningToRoomMessage{ - string roomId = 1; + string roomId = 1; } message RefreshRoomPromptMessage{ - string roomId = 1; + string roomId = 1; } message RefreshRoomMessage{ - string roomId = 1; - int32 versionNumber = 2; + string roomId = 1; + int32 versionNumber = 2; } message WorldFullMessage{ From 1fca99c0d1317b97b6ace0bca3d892eb7363128b Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sun, 12 Dec 2021 00:24:39 +0100 Subject: [PATCH 07/37] Send follow me request to all players --- back/src/RoomManager.ts | 7 +++++++ back/src/Services/SocketManager.ts | 12 ++++++++++++ front/src/Connexion/RoomConnection.ts | 12 ++++++++++++ messages/protos/messages.proto | 15 +++++++++++++++ pusher/src/Controller/IoSocketController.ts | 6 ++++++ pusher/src/Services/SocketManager.ts | 7 +++++++ 6 files changed, 59 insertions(+) diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 8dbde018..16a9d023 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -8,6 +8,7 @@ import { BatchToPusherMessage, BatchToPusherRoomMessage, EmotePromptMessage, + FollowMeRequestMessage, EmptyMessage, ItemEventMessage, JoinRoomMessage, @@ -116,6 +117,12 @@ const roomManager: IRoomManagerServer = { user, message.getEmotepromptmessage() as EmotePromptMessage ); + } else if (message.hasFollowmerequestmessage()) { + socketManager.handleFollowMeRequestMessage( + room, + user, + message.getFollowmerequestmessage() as FollowMeRequestMessage + ); } 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 8989df75..069e298b 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -30,6 +30,7 @@ import { BanUserMessage, RefreshRoomMessage, EmotePromptMessage, + FollowMeRequestMessage, VariableMessage, BatchToPusherRoomMessage, SubToPusherRoomMessage, @@ -833,6 +834,17 @@ export class SocketManager { emoteEventMessage.setActoruserid(user.id); room.emitEmoteEvent(user, emoteEventMessage); } + + handleFollowMeRequestMessage(room: GameRoom, user: User, requestMessage: FollowMeRequestMessage) { + console.log("Handling follow me request message"); + console.log(user.name); + requestMessage.setPlayername(user.name); + room.getUsers().forEach((recipient) => { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowmerequestmessage(requestMessage); + recipient.socket.write(clientMessage); + }); + } } export const socketManager = new SocketManager(); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 9e4025b1..de7cfcc2 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -30,6 +30,7 @@ import { PingMessage, EmoteEventMessage, EmotePromptMessage, + FollowMeRequestMessage, SendUserMessage, BanUserMessage, VariableMessage, @@ -257,6 +258,9 @@ export class RoomConnection implements RoomConnection { warningContainerStore.activateWarningContainer(); } else if (message.hasRefreshroommessage()) { //todo: implement a way to notify the user the room was refreshed. + } else if (message.hasFollowmerequestmessage()) { + const requestMessage = message.getFollowmerequestmessage() as FollowMeRequestMessage; + console.log("Follow me request from " + requestMessage.getPlayername()); } else if (message.hasErrormessage()) { const errorMessage = message.getErrormessage() as ErrorMessage; console.error("An error occurred server side: " + errorMessage.getMessage()); @@ -712,6 +716,14 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } + public emitFollowMeRequest(): void { + console.log("Emitting follow me request"); + const message = new FollowMeRequestMessage(); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setFollowmerequestmessage(message); + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + public getAllTags(): string[] { return this.tags; } diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 2dfd9aa2..e9e75205 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -80,6 +80,15 @@ message QueryJitsiJwtMessage { string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map! } +message FollowMeRequestMessage { + string playerName = 1; +} + +message FollowMeResponseMessage { + string playerName = 1; + bool accepted = 2; +} + message ClientToServerMessage { oneof message { UserMovesMessage userMovesMessage = 2; @@ -95,6 +104,8 @@ message ClientToServerMessage { QueryJitsiJwtMessage queryJitsiJwtMessage = 12; EmotePromptMessage emotePromptMessage = 13; VariableMessage variableMessage = 14; + FollowMeRequestMessage followMeRequestMessage = 15; + FollowMeResponseMessage followMeResponseMessage = 16; } } @@ -285,6 +296,8 @@ message ServerToClientMessage { WorldConnexionMessage worldConnexionMessage = 18; //EmoteEventMessage emoteEventMessage = 19; TokenExpiredMessage tokenExpiredMessage = 20; + FollowMeRequestMessage followMeRequestMessage = 21; + FollowMeResponseMessage followMeResponseMessage = 22; } } @@ -365,6 +378,8 @@ message PusherToBackMessage { BanUserMessage banUserMessage = 13; EmotePromptMessage emotePromptMessage = 14; VariableMessage variableMessage = 15; + FollowMeRequestMessage followMeRequestMessage = 16; + FollowMeResponseMessage followMeResponseMessage = 17; } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index df29db57..5d66c4df 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -17,6 +17,7 @@ import { ServerToClientMessage, CompanionMessage, EmotePromptMessage, + FollowMeRequestMessage, VariableMessage, } from "../Messages/generated/messages_pb"; import { UserMovesMessage } from "../Messages/generated/messages_pb"; @@ -469,6 +470,11 @@ export class IoSocketController { client, message.getEmotepromptmessage() as EmotePromptMessage ); + } else if (message.hasFollowmerequestmessage()) { + socketManager.handleFollowMeRequest( + client, + message.getFollowmerequestmessage() as FollowMeRequestMessage + ); } /* Ok is false if backpressure was built up, wait for drain */ diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 4f4b086f..2df167bd 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -8,6 +8,7 @@ import { CharacterLayerMessage, EmoteEventMessage, EmotePromptMessage, + FollowMeRequestMessage, GroupDeleteMessage, ItemEventMessage, JoinRoomMessage, @@ -269,6 +270,12 @@ export class SocketManager implements ZoneEventListener { this.handleViewport(client, viewport.toObject()); } + handleFollowMeRequest(client: ExSocketInterface, requestMessage: FollowMeRequestMessage): void { + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setFollowmerequestmessage(requestMessage); + client.backConnection.write(pusherToBackMessage); + } + onEmote(emoteMessage: EmoteEventMessage, listener: ExSocketInterface): void { const subMessage = new SubMessage(); subMessage.setEmoteeventmessage(emoteMessage); From d6ef60a3d898238265f247d23951d78cf0365585 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sun, 12 Dec 2021 02:17:39 +0100 Subject: [PATCH 08/37] Send request to group members only --- back/src/Model/Group.ts | 10 ++++++++++ back/src/Services/SocketManager.ts | 21 +++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 931ddda5..570eaedf 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -106,6 +106,16 @@ export class Group implements Movable { return this.users.size <= 1; } + includes(user: User): boolean { + let found = false; + this.users.forEach((currentUser: User) => { + if (currentUser.name === user.name) { + found = true; + } + }); + return found; + } + join(user: User): void { // Broadcast on the right event this.connectCallback(user, this); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 069e298b..cc950163 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -836,13 +836,22 @@ export class SocketManager { } handleFollowMeRequestMessage(room: GameRoom, user: User, requestMessage: FollowMeRequestMessage) { - console.log("Handling follow me request message"); - console.log(user.name); + // Find group including the requesting user + let foundGroups = room.getGroups().filter((grp) => grp.includes(user)); + if (!foundGroups[0]) { + return; + } + let group = foundGroups[0]; + + // Send invitations to other group members requestMessage.setPlayername(user.name); - room.getUsers().forEach((recipient) => { - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowmerequestmessage(requestMessage); - recipient.socket.write(clientMessage); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowmerequestmessage(requestMessage); + group.getUsers().forEach((currentUser: User) => { + if (user.name !== currentUser.name) { + console.log("Inviting " + currentUser.name + " to follow " + user.name); + currentUser.socket.write(clientMessage); + } }); } } From 0a410d289d8f0fdf553f70dd58da0ec830306a13 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sun, 12 Dec 2021 16:56:26 +0100 Subject: [PATCH 09/37] Implement follow request / confirmation UI --- back/src/Model/GameRoom.ts | 25 ++ back/src/RoomManager.ts | 22 +- back/src/Services/SocketManager.ts | 44 ++-- front/src/Components/App.svelte | 7 + .../InteractMenu/InteractMenu.svelte | 238 ++++++++++++++++++ front/src/Connexion/RoomConnection.ts | 79 +++++- front/src/Phaser/Player/Player.ts | 63 +++-- front/src/Stores/InteractStore.ts | 17 ++ messages/protos/messages.proto | 28 ++- pusher/src/Controller/IoSocketController.ts | 17 +- pusher/src/Services/SocketManager.ts | 20 +- 11 files changed, 481 insertions(+), 79 deletions(-) create mode 100644 front/src/Components/InteractMenu/InteractMenu.svelte create mode 100644 front/src/Stores/InteractStore.ts diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 5c114f19..1b0db5eb 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -14,6 +14,7 @@ import { SubToPusherRoomMessage, VariableMessage, VariableWithTagMessage, + ServerToClientMessage, } from "../Messages/generated/messages_pb"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { RoomSocket, ZoneSocket } from "src/RoomManager"; @@ -95,10 +96,20 @@ export class GameRoom { return Array.from(this.groups.values()); } + public getGroupIncludingUser(user: User): Group | undefined { + const foundGroups = this.getGroups().filter((grp) => grp.includes(user)); + return foundGroups[0]; + } + public getUsers(): Map { return this.users; } + public getUserByName(name: string): User | undefined { + let foundUsers = Array.from(this.users.values()); + foundUsers = foundUsers.filter((user: User) => user.name === name); + return foundUsers[0]; + } public getUserByUuid(uuid: string): User | undefined { return this.usersByUuid.get(uuid); } @@ -226,6 +237,20 @@ export class GameRoom { } } + public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void { + this.getGroupIncludingUser(user) + ?.getUsers() + .forEach((currentUser: User) => { + if (currentUser.name !== user.name) { + currentUser.socket.write(message); + } + }); + } + + public sendToUserWithName(name: string, message: ServerToClientMessage): void { + this.getUserByName(name)?.socket.write(message); + } + setSilent(user: User, silent: boolean) { if (user.silent === silent) { return; diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 16a9d023..9020c130 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -8,7 +8,9 @@ import { BatchToPusherMessage, BatchToPusherRoomMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, EmptyMessage, ItemEventMessage, JoinRoomMessage, @@ -117,11 +119,23 @@ const roomManager: IRoomManagerServer = { user, message.getEmotepromptmessage() as EmotePromptMessage ); - } else if (message.hasFollowmerequestmessage()) { - socketManager.handleFollowMeRequestMessage( + } else if (message.hasFollowrequestmessage()) { + socketManager.handleFollowRequestMessage( room, user, - message.getFollowmerequestmessage() as FollowMeRequestMessage + message.getFollowrequestmessage() as FollowRequestMessage + ); + } else if (message.hasFollowconfirmationmessage()) { + socketManager.handleFollowConfirmationMessage( + room, + user, + message.getFollowconfirmationmessage() as FollowConfirmationMessage + ); + } else if (message.hasFollowabortmessage()) { + socketManager.handleFollowAbortMessage( + room, + user, + message.getFollowabortmessage() as FollowAbortMessage ); } else if (message.hasSendusermessage()) { const sendUserMessage = message.getSendusermessage(); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index cc950163..3ab53719 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -30,7 +30,9 @@ import { BanUserMessage, RefreshRoomMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, VariableMessage, BatchToPusherRoomMessage, SubToPusherRoomMessage, @@ -835,24 +837,30 @@ export class SocketManager { room.emitEmoteEvent(user, emoteEventMessage); } - handleFollowMeRequestMessage(room: GameRoom, user: User, requestMessage: FollowMeRequestMessage) { - // Find group including the requesting user - let foundGroups = room.getGroups().filter((grp) => grp.includes(user)); - if (!foundGroups[0]) { - return; - } - let group = foundGroups[0]; - - // Send invitations to other group members - requestMessage.setPlayername(user.name); + handleFollowRequestMessage(room: GameRoom, user: User, message: FollowRequestMessage) { const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowmerequestmessage(requestMessage); - group.getUsers().forEach((currentUser: User) => { - if (user.name !== currentUser.name) { - console.log("Inviting " + currentUser.name + " to follow " + user.name); - currentUser.socket.write(clientMessage); - } - }); + clientMessage.setFollowrequestmessage(message); + room.sendToOthersInGroupIncludingUser(user, clientMessage); + } + + handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowconfirmationmessage(message); + room.sendToUserWithName(message.getLeader(), clientMessage); + } + + handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { + if (message.getRole() === "leader") { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + room.sendToOthersInGroupIncludingUser(user, clientMessage); + } else { + const recipient = message.getPlayername(); + message.setPlayername(user.name); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + room.sendToUserWithName(recipient, clientMessage); + } } } diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index 4886cc4e..b14801c9 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -42,6 +42,8 @@ import AudioManager from "./AudioManager/AudioManager.svelte"; import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore"; import ReportMenu from "./ReportMenu/ReportMenu.svelte"; + import { followStateStore, followStates } from "../Stores/InteractStore"; + import InteractMenu from "./InteractMenu/InteractMenu.svelte"; export let game: Game; @@ -102,6 +104,11 @@ {/if} + {#if $followStateStore !== followStates.off} +
+ +
+ {/if} {#if $menuIconVisiblilityStore}
diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/InteractMenu/InteractMenu.svelte new file mode 100644 index 00000000..cb78f5bc --- /dev/null +++ b/front/src/Components/InteractMenu/InteractMenu.svelte @@ -0,0 +1,238 @@ + + + + + +{#if followState === followStates.requesting} +
+
+

Interaction

+
+ {#if followRole === followRoles.follower} +
+

Do you want to follow {followUsers[0]}?

+
+
+ + +
+ {:else if followRole === followRoles.leader} +
+

Ask others to follow you?

+
+
+ + +
+ {/if} +
+{/if} + +{#if followState === followStates.ending} +
+
+

Interaction

+
+ {#if followRole === followRoles.follower} +
+

Do you want to stop following {followUsers[0]}?

+
+ {:else if followRole === followRoles.leader} +
+

Do you want to stop leading the way?

+
+ {/if} +
+ + +
+
+{/if} + +{#if followState === followStates.active || followState === followStates.ending} +
+
+ {#if followRole === followRoles.follower} +

Following {followUsers[0]}

+ {:else if followUsers.length === 0} +

Waiting for followers' confirmation

+ {:else if followUsers.length === 1} +

{followUsers[0]} is following you

+ {:else if followUsers.length === 2} +

{followUsers[0]} and {followUsers[1]} are following you

+ {:else} +

{followUsers[0]}, {followUsers[1]} and {followUsers[2]} are following you

+ {/if} +
+
+{/if} + + diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index de7cfcc2..e3f36cb7 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -30,7 +30,9 @@ import { PingMessage, EmoteEventMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, SendUserMessage, BanUserMessage, VariableMessage, @@ -58,7 +60,15 @@ import { adminMessagesService } from "./AdminMessagesService"; import { worldFullMessageStream } from "./WorldFullMessageStream"; import { connectionManager } from "./ConnectionManager"; import { emoteEventStream } from "./EmoteEventStream"; +import { get } from "svelte/store"; import { warningContainerStore } from "../Stores/MenuStore"; +import { + followStateStore, + followRoleStore, + followUsersStore, + followRoles, + followStates, +} from "../Stores/InteractStore"; const manualPingDelay = 20000; @@ -258,9 +268,32 @@ export class RoomConnection implements RoomConnection { warningContainerStore.activateWarningContainer(); } else if (message.hasRefreshroommessage()) { //todo: implement a way to notify the user the room was refreshed. - } else if (message.hasFollowmerequestmessage()) { - const requestMessage = message.getFollowmerequestmessage() as FollowMeRequestMessage; - console.log("Follow me request from " + requestMessage.getPlayername()); + } else if (message.hasFollowrequestmessage()) { + const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; + console.log("Got follow request from " + requestMessage.getPlayername()); + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.follower); + followUsersStore.set([requestMessage.getPlayername()]); + } else if (message.hasFollowconfirmationmessage()) { + const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; + console.log("Got follow response from " + responseMessage.getFollower()); + followUsersStore.set([...get(followUsersStore), responseMessage.getFollower()]); + } else if (message.hasFollowabortmessage()) { + const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; + console.log("Got follow abort message from", abortMessage.getRole()); + if (abortMessage.getRole() === followRoles.leader) { + followStateStore.set(followStates.off); + followRoleStore.set(followRoles.leader); + followUsersStore.set([]); + } else { + let followers = get(followUsersStore); + followers = followers.filter((name) => name !== abortMessage.getPlayername()); + followUsersStore.set(followers); + if (followers.length === 0) { + followStateStore.set(followStates.off); + followRoleStore.set(followRoles.leader); + } + } } else if (message.hasErrormessage()) { const errorMessage = message.getErrormessage() as ErrorMessage; console.error("An error occurred server side: " + errorMessage.getMessage()); @@ -716,11 +749,41 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowMeRequest(): void { - console.log("Emitting follow me request"); - const message = new FollowMeRequestMessage(); + public emitFollowRequest(user: string | null): void { + if (!user) { + return; + } + console.log("Emitting follow request"); + const message = new FollowRequestMessage(); + message.setPlayername(user); const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowmerequestmessage(message); + clientToServerMessage.setFollowrequestmessage(message); + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public emitFollowConfirmation(leader: string, follower: string | null): void { + if (!follower) { + return; + } + console.log("Emitting follow confirmation"); + const message = new FollowConfirmationMessage(); + message.setLeader(leader); + message.setFollower(follower); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setFollowconfirmationmessage(message); + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public emitFollowAbort(role: string, user: string | null): void { + if (!user) { + return; + } + console.log("Emitting follow abort"); + const message = new FollowAbortMessage(); + message.setRole(role); + message.setPlayername(user); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setFollowabortmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index adf7a302..d8de9ab6 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -3,7 +3,16 @@ import type { GameScene } from "../Game/GameScene"; import { ActiveEventList, UserInputEvent, UserInputManager } from "../UserInput/UserInputManager"; import { Character } from "../Entity/Character"; import type { RemotePlayer } from "../Entity/RemotePlayer"; + +import { get } from "svelte/store"; import { userMovingStore } from "../../Stores/GameStore"; +import { + followStateStore, + followRoleStore, + followUsersStore, + followRoles, + followStates, +} from "../../Stores/InteractStore"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; @@ -156,44 +165,34 @@ export class Player extends Character { userMovingStore.set(moving); } - moveUser(delta: number): void { + public enableFollowing() { + Array.from(this.scene.MapPlayersByKey.values()).forEach((player) => { + if (player.PlayerValue !== get(followUsersStore)[0]) { + return; + } + this.follow = { + followPlayer: player, + direction: this.previousDirection, + }; + followStateStore.set(followStates.active); + }); + } + + public moveUser(delta: number): void { const activeEvents = this.userInputManager.getEventListForGameTick(); + const state = get(followStateStore); + const role = get(followRoleStore); if (activeEvents.get(UserInputEvent.Interact)) { - const sortedPlayers = Array.from(this.scene.MapPlayersByKey.values()).sort((p1, p2) => { - const sdistToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); - const sdistToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); - if (sdistToP1 > sdistToP2) { - return 1; - } else if (sdistToP1 < sdistToP2) { - return -1; - } else { - return 0; - } - }); - - const minFollowDist = 10000; - if (typeof sortedPlayers !== "undefined" && sortedPlayers.length > 0) { - const sdist = Math.pow(sortedPlayers[0].x - this.x, 2) + Math.pow(sortedPlayers[0].y - this.y, 2); - if (sdist < minFollowDist) { - this.follow = { - followPlayer: sortedPlayers[0], - direction: this.previousDirection, - }; - } + if (state === followStates.off && this.scene.groups.size > 0) { + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.leader); + } else if (state === followStates.active) { + followStateStore.set(followStates.ending); } } - if ( - activeEvents.get(UserInputEvent.MoveUp) || - activeEvents.get(UserInputEvent.MoveDown) || - activeEvents.get(UserInputEvent.MoveLeft) || - activeEvents.get(UserInputEvent.MoveRight) - ) { - this.follow = null; - } - - if (this.follow === null) { + if ((state !== followStates.active && state !== followStates.ending) || role !== followRoles.follower) { this.inputStep(activeEvents, delta); } else { this.followStep(activeEvents, delta); diff --git a/front/src/Stores/InteractStore.ts b/front/src/Stores/InteractStore.ts new file mode 100644 index 00000000..960a6954 --- /dev/null +++ b/front/src/Stores/InteractStore.ts @@ -0,0 +1,17 @@ +import { writable } from "svelte/store"; + +export const followStates = { + off: "off", + requesting: "requesting", + active: "active", + ending: "ending", +}; + +export const followRoles = { + leader: "leader", + follower: "follower", +}; + +export const followStateStore = writable(followStates.off); +export const followRoleStore = writable(followRoles.leader); +export const followUsersStore = writable([]); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index e9e75205..8e5f7c6b 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -80,13 +80,18 @@ message QueryJitsiJwtMessage { string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map! } -message FollowMeRequestMessage { +message FollowRequestMessage { string playerName = 1; } -message FollowMeResponseMessage { - string playerName = 1; - bool accepted = 2; +message FollowConfirmationMessage { + string leader = 1; + string follower = 2; +} + +message FollowAbortMessage { + string role = 1; + string playerName = 2; } message ClientToServerMessage { @@ -104,8 +109,9 @@ message ClientToServerMessage { QueryJitsiJwtMessage queryJitsiJwtMessage = 12; EmotePromptMessage emotePromptMessage = 13; VariableMessage variableMessage = 14; - FollowMeRequestMessage followMeRequestMessage = 15; - FollowMeResponseMessage followMeResponseMessage = 16; + FollowRequestMessage followRequestMessage = 15; + FollowConfirmationMessage followConfirmationMessage = 16; + FollowAbortMessage followAbortMessage = 17; } } @@ -296,8 +302,9 @@ message ServerToClientMessage { WorldConnexionMessage worldConnexionMessage = 18; //EmoteEventMessage emoteEventMessage = 19; TokenExpiredMessage tokenExpiredMessage = 20; - FollowMeRequestMessage followMeRequestMessage = 21; - FollowMeResponseMessage followMeResponseMessage = 22; + FollowRequestMessage followRequestMessage = 21; + FollowConfirmationMessage followConfirmationMessage = 22; + FollowAbortMessage followAbortMessage = 23; } } @@ -378,8 +385,9 @@ message PusherToBackMessage { BanUserMessage banUserMessage = 13; EmotePromptMessage emotePromptMessage = 14; VariableMessage variableMessage = 15; - FollowMeRequestMessage followMeRequestMessage = 16; - FollowMeResponseMessage followMeResponseMessage = 17; + FollowRequestMessage followRequestMessage = 16; + FollowConfirmationMessage followConfirmationMessage = 17; + FollowAbortMessage followAbortMessage = 18; } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 5d66c4df..930eb4cf 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -17,7 +17,9 @@ import { ServerToClientMessage, CompanionMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, VariableMessage, } from "../Messages/generated/messages_pb"; import { UserMovesMessage } from "../Messages/generated/messages_pb"; @@ -470,11 +472,18 @@ export class IoSocketController { client, message.getEmotepromptmessage() as EmotePromptMessage ); - } else if (message.hasFollowmerequestmessage()) { - socketManager.handleFollowMeRequest( + } else if (message.hasFollowrequestmessage()) { + socketManager.handleFollowRequest( client, - message.getFollowmerequestmessage() as FollowMeRequestMessage + message.getFollowrequestmessage() as FollowRequestMessage ); + } else if (message.hasFollowconfirmationmessage()) { + socketManager.handleFollowConfirmation( + client, + message.getFollowconfirmationmessage() as FollowConfirmationMessage + ); + } else if (message.hasFollowabortmessage()) { + socketManager.handleFollowAbort(client, message.getFollowabortmessage() as FollowAbortMessage); } /* Ok is false if backpressure was built up, wait for drain */ diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 2df167bd..2bbf83c1 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -8,7 +8,9 @@ import { CharacterLayerMessage, EmoteEventMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, GroupDeleteMessage, ItemEventMessage, JoinRoomMessage, @@ -270,9 +272,21 @@ export class SocketManager implements ZoneEventListener { this.handleViewport(client, viewport.toObject()); } - handleFollowMeRequest(client: ExSocketInterface, requestMessage: FollowMeRequestMessage): void { + handleFollowRequest(client: ExSocketInterface, message: FollowRequestMessage): void { const pusherToBackMessage = new PusherToBackMessage(); - pusherToBackMessage.setFollowmerequestmessage(requestMessage); + pusherToBackMessage.setFollowrequestmessage(message); + client.backConnection.write(pusherToBackMessage); + } + + handleFollowConfirmation(client: ExSocketInterface, message: FollowConfirmationMessage): void { + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setFollowconfirmationmessage(message); + client.backConnection.write(pusherToBackMessage); + } + + handleFollowAbort(client: ExSocketInterface, message: FollowAbortMessage): void { + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setFollowabortmessage(message); client.backConnection.write(pusherToBackMessage); } From c2f550123666ddc67367ab3fd9698adfec4b751b Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Mon, 13 Dec 2021 05:06:52 +0100 Subject: [PATCH 10/37] Do not leave group when following is active --- back/src/Model/GameRoom.ts | 3 ++- back/src/Model/User.ts | 16 ++++++++++++++++ back/src/Services/SocketManager.ts | 16 ++++++++++++++++ back/tests/PositionNotifierTest.ts | 8 ++++---- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 1b0db5eb..e8803762 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -139,6 +139,7 @@ export class GameRoom { joinRoomMessage.getIpaddress(), position, false, + [], this.positionNotifier, socket, joinRoomMessage.getTagList(), @@ -231,7 +232,7 @@ export class GameRoom { // If the user is part of a group: // should he leave the group? const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition()); - if (distance > this.groupRadius) { + if (user.following.length === 0 && distance > this.groupRadius) { this.leaveGroup(user); } } diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 186fb32a..09c0d3d9 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -25,6 +25,7 @@ export class User implements Movable { public readonly IPAddress: string, private position: PointInterface, public silent: boolean, + public following: string[], private positionNotifier: PositionNotifier, public readonly socket: UserSocket, public readonly tags: string[], @@ -48,6 +49,21 @@ export class User implements Movable { this.positionNotifier.updatePosition(this, position, oldPosition); } + public addFollower(name: string): void { + if (this.following.includes(name)) { + return; + } + this.following.push(name); + } + + public delFollower(name: string): void { + const idx = this.following.indexOf(name); + if (idx === -1) { + return; + } + this.following.splice(idx, 1); + } + private batchedMessages: BatchMessage = new BatchMessage(); private batchTimeout: NodeJS.Timeout | null = null; diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 3ab53719..94ad2ed3 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -847,19 +847,35 @@ export class SocketManager { const clientMessage = new ServerToClientMessage(); clientMessage.setFollowconfirmationmessage(message); room.sendToUserWithName(message.getLeader(), clientMessage); + + room.getUserByName(message.getLeader())?.addFollower(user.name); + user.addFollower(message.getLeader()); } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { if (message.getRole() === "leader") { + // Forward message const clientMessage = new ServerToClientMessage(); clientMessage.setFollowabortmessage(message); room.sendToOthersInGroupIncludingUser(user, clientMessage); + + // Update followers + room.getGroupIncludingUser(user) + ?.getUsers() + .forEach((user) => { + user.following = []; + }); } else { + // Forward message const recipient = message.getPlayername(); message.setPlayername(user.name); const clientMessage = new ServerToClientMessage(); clientMessage.setFollowabortmessage(message); room.sendToUserWithName(recipient, clientMessage); + + // Update followers + room.getUserByName(recipient)?.delFollower(user.name); + user.following = []; } } } diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 1aaf2e13..955ed40f 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -26,14 +26,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: -9999, y: -9999, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); positionNotifier.addZoneListener({} as ZoneSocket, 0, 0); positionNotifier.addZoneListener({} as ZoneSocket, 0, 1); @@ -101,14 +101,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: 0, y: 0, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); const listener = {} as ZoneSocket; positionNotifier.addZoneListener(listener, 0, 0); From 7bff782f7fae5f2fb308da7499bc6613a63416d1 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 14 Dec 2021 01:35:00 +0100 Subject: [PATCH 11/37] Improve follow abort message; avoid having to change it in backend --- back/src/Services/SocketManager.ts | 23 +++++++--------- .../InteractMenu/InteractMenu.svelte | 4 +-- front/src/Connexion/RoomConnection.ts | 27 ++++++++++--------- messages/protos/messages.proto | 6 ++--- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 94ad2ed3..727cf430 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -853,28 +853,23 @@ export class SocketManager { } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { - if (message.getRole() === "leader") { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + if (user.name === message.getLeader()) { // Forward message - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowabortmessage(message); room.sendToOthersInGroupIncludingUser(user, clientMessage); // Update followers - room.getGroupIncludingUser(user) - ?.getUsers() - .forEach((user) => { - user.following = []; - }); + const group = room.getGroupIncludingUser(user); + group?.getUsers().forEach((user) => { + user.following = []; + }); } else { // Forward message - const recipient = message.getPlayername(); - message.setPlayername(user.name); - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowabortmessage(message); - room.sendToUserWithName(recipient, clientMessage); + room.sendToUserWithName(message.getLeader(), clientMessage); // Update followers - room.getUserByName(recipient)?.delFollower(user.name); + room.getUserByName(message.getLeader())?.delFollower(user.name); user.following = []; } } diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/InteractMenu/InteractMenu.svelte index cb78f5bc..6f5e62b2 100644 --- a/front/src/Components/InteractMenu/InteractMenu.svelte +++ b/front/src/Components/InteractMenu/InteractMenu.svelte @@ -67,9 +67,9 @@ vim: ft=typescript function reset() { if (followRole === followRoles.leader && followUsers.length > 0) { - gameScene.connection?.emitFollowAbort(followRole, gameManager.getPlayerName()); + gameScene.connection?.emitFollowAbort(gameManager.getPlayerName(), "*"); } else { - gameScene.connection?.emitFollowAbort(followRole, followUsers[0]); + gameScene.connection?.emitFollowAbort(followUsers[0], gameManager.getPlayerName()); } followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index e3f36cb7..6d0abaef 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -270,26 +270,27 @@ export class RoomConnection implements RoomConnection { //todo: implement a way to notify the user the room was refreshed. } else if (message.hasFollowrequestmessage()) { const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; - console.log("Got follow request from " + requestMessage.getPlayername()); + console.log("Got follow request from " + requestMessage.getLeader()); followStateStore.set(followStates.requesting); followRoleStore.set(followRoles.follower); - followUsersStore.set([requestMessage.getPlayername()]); + followUsersStore.set([requestMessage.getLeader()]); } else if (message.hasFollowconfirmationmessage()) { const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; console.log("Got follow response from " + responseMessage.getFollower()); followUsersStore.set([...get(followUsersStore), responseMessage.getFollower()]); } else if (message.hasFollowabortmessage()) { const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; - console.log("Got follow abort message from", abortMessage.getRole()); - if (abortMessage.getRole() === followRoles.leader) { + console.log("Got follow abort message"); + if (get(followRoleStore) === followRoles.follower) { followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); followUsersStore.set([]); } else { let followers = get(followUsersStore); - followers = followers.filter((name) => name !== abortMessage.getPlayername()); + const oldFollowerCount = followers.length; + followers = followers.filter((name) => name !== abortMessage.getFollower()); followUsersStore.set(followers); - if (followers.length === 0) { + if (followers.length === 0 && oldFollowerCount > 0) { followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); } @@ -755,14 +756,14 @@ export class RoomConnection implements RoomConnection { } console.log("Emitting follow request"); const message = new FollowRequestMessage(); - message.setPlayername(user); + message.setLeader(user); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowrequestmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowConfirmation(leader: string, follower: string | null): void { - if (!follower) { + public emitFollowConfirmation(leader: string | null, follower: string | null): void { + if (!leader || !follower) { return; } console.log("Emitting follow confirmation"); @@ -774,14 +775,14 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowAbort(role: string, user: string | null): void { - if (!user) { + public emitFollowAbort(leader: string | null, follower: string | null): void { + if (!leader || !follower) { return; } console.log("Emitting follow abort"); const message = new FollowAbortMessage(); - message.setRole(role); - message.setPlayername(user); + message.setLeader(leader); + message.setFollower(follower); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowabortmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 8e5f7c6b..7152f43f 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -81,7 +81,7 @@ message QueryJitsiJwtMessage { } message FollowRequestMessage { - string playerName = 1; + string leader = 1; } message FollowConfirmationMessage { @@ -90,8 +90,8 @@ message FollowConfirmationMessage { } message FollowAbortMessage { - string role = 1; - string playerName = 2; + string leader = 1; + string follower = 2; } message ClientToServerMessage { From 290e5131e95c086013c033f91056bab769096003 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 14 Dec 2021 18:47:51 +0100 Subject: [PATCH 12/37] Clean up follow implementation; stop following when leader leaves the scene --- front/src/Phaser/Entity/Character.ts | 32 +++++------ front/src/Phaser/Game/GameScene.ts | 4 ++ front/src/Phaser/Player/Player.ts | 83 ++++++++-------------------- 3 files changed, 40 insertions(+), 79 deletions(-) diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 2e0bd363..1666063f 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -32,7 +32,7 @@ export abstract class Character extends Container { private readonly playerName: Text; public PlayerValue: string; public sprites: Map; - private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; + protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; //private teleportation: Sprite; private invisible: boolean; public companion?: Companion; @@ -266,24 +266,20 @@ export abstract class Character extends Container { body.setVelocity(x, y); - // up or down animations are prioritized over left and right - if (body.velocity.y < 0) { - //moving up - this.lastDirection = PlayerAnimationDirections.Up; - this.playAnimation(PlayerAnimationDirections.Up, true); - } else if (body.velocity.y > 0) { - //moving down - this.lastDirection = PlayerAnimationDirections.Down; - this.playAnimation(PlayerAnimationDirections.Down, true); - } else if (body.velocity.x > 0) { - //moving right - this.lastDirection = PlayerAnimationDirections.Right; - this.playAnimation(PlayerAnimationDirections.Right, true); - } else if (body.velocity.x < 0) { - //moving left - this.lastDirection = PlayerAnimationDirections.Left; - this.playAnimation(PlayerAnimationDirections.Left, true); + if (Math.abs(body.velocity.x) > Math.abs(body.velocity.y)) { + if (body.velocity.x < 0) { + this.lastDirection = PlayerAnimationDirections.Left; + } else if (body.velocity.x > 0) { + this.lastDirection = PlayerAnimationDirections.Right; + } + } else { + if (body.velocity.y < 0) { + this.lastDirection = PlayerAnimationDirections.Up; + } else if (body.velocity.y > 0) { + this.lastDirection = PlayerAnimationDirections.Down; + } } + this.playAnimation(this.lastDirection, true); this.setDepth(this.y); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index ae89e2c3..6d735182 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1715,6 +1715,10 @@ ${escapedMessage} }); } + public findPlayer(testFunction: (player: RemotePlayer) => boolean): RemotePlayer | undefined { + return Array.from(this.MapPlayersByKey.values()).find(testFunction); + } + /** * Called by the connexion when a new player arrives on a map */ diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index d8de9ab6..61951514 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -18,11 +18,6 @@ export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; export class Player extends Character { - private previousDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; - private wasMoving: boolean = false; - private timeCounter: number = 0; - private follow: { followPlayer: RemotePlayer; direction: PlayerAnimationDirections } | null = null; - constructor( Scene: GameScene, x: number, @@ -41,9 +36,9 @@ export class Player extends Character { this.getBody().setImmovable(false); } - private inputStep(activeEvents: ActiveEventList, delta: number) { + private inputStep(activeEvents: ActiveEventList) { //if user client on shift, camera and player speed - let direction = null; + let direction = this.lastDirection; let moving = false; const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; @@ -77,22 +72,22 @@ export class Player extends Character { if (x !== 0 || y !== 0) { this.move(x, y); this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y, oldX: x, oldY: y }); - } else if (this.wasMoving && moving) { + } else if (get(userMovingStore) && moving) { // slow joystick movement this.move(0, 0); this.emit(hasMovedEventName, { moving, - direction: this.previousDirection, + direction: direction, x: this.x, y: this.y, oldX: x, oldY: y, }); - } else if (this.wasMoving && !moving) { + } else if (get(userMovingStore) && !moving) { this.stop(); this.emit(hasMovedEventName, { moving, - direction: this.previousDirection, + direction: direction, x: this.x, y: this.y, oldX: x, @@ -100,35 +95,27 @@ export class Player extends Character { }); } - if (direction !== null) { - this.previousDirection = direction; - } - - this.wasMoving = moving; userMovingStore.set(moving); } - private followStep(activeEvents: ActiveEventList, delta: number) { - let moving = false; - - if (this.follow === null) { + private followStep(delta: number) { + const player = this.scene.findPlayer((p) => p.PlayerValue === get(followUsersStore)[0]); + if (!player) { + this.scene.connection?.emitFollowAbort(get(followUsersStore)[0], this.PlayerValue); + followStateStore.set(followStates.off); return; } - this.timeCounter += delta; - if (this.timeCounter < 128) { - return; - } - this.timeCounter = 0; - - const xDist = this.follow.followPlayer.x - this.x; - const yDist = this.follow.followPlayer.y - this.y; - + const xDist = player.x - this.x; + const yDist = player.y - this.y; const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); + let moving = false; + let direction = this.lastDirection; if (distance < 2000) { this.stop(); } else { + moving = true; const moveAmount = 9 * 20; const xDir = xDist / Math.sqrt(distance); const yDir = yDist / Math.sqrt(distance); @@ -136,46 +123,24 @@ export class Player extends Character { this.move(xDir * moveAmount, yDir * moveAmount); if (Math.abs(xDist) > Math.abs(yDist)) { - if (xDist < 0) { - this.follow.direction = PlayerAnimationDirections.Left; - } else { - this.follow.direction = PlayerAnimationDirections.Right; - } + direction = xDist < 0 ? PlayerAnimationDirections.Left : PlayerAnimationDirections.Right; } else { - if (yDist < 0) { - this.follow.direction = PlayerAnimationDirections.Up; - } else { - this.follow.direction = PlayerAnimationDirections.Down; - } + direction = yDist < 0 ? PlayerAnimationDirections.Up : PlayerAnimationDirections.Down; } - - moving = true; } this.emit(hasMovedEventName, { moving: moving, - direction: this.follow.direction, + direction: direction, x: this.x, y: this.y, }); - this.previousDirection = this.follow.direction; - - this.wasMoving = moving; userMovingStore.set(moving); } public enableFollowing() { - Array.from(this.scene.MapPlayersByKey.values()).forEach((player) => { - if (player.PlayerValue !== get(followUsersStore)[0]) { - return; - } - this.follow = { - followPlayer: player, - direction: this.previousDirection, - }; - followStateStore.set(followStates.active); - }); + followStateStore.set(followStates.active); } public moveUser(delta: number): void { @@ -193,13 +158,9 @@ export class Player extends Character { } if ((state !== followStates.active && state !== followStates.ending) || role !== followRoles.follower) { - this.inputStep(activeEvents, delta); + this.inputStep(activeEvents); } else { - this.followStep(activeEvents, delta); + this.followStep(delta); } } - - public isMoving(): boolean { - return this.wasMoving; - } } From 1ab8165951d71e76cec0fced877a537fab48a9e3 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 00:10:28 +0100 Subject: [PATCH 13/37] Process input events in follow mode as well --- front/src/Phaser/Player/Player.ts | 129 +++++++++++------------------- 1 file changed, 48 insertions(+), 81 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 61951514..285163e8 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -36,107 +36,73 @@ export class Player extends Character { this.getBody().setImmovable(false); } - private inputStep(activeEvents: ActiveEventList) { - //if user client on shift, camera and player speed - let direction = this.lastDirection; - let moving = false; - - const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; - const moveAmount = speedMultiplier * 20; - - let x = 0; - let y = 0; - + private inputStep(activeEvents: ActiveEventList, x: number, y: number) { + // Process input events if (activeEvents.get(UserInputEvent.MoveUp)) { - y = -moveAmount; - direction = PlayerAnimationDirections.Up; - moving = true; + y = y - 1; } else if (activeEvents.get(UserInputEvent.MoveDown)) { - y = moveAmount; - direction = PlayerAnimationDirections.Down; - moving = true; + y = y + 1; } if (activeEvents.get(UserInputEvent.MoveLeft)) { - x = -moveAmount; - direction = PlayerAnimationDirections.Left; - moving = true; + x = x - 1; } else if (activeEvents.get(UserInputEvent.MoveRight)) { - x = moveAmount; - direction = PlayerAnimationDirections.Right; - moving = true; + x = x + 1; } - moving = moving || activeEvents.get(UserInputEvent.JoystickMove); + // Compute movement deltas + const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; + const moveAmount = speedMultiplier * 20; + x = x * moveAmount; + y = y * moveAmount; - if (x !== 0 || y !== 0) { + // Compute moving state + const joystickMovement = activeEvents.get(UserInputEvent.JoystickMove); + const moving = x !== 0 || y !== 0 || joystickMovement; + + // Compute direction + let direction = this.lastDirection; + if (moving && !joystickMovement) { + if (Math.abs(x) > Math.abs(y)) { + direction = x < 0 ? PlayerAnimationDirections.Left : PlayerAnimationDirections.Right; + } else { + direction = y < 0 ? PlayerAnimationDirections.Up : PlayerAnimationDirections.Down; + } + } + + // Send movement events + const emit = () => this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y }); + if (moving) { this.move(x, y); - this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y, oldX: x, oldY: y }); - } else if (get(userMovingStore) && moving) { - // slow joystick movement - this.move(0, 0); - this.emit(hasMovedEventName, { - moving, - direction: direction, - x: this.x, - y: this.y, - oldX: x, - oldY: y, - }); - } else if (get(userMovingStore) && !moving) { + emit(); + } else if (get(userMovingStore)) { this.stop(); - this.emit(hasMovedEventName, { - moving, - direction: direction, - x: this.x, - y: this.y, - oldX: x, - oldY: y, - }); + emit(); } + // Update state userMovingStore.set(moving); } - private followStep(delta: number) { + private computeFollowMovement(): number[] { + // Find followed WOKA and abort following if we lost it const player = this.scene.findPlayer((p) => p.PlayerValue === get(followUsersStore)[0]); if (!player) { this.scene.connection?.emitFollowAbort(get(followUsersStore)[0], this.PlayerValue); followStateStore.set(followStates.off); - return; + return [0, 0]; } - const xDist = player.x - this.x; - const yDist = player.y - this.y; - const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); - - let moving = false; - let direction = this.lastDirection; + // Compute movement direction + const xDistance = player.x - this.x; + const yDistance = player.y - this.y; + const distance = Math.pow(xDistance, 2) + Math.pow(yDistance, 2); if (distance < 2000) { - this.stop(); - } else { - moving = true; - const moveAmount = 9 * 20; - const xDir = xDist / Math.sqrt(distance); - const yDir = yDist / Math.sqrt(distance); - - this.move(xDir * moveAmount, yDir * moveAmount); - - if (Math.abs(xDist) > Math.abs(yDist)) { - direction = xDist < 0 ? PlayerAnimationDirections.Left : PlayerAnimationDirections.Right; - } else { - direction = yDist < 0 ? PlayerAnimationDirections.Up : PlayerAnimationDirections.Down; - } + return [0, 0]; } - - this.emit(hasMovedEventName, { - moving: moving, - direction: direction, - x: this.x, - y: this.y, - }); - - userMovingStore.set(moving); + const xMovement = xDistance / Math.sqrt(distance); + const yMovement = yDistance / Math.sqrt(distance); + return [xMovement, yMovement]; } public enableFollowing() { @@ -157,10 +123,11 @@ export class Player extends Character { } } - if ((state !== followStates.active && state !== followStates.ending) || role !== followRoles.follower) { - this.inputStep(activeEvents); - } else { - this.followStep(delta); + let x = 0; + let y = 0; + if ((state === followStates.active || state === followStates.ending) && role === followRoles.follower) { + [x, y] = this.computeFollowMovement(); } + this.inputStep(activeEvents, x, y); } } From e5286824037763701da23d9dc8fedcd97665101d Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 13:21:30 +0100 Subject: [PATCH 14/37] Use User.group instead of iterating over groups Thanks @moufmouf for pointing this out. --- back/src/Model/GameRoom.ts | 21 +++++---------------- back/src/Services/SocketManager.ts | 3 +-- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index e8803762..79ed9d3c 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -92,15 +92,6 @@ export class GameRoom { return gameRoom; } - public getGroups(): Group[] { - return Array.from(this.groups.values()); - } - - public getGroupIncludingUser(user: User): Group | undefined { - const foundGroups = this.getGroups().filter((grp) => grp.includes(user)); - return foundGroups[0]; - } - public getUsers(): Map { return this.users; } @@ -239,13 +230,11 @@ export class GameRoom { } public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void { - this.getGroupIncludingUser(user) - ?.getUsers() - .forEach((currentUser: User) => { - if (currentUser.name !== user.name) { - currentUser.socket.write(message); - } - }); + user.group?.getUsers().forEach((currentUser: User) => { + if (currentUser.name !== user.name) { + currentUser.socket.write(message); + } + }); } public sendToUserWithName(name: string, message: ServerToClientMessage): void { diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 727cf430..5818afa9 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -860,8 +860,7 @@ export class SocketManager { room.sendToOthersInGroupIncludingUser(user, clientMessage); // Update followers - const group = room.getGroupIncludingUser(user); - group?.getUsers().forEach((user) => { + user.group?.getUsers().forEach((user) => { user.following = []; }); } else { From 2bd71790b58c0ac26edb187dc402ac1342ab4b58 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 14:48:45 +0100 Subject: [PATCH 15/37] Use user IDs instead of names --- back/src/Model/GameRoom.ts | 9 ------ back/src/Model/User.ts | 12 ++++---- back/src/Services/SocketManager.ts | 12 ++++---- .../InteractMenu/InteractMenu.svelte | 28 +++++++++---------- front/src/Connexion/RoomConnection.ts | 24 ++++++++-------- front/src/Phaser/Game/GameScene.ts | 4 --- front/src/Phaser/Player/Player.ts | 4 +-- front/src/Stores/InteractStore.ts | 2 +- messages/protos/messages.proto | 10 +++---- 9 files changed, 48 insertions(+), 57 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 79ed9d3c..9afeae55 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -96,11 +96,6 @@ export class GameRoom { return this.users; } - public getUserByName(name: string): User | undefined { - let foundUsers = Array.from(this.users.values()); - foundUsers = foundUsers.filter((user: User) => user.name === name); - return foundUsers[0]; - } public getUserByUuid(uuid: string): User | undefined { return this.usersByUuid.get(uuid); } @@ -237,10 +232,6 @@ export class GameRoom { }); } - public sendToUserWithName(name: string, message: ServerToClientMessage): void { - this.getUserByName(name)?.socket.write(message); - } - setSilent(user: User, silent: boolean) { if (user.silent === silent) { return; diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 09c0d3d9..13a61c3f 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -25,7 +25,7 @@ export class User implements Movable { public readonly IPAddress: string, private position: PointInterface, public silent: boolean, - public following: string[], + public following: number[], private positionNotifier: PositionNotifier, public readonly socket: UserSocket, public readonly tags: string[], @@ -49,15 +49,15 @@ export class User implements Movable { this.positionNotifier.updatePosition(this, position, oldPosition); } - public addFollower(name: string): void { - if (this.following.includes(name)) { + public addFollower(userId: number): void { + if (this.following.includes(userId)) { return; } - this.following.push(name); + this.following.push(userId); } - public delFollower(name: string): void { - const idx = this.following.indexOf(name); + public delFollower(userId: number): void { + const idx = this.following.indexOf(userId); if (idx === -1) { return; } diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 5818afa9..8c7eecac 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -846,16 +846,17 @@ export class SocketManager { handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { const clientMessage = new ServerToClientMessage(); clientMessage.setFollowconfirmationmessage(message); - room.sendToUserWithName(message.getLeader(), clientMessage); + const leader = room.getUserById(message.getLeader()); + leader?.socket.write(clientMessage); - room.getUserByName(message.getLeader())?.addFollower(user.name); + leader?.addFollower(user.id); user.addFollower(message.getLeader()); } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { const clientMessage = new ServerToClientMessage(); clientMessage.setFollowabortmessage(message); - if (user.name === message.getLeader()) { + if (user.id === message.getLeader()) { // Forward message room.sendToOthersInGroupIncludingUser(user, clientMessage); @@ -865,10 +866,11 @@ export class SocketManager { }); } else { // Forward message - room.sendToUserWithName(message.getLeader(), clientMessage); + const leader = room.getUserById(message.getLeader()); + leader?.socket.write(clientMessage); // Update followers - room.getUserByName(message.getLeader())?.delFollower(user.name); + leader?.delFollower(user.id); user.following = []; } } diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/InteractMenu/InteractMenu.svelte index 6f5e62b2..dae7f099 100644 --- a/front/src/Components/InteractMenu/InteractMenu.svelte +++ b/front/src/Components/InteractMenu/InteractMenu.svelte @@ -19,7 +19,7 @@ vim: ft=typescript let followState: string; let followRole: string; - let followUsers: string[]; + let followUsers: number[]; let stateUnsubscriber: Unsubscriber; let roleUnsubscriber: Unsubscriber; let nameUnsubscriber: Unsubscriber; @@ -51,14 +51,18 @@ vim: ft=typescript } }); + function name(userId: number): string | undefined { + return gameScene.MapPlayersByKey.get(userId)?.PlayerValue; + } + function sendFollowRequest() { - gameScene.connection?.emitFollowRequest(gameManager.getPlayerName()); + gameScene.connection?.emitFollowRequest(); followStateStore.set(followStates.active); } function acceptFollowRequest() { gameScene.CurrentPlayer.enableFollowing(); - gameScene.connection?.emitFollowConfirmation(followUsers[0], gameManager.getPlayerName()); + gameScene.connection?.emitFollowConfirmation(); } function abortEnding() { @@ -66,11 +70,7 @@ vim: ft=typescript } function reset() { - if (followRole === followRoles.leader && followUsers.length > 0) { - gameScene.connection?.emitFollowAbort(gameManager.getPlayerName(), "*"); - } else { - gameScene.connection?.emitFollowAbort(followUsers[0], gameManager.getPlayerName()); - } + gameScene.connection?.emitFollowAbort(); followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); followUsersStore.set([]); @@ -92,7 +92,7 @@ vim: ft=typescript {#if followRole === followRoles.follower}
-

Do you want to follow {followUsers[0]}?

+

Do you want to follow {name(followUsers[0])}?

@@ -117,7 +117,7 @@ vim: ft=typescript
{#if followRole === followRoles.follower}
-

Do you want to stop following {followUsers[0]}?

+

Do you want to stop following {name(followUsers[0])}?

{:else if followRole === followRoles.leader}
@@ -135,15 +135,15 @@ vim: ft=typescript
{#if followRole === followRoles.follower} -

Following {followUsers[0]}

+

Following {name(followUsers[0])}

{:else if followUsers.length === 0}

Waiting for followers' confirmation

{:else if followUsers.length === 1} -

{followUsers[0]} is following you

+

{name(followUsers[0])} is following you

{:else if followUsers.length === 2} -

{followUsers[0]} and {followUsers[1]} are following you

+

{name(followUsers[0])} and {name(followUsers[1])} are following you

{:else} -

{followUsers[0]}, {followUsers[1]} and {followUsers[2]} are following you

+

{name(followUsers[0])}, {name(followUsers[1])} and {name(followUsers[2])} are following you

{/if}
diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 6d0abaef..49b2dc4c 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -750,39 +750,41 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowRequest(user: string | null): void { - if (!user) { + public emitFollowRequest(): void { + if (!this.userId) { return; } console.log("Emitting follow request"); const message = new FollowRequestMessage(); - message.setLeader(user); + message.setLeader(this.userId); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowrequestmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowConfirmation(leader: string | null, follower: string | null): void { - if (!leader || !follower) { + public emitFollowConfirmation(): void { + if (!this.userId) { return; } console.log("Emitting follow confirmation"); const message = new FollowConfirmationMessage(); - message.setLeader(leader); - message.setFollower(follower); + message.setLeader(get(followUsersStore)[0]); + message.setFollower(this.userId); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowconfirmationmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowAbort(leader: string | null, follower: string | null): void { - if (!leader || !follower) { + public emitFollowAbort(): void { + const isLeader = get(followRoleStore) === followRoles.leader; + const hasFollowers = get(followUsersStore).length > 0; + if (!this.userId || (isLeader && !hasFollowers)) { return; } console.log("Emitting follow abort"); const message = new FollowAbortMessage(); - message.setLeader(leader); - message.setFollower(follower); + message.setLeader(isLeader ? this.userId : get(followUsersStore)[0]); + message.setFollower(isLeader ? 0 : this.userId); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowabortmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 6d735182..ae89e2c3 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1715,10 +1715,6 @@ ${escapedMessage} }); } - public findPlayer(testFunction: (player: RemotePlayer) => boolean): RemotePlayer | undefined { - return Array.from(this.MapPlayersByKey.values()).find(testFunction); - } - /** * Called by the connexion when a new player arrives on a map */ diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 285163e8..159816e3 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -86,9 +86,9 @@ export class Player extends Character { private computeFollowMovement(): number[] { // Find followed WOKA and abort following if we lost it - const player = this.scene.findPlayer((p) => p.PlayerValue === get(followUsersStore)[0]); + const player = this.scene.MapPlayersByKey.get(get(followUsersStore)[0]); if (!player) { - this.scene.connection?.emitFollowAbort(get(followUsersStore)[0], this.PlayerValue); + this.scene.connection?.emitFollowAbort(); followStateStore.set(followStates.off); return [0, 0]; } diff --git a/front/src/Stores/InteractStore.ts b/front/src/Stores/InteractStore.ts index 960a6954..6c85ab17 100644 --- a/front/src/Stores/InteractStore.ts +++ b/front/src/Stores/InteractStore.ts @@ -14,4 +14,4 @@ export const followRoles = { export const followStateStore = writable(followStates.off); export const followRoleStore = writable(followRoles.leader); -export const followUsersStore = writable([]); +export const followUsersStore = writable([]); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 7152f43f..96fecb69 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -81,17 +81,17 @@ message QueryJitsiJwtMessage { } message FollowRequestMessage { - string leader = 1; + int32 leader = 1; } message FollowConfirmationMessage { - string leader = 1; - string follower = 2; + int32 leader = 1; + int32 follower = 2; } message FollowAbortMessage { - string leader = 1; - string follower = 2; + int32 leader = 1; + int32 follower = 2; } message ClientToServerMessage { From e3e7fba53924d38a3448dae9c5932525481711f8 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 14:57:44 +0100 Subject: [PATCH 16/37] Rename InteractMenu/-Store => FollowMenu/-Store --- front/src/Components/App.svelte | 6 +++--- .../InteractMenu.svelte => FollowMenu/FollowMenu.svelte} | 2 +- front/src/Connexion/RoomConnection.ts | 8 +------- front/src/Phaser/Player/Player.ts | 2 +- front/src/Stores/{InteractStore.ts => FollowStore.ts} | 0 5 files changed, 6 insertions(+), 12 deletions(-) rename front/src/Components/{InteractMenu/InteractMenu.svelte => FollowMenu/FollowMenu.svelte} (99%) rename front/src/Stores/{InteractStore.ts => FollowStore.ts} (100%) diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index b14801c9..5f09beef 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -42,8 +42,8 @@ import AudioManager from "./AudioManager/AudioManager.svelte"; import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore"; import ReportMenu from "./ReportMenu/ReportMenu.svelte"; - import { followStateStore, followStates } from "../Stores/InteractStore"; - import InteractMenu from "./InteractMenu/InteractMenu.svelte"; + import { followStateStore, followStates } from "../Stores/FollowStore"; + import FollowMenu from "./FollowMenu/FollowMenu.svelte"; export let game: Game; @@ -106,7 +106,7 @@ {/if} {#if $followStateStore !== followStates.off}
- +
{/if} {#if $menuIconVisiblilityStore} diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte similarity index 99% rename from front/src/Components/InteractMenu/InteractMenu.svelte rename to front/src/Components/FollowMenu/FollowMenu.svelte index dae7f099..590fd61c 100644 --- a/front/src/Components/InteractMenu/InteractMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -13,7 +13,7 @@ vim: ft=typescript followUsersStore, followRoles, followStates, - } from "../../Stores/InteractStore"; + } from "../../Stores/FollowStore"; const gameScene = gameManager.getCurrentGameScene(); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 49b2dc4c..f8d385d4 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -62,13 +62,7 @@ import { connectionManager } from "./ConnectionManager"; import { emoteEventStream } from "./EmoteEventStream"; import { get } from "svelte/store"; import { warningContainerStore } from "../Stores/MenuStore"; -import { - followStateStore, - followRoleStore, - followUsersStore, - followRoles, - followStates, -} from "../Stores/InteractStore"; +import { followStateStore, followRoleStore, followUsersStore, followRoles, followStates } from "../Stores/FollowStore"; const manualPingDelay = 20000; diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 159816e3..f34d1478 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -12,7 +12,7 @@ import { followUsersStore, followRoles, followStates, -} from "../../Stores/InteractStore"; +} from "../../Stores/FollowStore"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; diff --git a/front/src/Stores/InteractStore.ts b/front/src/Stores/FollowStore.ts similarity index 100% rename from front/src/Stores/InteractStore.ts rename to front/src/Stores/FollowStore.ts From d3297a448e061bbdb3843facc57692b3dedae5e1 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 19:47:14 +0100 Subject: [PATCH 17/37] Add setting that ignores follow invites --- front/src/Components/Menu/SettingsSubMenu.svelte | 14 ++++++++++++++ front/src/Connexion/LocalUserStore.ts | 8 ++++++++ front/src/Connexion/RoomConnection.ts | 12 ++++++------ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/front/src/Components/Menu/SettingsSubMenu.svelte b/front/src/Components/Menu/SettingsSubMenu.svelte index 93d3eaa9..1db14036 100644 --- a/front/src/Components/Menu/SettingsSubMenu.svelte +++ b/front/src/Components/Menu/SettingsSubMenu.svelte @@ -8,6 +8,7 @@ let fullscreen: boolean = localUserStore.getFullscreen(); let notification: boolean = localUserStore.getNotification() === "granted"; let forceCowebsiteTrigger: boolean = localUserStore.getForceCowebsiteTrigger(); + let ignoreFollowRequests: boolean = localUserStore.getIgnoreFollowRequests(); let valueGame: number = localUserStore.getGameQualityValue(); let valueVideo: number = localUserStore.getVideoQualityValue(); let previewValueGame = valueGame; @@ -59,6 +60,10 @@ localUserStore.setForceCowebsiteTrigger(forceCowebsiteTrigger); } + function changeIgnoreFollowRequests() { + localUserStore.setIgnoreFollowRequests(ignoreFollowRequests); + } + function closeMenu() { menuVisiblilityStore.set(false); } @@ -123,6 +128,15 @@ /> Always ask before opening websites and Jitsi Meet rooms +
diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 30755034..4dce6924 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -14,6 +14,7 @@ const audioPlayerMuteKey = "audioMute"; const helpCameraSettingsShown = "helpCameraSettingsShown"; const fullscreenKey = "fullscreen"; const forceCowebsiteTriggerKey = "forceCowebsiteTrigger"; +const ignoreFollowRequests = "ignoreFollowRequests"; const lastRoomUrl = "lastRoomUrl"; const authToken = "authToken"; const state = "state"; @@ -128,6 +129,13 @@ class LocalUserStore { return localStorage.getItem(forceCowebsiteTriggerKey) === "true"; } + setIgnoreFollowRequests(value: boolean): void { + localStorage.setItem(ignoreFollowRequests, value.toString()); + } + getIgnoreFollowRequests(): boolean { + return localStorage.getItem(ignoreFollowRequests) === "true"; + } + setLastRoomUrl(roomUrl: string): void { localStorage.setItem(lastRoomUrl, roomUrl.toString()); if ("caches" in window) { diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index f8d385d4..f4cbc251 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -63,6 +63,7 @@ import { emoteEventStream } from "./EmoteEventStream"; import { get } from "svelte/store"; import { warningContainerStore } from "../Stores/MenuStore"; import { followStateStore, followRoleStore, followUsersStore, followRoles, followStates } from "../Stores/FollowStore"; +import { localUserStore } from "./LocalUserStore"; const manualPingDelay = 20000; @@ -264,17 +265,16 @@ export class RoomConnection implements RoomConnection { //todo: implement a way to notify the user the room was refreshed. } else if (message.hasFollowrequestmessage()) { const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; - console.log("Got follow request from " + requestMessage.getLeader()); - followStateStore.set(followStates.requesting); - followRoleStore.set(followRoles.follower); - followUsersStore.set([requestMessage.getLeader()]); + if (!localUserStore.getIgnoreFollowRequests()) { + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.follower); + followUsersStore.set([requestMessage.getLeader()]); + } } else if (message.hasFollowconfirmationmessage()) { const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; - console.log("Got follow response from " + responseMessage.getFollower()); followUsersStore.set([...get(followUsersStore), responseMessage.getFollower()]); } else if (message.hasFollowabortmessage()) { const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; - console.log("Got follow abort message"); if (get(followRoleStore) === followRoles.follower) { followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); From a48137663321e1657d03c0c5e422b865674520d3 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 20:11:42 +0100 Subject: [PATCH 18/37] Clean up remaining debug log messages --- front/src/Connexion/RoomConnection.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index f4cbc251..0c8390b8 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -748,7 +748,6 @@ export class RoomConnection implements RoomConnection { if (!this.userId) { return; } - console.log("Emitting follow request"); const message = new FollowRequestMessage(); message.setLeader(this.userId); const clientToServerMessage = new ClientToServerMessage(); @@ -760,7 +759,6 @@ export class RoomConnection implements RoomConnection { if (!this.userId) { return; } - console.log("Emitting follow confirmation"); const message = new FollowConfirmationMessage(); message.setLeader(get(followUsersStore)[0]); message.setFollower(this.userId); @@ -775,7 +773,6 @@ export class RoomConnection implements RoomConnection { if (!this.userId || (isLeader && !hasFollowers)) { return; } - console.log("Emitting follow abort"); const message = new FollowAbortMessage(); message.setLeader(isLeader ? this.userId : get(followUsersStore)[0]); message.setFollower(isLeader ? 0 : this.userId); From ab994183e5eb0be3c503d41798e55f9307cb7b68 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Thu, 16 Dec 2021 21:26:30 +0100 Subject: [PATCH 19/37] Fix not following users getting "stuck" in groups --- back/src/Model/GameRoom.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 9afeae55..43605778 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -217,9 +217,22 @@ export class GameRoom { } else { // If the user is part of a group: // should he leave the group? - const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition()); - if (user.following.length === 0 && distance > this.groupRadius) { - this.leaveGroup(user); + const leaveIfOutOfRadius = (user: User) => { + if (user.group === undefined) { + return; + } + const usrPos = user.getPosition(); + const grpPos = user.group.getPosition(); + const distance = GameRoom.computeDistanceBetweenPositions(usrPos, grpPos); + if (distance > this.groupRadius) { + this.leaveGroup(user); + } + }; + if (user.following.length > 0) { + const users = user.group.getUsers().filter((u) => u.following.length === 0); + users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); + } else { + leaveIfOutOfRadius(user); } } } From 44ff9e30d5df375509c8b2afff64f75f3f5dd619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 11:54:37 +0100 Subject: [PATCH 20/37] Using id instead of name to identify other Wokas --- back/src/Model/GameRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 43605778..ba555c92 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -239,7 +239,7 @@ export class GameRoom { public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void { user.group?.getUsers().forEach((currentUser: User) => { - if (currentUser.name !== user.name) { + if (currentUser.id !== user.id) { currentUser.socket.write(message); } }); From fd9cb09de618a82250fea7f2ede5f4fe54821621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 11:55:27 +0100 Subject: [PATCH 21/37] Checking if a user should leave a group when someone moves in the group every time. This fixes a long standing issue. --- back/src/Model/GameRoom.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index ba555c92..0e8c5a10 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -228,12 +228,8 @@ export class GameRoom { this.leaveGroup(user); } }; - if (user.following.length > 0) { - const users = user.group.getUsers().filter((u) => u.following.length === 0); - users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); - } else { - leaveIfOutOfRadius(user); - } + const users = user.group.getUsers().filter((u) => u.following.length === 0); + users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); } } From cd805fab310c32ea28e73093b8b8f7903c318a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 18:26:17 +0100 Subject: [PATCH 22/37] Refactoring following code to make a distinction beween followed and following users. Also, moving sending messages to the User class. --- back/src/Model/GameRoom.ts | 3 +- back/src/Model/Group.ts | 12 ++++++++ back/src/Model/User.ts | 49 ++++++++++++++++++++++-------- back/src/Services/SocketManager.ts | 32 ++++++++----------- 4 files changed, 63 insertions(+), 33 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 0e8c5a10..07b611e8 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -125,7 +125,6 @@ export class GameRoom { joinRoomMessage.getIpaddress(), position, false, - [], this.positionNotifier, socket, joinRoomMessage.getTagList(), @@ -228,7 +227,7 @@ export class GameRoom { this.leaveGroup(user); } }; - const users = user.group.getUsers().filter((u) => u.following.length === 0); + const users = user.group.getUsers().filter((u) => !u.hasFollowers() && !u.following); users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); } } diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 570eaedf..db9f6305 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -152,4 +152,16 @@ export class Group implements Movable { get getSize() { return this.users.size; } + + /** + * A group can have at most one person leading the way in it. + */ + get leader(): User|undefined { + for (const user of this.users) { + if (user.hasFollowers()) { + return user; + } + } + return undefined; + } } diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 13a61c3f..4619ce2c 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -6,7 +6,7 @@ import { PositionNotifier } from "_Model/PositionNotifier"; import { ServerDuplexStream } from "grpc"; import { BatchMessage, - CompanionMessage, + CompanionMessage, FollowAbortMessage, FollowConfirmationMessage, PusherToBackMessage, ServerToClientMessage, SubMessage, @@ -18,6 +18,8 @@ export type UserSocket = ServerDuplexStream; public group?: Group; + private _following: User|undefined; + private followedBy: Set = new Set(); public constructor( public id: number, @@ -25,7 +27,6 @@ export class User implements Movable { public readonly IPAddress: string, private position: PointInterface, public silent: boolean, - public following: number[], private positionNotifier: PositionNotifier, public readonly socket: UserSocket, public readonly tags: string[], @@ -49,19 +50,43 @@ export class User implements Movable { this.positionNotifier.updatePosition(this, position, oldPosition); } - public addFollower(userId: number): void { - if (this.following.includes(userId)) { - return; - } - this.following.push(userId); + public addFollower(follower: User): void { + this.followedBy.add(follower); + follower._following = this; + + const message = new FollowConfirmationMessage(); + message.setFollower(follower.id); + message.setLeader(this.id); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowconfirmationmessage(message); + this.socket.write(clientMessage); } - public delFollower(userId: number): void { - const idx = this.following.indexOf(userId); - if (idx === -1) { - return; + public delFollower(follower: User): void { + this.followedBy.delete(follower); + follower._following = undefined; + + const message = new FollowAbortMessage(); + message.setFollower(follower.id); + message.setLeader(this.id); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + this.socket.write(clientMessage); + follower.socket.write(clientMessage); + } + + public hasFollowers(): boolean { + return this.followedBy.size !== 0; + } + + get following(): User | undefined { + return this._following; + } + + public stopLeading(): void { + for (const follower of this.followedBy) { + this.delFollower(follower); } - this.following.splice(idx, 1); } private batchedMessages: BatchMessage = new BatchMessage(); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 8c7eecac..d7178d3d 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -844,34 +844,28 @@ export class SocketManager { } handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowconfirmationmessage(message); const leader = room.getUserById(message.getLeader()); - leader?.socket.write(clientMessage); + if (!leader) { + console.info('Could not find user "', message.getLeader(), '" while handling a follow confirmation in room "', room.roomUrl,'". Maybe the user just left.'); + return; + } - leader?.addFollower(user.id); - user.addFollower(message.getLeader()); + // By security, we look at the group leader. If the group leader is NOT the leader in the message, everybody should + // stop following the group leader (to avoid having 2 group leaders) + if (user?.group?.leader && user?.group?.leader !== leader) { + user?.group?.leader?.stopLeading(); + } + + leader.addFollower(user); } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowabortmessage(message); if (user.id === message.getLeader()) { - // Forward message - room.sendToOthersInGroupIncludingUser(user, clientMessage); - - // Update followers - user.group?.getUsers().forEach((user) => { - user.following = []; - }); + user?.group?.leader?.stopLeading(); } else { // Forward message const leader = room.getUserById(message.getLeader()); - leader?.socket.write(clientMessage); - - // Update followers - leader?.delFollower(user.id); - user.following = []; + leader?.delFollower(user); } } } From c96b65549f92de55652d427aa81eda16a684f7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 18:29:52 +0100 Subject: [PATCH 23/37] Performing proper cleanup when a user leaves --- back/src/Model/GameRoom.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 07b611e8..9b0fef45 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -154,6 +154,14 @@ export class GameRoom { if (userObj !== undefined && typeof userObj.group !== "undefined") { this.leaveGroup(userObj); } + + if (user.hasFollowers()) { + user.stopLeading(); + } + if (user.following) { + user.following.delFollower(user); + } + this.users.delete(user.id); this.usersByUuid.delete(user.uuid); From 5c385c520a4f8848bb69947390877b166371ba89 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sat, 18 Dec 2021 11:30:58 +0100 Subject: [PATCH 24/37] Cleanup; pretty --- back/src/Model/GameRoom.ts | 15 +++++++-------- back/src/Model/Group.ts | 2 +- back/src/Model/User.ts | 6 ++++-- back/src/Services/SocketManager.ts | 7 ++++--- back/tests/PositionNotifierTest.ts | 8 ++++---- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 9b0fef45..e7f23549 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -224,19 +224,18 @@ export class GameRoom { } else { // If the user is part of a group: // should he leave the group? - const leaveIfOutOfRadius = (user: User) => { - if (user.group === undefined) { + const users = user.group.getUsers().filter((u) => !u.hasFollowers() && !u.following); + users.forEach((foreignUser: User) => { + if (foreignUser.group === undefined) { return; } - const usrPos = user.getPosition(); - const grpPos = user.group.getPosition(); + const usrPos = foreignUser.getPosition(); + const grpPos = foreignUser.group.getPosition(); const distance = GameRoom.computeDistanceBetweenPositions(usrPos, grpPos); if (distance > this.groupRadius) { - this.leaveGroup(user); + this.leaveGroup(foreignUser); } - }; - const users = user.group.getUsers().filter((u) => !u.hasFollowers() && !u.following); - users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); + }); } } diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index db9f6305..08d1d21e 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -156,7 +156,7 @@ export class Group implements Movable { /** * A group can have at most one person leading the way in it. */ - get leader(): User|undefined { + get leader(): User | undefined { for (const user of this.users) { if (user.hasFollowers()) { return user; diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 4619ce2c..50f140fc 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -6,7 +6,9 @@ import { PositionNotifier } from "_Model/PositionNotifier"; import { ServerDuplexStream } from "grpc"; import { BatchMessage, - CompanionMessage, FollowAbortMessage, FollowConfirmationMessage, + CompanionMessage, + FollowAbortMessage, + FollowConfirmationMessage, PusherToBackMessage, ServerToClientMessage, SubMessage, @@ -18,7 +20,7 @@ export type UserSocket = ServerDuplexStream; public group?: Group; - private _following: User|undefined; + private _following: User | undefined; private followedBy: Set = new Set(); public constructor( diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index d7178d3d..bafce4d3 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -846,12 +846,13 @@ export class SocketManager { handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { const leader = room.getUserById(message.getLeader()); if (!leader) { - console.info('Could not find user "', message.getLeader(), '" while handling a follow confirmation in room "', room.roomUrl,'". Maybe the user just left.'); + const message = `Could not follow user "{message.getLeader()}" in room "{room.roomUrl}".`; + console.info(message, "Maybe the user just left."); return; } - // By security, we look at the group leader. If the group leader is NOT the leader in the message, everybody should - // stop following the group leader (to avoid having 2 group leaders) + // By security, we look at the group leader. If the group leader is NOT the leader in the message, + // everybody should stop following the group leader (to avoid having 2 group leaders) if (user?.group?.leader && user?.group?.leader !== leader) { user?.group?.leader?.stopLeading(); } diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 955ed40f..1aaf2e13 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -26,14 +26,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: -9999, y: -9999, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); positionNotifier.addZoneListener({} as ZoneSocket, 0, 0); positionNotifier.addZoneListener({} as ZoneSocket, 0, 1); @@ -101,14 +101,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: 0, y: 0, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const listener = {} as ZoneSocket; positionNotifier.addZoneListener(listener, 0, 0); From 2cd088c0499b9de561ea80df0c9053b8c216d12d Mon Sep 17 00:00:00 2001 From: Lurkars Date: Sat, 18 Dec 2021 10:43:23 +0100 Subject: [PATCH 25/37] Change follow request to "F" button, use nes-css buttons --- .../Components/FollowMenu/FollowMenu.svelte | 28 ++++++------------- front/src/Phaser/Player/Player.ts | 2 +- .../src/Phaser/UserInput/UserInputManager.ts | 5 ++++ 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 590fd61c..983fc507 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -95,16 +95,19 @@ vim: ft=typescript

Do you want to follow {name(followUsers[0])}?

- - + +
{:else if followRole === followRoles.leader}

Ask others to follow you?

- - + +
{/if} @@ -125,8 +128,8 @@ vim: ft=typescript {/if}
- - + +
{/if} @@ -206,19 +209,6 @@ vim: ft=typescript font-weight: bold; height: 2.5em; } - - .accept { - background-color: #00ff0088; - } - .accept:hover { - background-color: #00ff00cc; - } - .deny { - background-color: #ff000088; - } - .deny:hover { - background-color: #ff0000cc; - } } } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index f34d1478..f37927e3 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -114,7 +114,7 @@ export class Player extends Character { const state = get(followStateStore); const role = get(followRoleStore); - if (activeEvents.get(UserInputEvent.Interact)) { + if (activeEvents.get(UserInputEvent.Follow)) { if (state === followStates.off && this.scene.groups.size > 0) { followStateStore.set(followStates.requesting); followRoleStore.set(followRoles.leader); diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index bb7041d2..71dbb6c3 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -16,6 +16,7 @@ export enum UserInputEvent { MoveDown, SpeedUp, Interact, + Follow, Shout, JoystickMove, } @@ -147,6 +148,10 @@ export class UserInputManager { event: UserInputEvent.Interact, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false), }, + { + event: UserInputEvent.Follow, + keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false), + }, { event: UserInputEvent.Shout, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false), From 3916d9c58e803e30ef6d4a24cc0a0c7c75c75b96 Mon Sep 17 00:00:00 2001 From: Lurkars Date: Sun, 19 Dec 2021 12:51:19 +0100 Subject: [PATCH 26/37] Add follow button to ui, improved flow --- front/src/Components/App.svelte | 4 +- .../Components/FollowMenu/FollowMenu.svelte | 40 +++++++++++++++++++ front/src/Components/images/follow.svg | 1 + front/src/Phaser/Player/Player.ts | 7 ++++ front/src/Stores/FollowStore.ts | 1 + 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 front/src/Components/images/follow.svg diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index 5f09beef..36f815bd 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -42,7 +42,7 @@ import AudioManager from "./AudioManager/AudioManager.svelte"; import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore"; import ReportMenu from "./ReportMenu/ReportMenu.svelte"; - import { followStateStore, followStates } from "../Stores/FollowStore"; + import { followStateStore, followRoleStore, followStates, followRoles } from "../Stores/FollowStore"; import FollowMenu from "./FollowMenu/FollowMenu.svelte"; export let game: Game; @@ -104,7 +104,7 @@ {/if} - {#if $followStateStore !== followStates.off} + {#if $followStateStore !== followStates.off || $followRoleStore === followRoles.open}
diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 983fc507..e26032bb 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -6,6 +6,7 @@ vim: ft=typescript import type { Unsubscriber } from "svelte/store"; import { get } from "svelte/store"; import { gameManager } from "../../Phaser/Game/GameManager"; + import followImg from "../images/follow.svg"; import { followStateStore, @@ -76,6 +77,11 @@ vim: ft=typescript followUsersStore.set([]); } + function request() { + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.leader); + } + function onKeyDown(e: KeyboardEvent) { if (e.key === "Escape") { reset(); @@ -152,6 +158,33 @@ vim: ft=typescript {/if} +{#if followRole === followRoles.open} + +{/if} + +{#if followState === followStates.active || followState === followStates.ending} + {#if followRole === followRoles.follower} + + {:else if followUsers.length > 0} + + {/if} +{/if} +