From c07079051a39d0cddf40e1d4846d8490840630a2 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 21:21:11 +0200 Subject: [PATCH] transmit companion to remote players --- back/src/Model/GameRoom.ts | 3 ++- back/src/Model/User.ts | 5 +++-- back/src/Services/SocketManager.ts | 2 ++ front/src/Connexion/ConnectionManager.ts | 6 +++--- front/src/Connexion/ConnexionModels.ts | 4 +++- front/src/Connexion/LocalUserStore.ts | 8 ++++++++ front/src/Connexion/RoomConnection.ts | 11 ++++++++-- front/src/Phaser/Companion/Companion.ts | 20 ++++++++----------- front/src/Phaser/Entity/Character.ts | 4 ++-- front/src/Phaser/Entity/RemotePlayer.ts | 9 +++++++-- front/src/Phaser/Game/AddPlayerInterface.ts | 1 + front/src/Phaser/Game/GameManager.ts | 5 +++++ front/src/Phaser/Game/GameScene.ts | 16 ++++++++++----- front/src/Phaser/Player/Player.ts | 11 +++++----- messages/protos/messages.proto | 7 +++++++ pusher/src/Controller/IoSocketController.ts | 12 ++++++++++- .../src/Model/Websocket/ExSocketInterface.ts | 2 ++ pusher/src/Model/Zone.ts | 8 +++++--- pusher/src/Services/SocketManager.ts | 2 ++ 19 files changed, 96 insertions(+), 40 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 6a592ed0..430dbc48 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -110,7 +110,8 @@ export class GameRoom { socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), - ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()) + ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()), + joinRoomMessage.getCompanion() ); this.nextUserId++; this.users.set(user.id, user); diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 51a1a617..370781e5 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -4,7 +4,7 @@ import {Zone} from "_Model/Zone"; import {Movable} from "_Model/Movable"; import {PositionNotifier} from "_Model/PositionNotifier"; import {ServerDuplexStream} from "grpc"; -import {BatchMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; +import {BatchMessage, Companion, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; import {CharacterLayer} from "_Model/Websocket/CharacterLayer"; export type UserSocket = ServerDuplexStream; @@ -23,7 +23,8 @@ export class User implements Movable { public readonly socket: UserSocket, public readonly tags: string[], public readonly name: string, - public readonly characterLayers: CharacterLayer[] + public readonly characterLayers: CharacterLayer[], + public readonly companion?: Companion ) { this.listenedZones = new Set(); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index c03f4773..166622f9 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -325,6 +325,7 @@ export class SocketManager { userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone)); + userJoinedZoneMessage.setCompanion(thing.companion); const subMessage = new SubToPusherMessage(); subMessage.setUserjoinedzonemessage(userJoinedZoneMessage); @@ -634,6 +635,7 @@ export class SocketManager { userJoinedMessage.setName(thing.name); userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); + userJoinedMessage.setCompanion(thing.companion); const subMessage = new SubToPusherMessage(); subMessage.setUserjoinedzonemessage(userJoinedMessage); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index ed920aa0..e1e0db0a 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -88,9 +88,9 @@ class ConnectionManager { this.localUser = new LocalUser('', 'test', []); } - public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise { + public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null): Promise { return new Promise((resolve, reject) => { - const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport); + const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport, companion); connection.onConnectError((error: object) => { console.log('An error occurred while connecting to socket server. Retrying'); reject(error); @@ -111,7 +111,7 @@ class ConnectionManager { this.reconnectingTimeout = setTimeout(() => { //todo: allow a way to break recursion? //todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely. - this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection)); + this.connectToRoomSocket(roomId, name, characterLayers, position, viewport, companion).then((connection) => resolve(connection)); }, 4000 + Math.floor(Math.random() * 2000) ); }); }); diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 519afcd3..477e86e3 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -47,6 +47,7 @@ export interface MessageUserPositionInterface { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + companion: string|null; } export interface MessageUserMovedInterface { @@ -58,7 +59,8 @@ export interface MessageUserJoined { userId: number; name: string; characterLayers: BodyResourceDescriptionInterface[]; - position: PointInterface + position: PointInterface; + companion: string|null; } export interface PositionInterface { diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 702df561..4989a9ea 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -4,6 +4,7 @@ const playerNameKey = 'playerName'; const selectedPlayerKey = 'selectedPlayer'; const customCursorPositionKey = 'customCursorPosition'; const characterLayersKey = 'characterLayers'; +const companionKey = 'companion'; const gameQualityKey = 'gameQuality'; const videoQualityKey = 'videoQuality'; const audioPlayerVolumeKey = 'audioVolume'; @@ -47,6 +48,13 @@ class LocalUserStore { return JSON.parse(localStorage.getItem(characterLayersKey) || "null"); } + setCompanion(companion: string): void { + localStorage.setItem(companionKey, companion); + } + getCompanion(): string|null { + return localStorage.getItem(companionKey); + } + setGameQualityValue(value: number): void { localStorage.setItem(gameQualityKey, '' + value); } diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 584dae06..391d227d 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -66,7 +66,7 @@ export class RoomConnection implements RoomConnection { * @param token A JWT token containing the UUID of the user * @param roomId The ID of the room in the form "_/[instance]/[map_url]" or "@/[org]/[event]/[map]" */ - public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { + public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null) { let url = API_URL.replace('http://', 'ws://').replace('https://', 'wss://'); url += '/room'; url += '?roomId='+(roomId ?encodeURIComponent(roomId):''); @@ -81,6 +81,10 @@ export class RoomConnection implements RoomConnection { url += '&bottom='+Math.floor(viewport.bottom); url += '&left='+Math.floor(viewport.left); url += '&right='+Math.floor(viewport.right); + + if (typeof companion === 'string') { + url += '&companion='+encodeURIComponent(companion); + } if (RoomConnection.websocketFactory) { this.socket = RoomConnection.websocketFactory(url); @@ -316,11 +320,14 @@ export class RoomConnection implements RoomConnection { } }) + const companion = message.getCompanion(); + return { userId: message.getUserid(), name: message.getName(), characterLayers, - position: ProtobufClientUtils.toPointInterface(position) + position: ProtobufClientUtils.toPointInterface(position), + companion: companion ? companion.getName() : null } } diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 60e58995..196da09e 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -22,11 +22,7 @@ export class Companion extends Container { private direction: PlayerAnimationDirections; private animationType: PlayerAnimationTypes; - constructor( - scene: Phaser.Scene, - x: number, - y: number - ) { + constructor(scene: Phaser.Scene, x: number, y: number, name: string) { super(scene, x + 8, y + 8); this.sprites = new Map(); @@ -38,11 +34,7 @@ export class Companion extends Container { this.direction = PlayerAnimationDirections.Down; this.animationType = PlayerAnimationTypes.Idle; - // select random animal - const animal = ["dog1", "dog2", "dog3", "cat1", "cat2", "cat3"]; - const random = Math.floor(Math.random() * animal.length); - - this.companionName = animal[random]; + this.companionName = name; lazyLoadResource(this.scene.load, this.companionName).then(resource => { this.addResource(resource); @@ -59,14 +51,16 @@ export class Companion extends Container { this.setDepth(-1); - scene.add.existing(this); + this.scene.events.addListener('update', this.step, this); + + this.scene.add.existing(this); } public setTarget(x: number, y: number, direction: PlayerAnimationDirections) { this.target = { x, y, direction }; } - public step(delta: number) { + public step(time: number, delta: number) { if (typeof this.target === 'undefined') return; this.delta += delta; @@ -216,6 +210,8 @@ export class Companion extends Container { } } + this.scene.events.removeListener('update', this.step, this); + super.destroy(); } } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index f329192c..eb8e45fd 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -71,8 +71,8 @@ export abstract class Character extends Container { this.playAnimation(direction, moving); } - public addCompanion(): void { - this.companion = new Companion(this.scene, this.x, this.y); + public addCompanion(name: string): void { + this.companion = new Companion(this.scene, this.x, this.y, name); } public addTextures(textures: string[], frame?: string | number): void { diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 4fe18fc6..b405d8df 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -17,12 +17,17 @@ export class RemotePlayer extends Character { name: string, texturesPromise: Promise, direction: PlayerAnimationDirections, - moving: boolean + moving: boolean, + companion: string|null ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1); - + //set data this.userId = userId; + + if (typeof companion === 'string') { + this.addCompanion(companion); + } } updatePosition(position: PointInterface): void { diff --git a/front/src/Phaser/Game/AddPlayerInterface.ts b/front/src/Phaser/Game/AddPlayerInterface.ts index 0edf197e..96762a66 100644 --- a/front/src/Phaser/Game/AddPlayerInterface.ts +++ b/front/src/Phaser/Game/AddPlayerInterface.ts @@ -6,4 +6,5 @@ export interface AddPlayerInterface { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + companion: string|null; } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 8751796f..fbe5102c 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -21,12 +21,14 @@ export interface HasMovedEvent { export class GameManager { private playerName: string|null; private characterLayers: string[]|null; + private companion: string|null; private startRoom!:Room; currentGameSceneName: string|null = null; constructor() { this.playerName = localUserStore.getName(); this.characterLayers = localUserStore.getCharacterLayers(); + this.companion = localUserStore.getCompanion(); } public async init(scenePlugin: Phaser.Scenes.ScenePlugin): Promise { @@ -63,6 +65,9 @@ export class GameManager { return this.characterLayers; } + getCompanion(): string|null { + return this.companion; + } public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise { const roomID = room.id; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 51388169..222f7ed5 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -159,6 +159,7 @@ export class GameScene extends ResizableScene implements CenterListener { private openChatIcon!: OpenChatIcon; private playerName!: string; private characterLayers!: string[]; + private companion!: string|null; private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); @@ -335,7 +336,7 @@ export class GameScene extends ResizableScene implements CenterListener { } this.playerName = playerName; this.characterLayers = gameManager.getCharacterLayers(); - + this.companion = gameManager.getCompanion(); //initalise map this.Map = this.add.tilemap(this.MapUrlFile); @@ -459,7 +460,9 @@ export class GameScene extends ResizableScene implements CenterListener { top: camera.scrollY, right: camera.scrollX + camera.width, bottom: camera.scrollY + camera.height, - }).then((onConnect: OnConnectInterface) => { + }, + this.companion + ).then((onConnect: OnConnectInterface) => { this.connection = onConnect.connection; this.connection.onUserJoins((message: MessageUserJoined) => { @@ -467,7 +470,8 @@ export class GameScene extends ResizableScene implements CenterListener { userId: message.userId, characterLayers: message.characterLayers, name: message.name, - position: message.position + position: message.position, + companion: message.companion } this.addPlayer(userMessage); }); @@ -1019,7 +1023,8 @@ ${escapedMessage} texturesPromise, PlayerAnimationDirections.Down, false, - this.userInputManager + this.userInputManager, + this.companion ); }catch (err){ if(err instanceof TextureError) { @@ -1211,7 +1216,8 @@ ${escapedMessage} addPlayerData.name, texturesPromise, addPlayerData.position.direction as PlayerAnimationDirections, - addPlayerData.position.moving + addPlayerData.position.moving, + addPlayerData.companion ); this.MapPlayers.add(player); this.MapPlayersByKey.set(player.userId, player); diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index af63ddb3..d018a41f 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -21,14 +21,17 @@ export class Player extends Character implements CurrentGamerInterface { texturesPromise: Promise, direction: PlayerAnimationDirections, moving: boolean, - private userInputManager: UserInputManager + private userInputManager: UserInputManager, + companion: string|null ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1); //the current player model should be push away by other players to prevent conflict this.getBody().setImmovable(false); - this.addCompanion(); + if (typeof companion === 'string') { + this.addCompanion(companion); + } } moveUser(delta: number): void { @@ -61,10 +64,6 @@ export class Player extends Character implements CurrentGamerInterface { moving = true; } - if (this.companion) { - this.companion.step(delta); - } - if (x !== 0 || y !== 0) { this.move(x, y); this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y}); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index cc23ed24..eee3be86 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -36,6 +36,10 @@ message CharacterLayerMessage { string name = 2; } +message Companion { + string name = 1; +} + /*********** CLIENT TO SERVER MESSAGES *************/ message PingMessage { @@ -141,6 +145,7 @@ message UserJoinedMessage { string name = 2; repeated CharacterLayerMessage characterLayers = 3; PositionMessage position = 4; + Companion companion = 5; } message UserLeftMessage { @@ -243,6 +248,7 @@ message JoinRoomMessage { string roomId = 5; repeated string tag = 6; string IPAddress = 7; + Companion companion = 8; } message UserJoinedZoneMessage { @@ -251,6 +257,7 @@ message UserJoinedZoneMessage { repeated CharacterLayerMessage characterLayers = 3; PositionMessage position = 4; Zone fromZone = 5; + Companion companion = 6; } message UserLeftZoneMessage { diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 85a80e11..55a8e032 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -12,7 +12,7 @@ import { WebRtcSignalToServerMessage, PlayGlobalMessage, ReportPlayerMessage, - QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage + QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, Companion } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {TemplatedApp} from "uWebSockets.js" @@ -138,6 +138,14 @@ export class IoSocketController { const left = Number(query.left); const right = Number(query.right); const name = query.name; + + let companion: Companion|undefined = undefined; + + if (typeof query.companion === 'string') { + companion = new Companion(); + companion.setName(query.companion); + } + if (typeof name !== 'string') { throw new Error('Expecting name'); } @@ -221,6 +229,7 @@ export class IoSocketController { IPAddress, roomId, name, + companion, characterLayers: characterLayerObjs, messages: memberMessages, tags: memberTags, @@ -350,6 +359,7 @@ export class IoSocketController { client.tags = ws.tags; client.textures = ws.textures; client.characterLayers = ws.characterLayers; + client.companion = ws.companion; client.roomId = ws.roomId; client.listenedZones = new Set(); return client; diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 56e7e5ca..135c6f10 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -3,6 +3,7 @@ import {Identificable} from "./Identificable"; import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; import { BatchMessage, + Companion, PusherToBackMessage, ServerToClientMessage, SubMessage @@ -29,6 +30,7 @@ export interface ExSocketInterface extends WebSocket, Identificable { characterLayers: CharacterLayer[]; position: PointInterface; viewport: ViewportInterface; + companion?: Companion; /** * Pushes an event that will be sent in the next batch of events */ diff --git a/pusher/src/Model/Zone.ts b/pusher/src/Model/Zone.ts index 12d27ff3..a54481a5 100644 --- a/pusher/src/Model/Zone.ts +++ b/pusher/src/Model/Zone.ts @@ -5,7 +5,8 @@ import { CharacterLayerMessage, GroupLeftZoneMessage, GroupUpdateMessage, GroupUpdateZoneMessage, PointMessage, PositionMessage, UserJoinedMessage, UserJoinedZoneMessage, UserLeftZoneMessage, UserMovedMessage, - ZoneMessage + ZoneMessage, + Companion } from "../Messages/generated/messages_pb"; import * as messages_pb from "../Messages/generated/messages_pb"; import {ClientReadableStream} from "grpc"; @@ -30,7 +31,7 @@ export type MovesCallback = (thing: Movable, position: PositionInterface, listen export type LeavesCallback = (thing: Movable, listener: User) => void;*/ export class UserDescriptor { - private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage) { + private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage, private companion?: Companion) { if (!Number.isInteger(this.userId)) { throw new Error('UserDescriptor.userId is not an integer: '+this.userId); } @@ -41,7 +42,7 @@ export class UserDescriptor { if (position === undefined) { throw new Error('Missing position'); } - return new UserDescriptor(message.getUserid(), message.getName(), message.getCharacterlayersList(), position); + return new UserDescriptor(message.getUserid(), message.getName(), message.getCharacterlayersList(), position, message.getCompanion()); } public update(userMovedMessage: UserMovedMessage) { @@ -59,6 +60,7 @@ export class UserDescriptor { userJoinedMessage.setName(this.name); userJoinedMessage.setCharacterlayersList(this.characterLayers); userJoinedMessage.setPosition(this.position); + userJoinedMessage.setCompanion(this.companion) return userJoinedMessage; } diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 9b698e38..d555a59c 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -153,6 +153,8 @@ export class SocketManager implements ZoneEventListener { joinRoomMessage.setName(client.name); joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position)); joinRoomMessage.setTagList(client.tags); + joinRoomMessage.setCompanion(client.companion); + for (const characterLayer of client.characterLayers) { const characterLayerMessage = new CharacterLayerMessage(); characterLayerMessage.setName(characterLayer.name);