diff --git a/back/package.json b/back/package.json index 4fe7681d..d4bf6349 100644 --- a/back/package.json +++ b/back/package.json @@ -25,13 +25,15 @@ "@types/http-status-codes": "^1.2.0", "@types/jsonwebtoken": "^8.3.8", "@types/socket.io": "^2.1.4", + "@types/uuidv4": "^5.0.0", "body-parser": "^1.19.0", "express": "^4.17.1", "http-status-codes": "^1.4.0", "jsonwebtoken": "^8.5.1", "socket.io": "^2.3.0", "ts-node-dev": "^1.0.0-pre.44", - "typescript": "^3.8.3" + "typescript": "^3.8.3", + "uuidv4": "^6.0.7" }, "devDependencies": { "@types/jasmine": "^3.5.10", diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index 7555c67a..f9eed470 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -2,6 +2,7 @@ import {Application, Request, Response} from "express"; import Jwt from "jsonwebtoken"; import {BAD_REQUEST, OK} from "http-status-codes"; import {SECRET_KEY, ROOM} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import { uuid } from 'uuidv4'; export class AuthenticateController{ App : Application; @@ -21,8 +22,13 @@ export class AuthenticateController{ }); } //TODO check user email for The Coding Machine game - let token = Jwt.sign({email: param.email, roomId: ROOM}, SECRET_KEY, {expiresIn: '24h'}); - return res.status(OK).send({token: token, roomId: ROOM}); + let userId = uuid(); + let token = Jwt.sign({email: param.email, roomId: ROOM, userId: userId}, SECRET_KEY, {expiresIn: '24h'}); + return res.status(OK).send({ + token: token, + roomId: ROOM, + userId: userId + }); }); } } \ No newline at end of file diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 24392f09..1fc114a4 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -125,8 +125,7 @@ export class IoSocketController{ } arrayMap.forEach((value : any) => { let roomId = value[0]; - let data = value[1]; - this.Io.in(roomId).emit('user-position', JSON.stringify(data)); + this.Io.in(roomId).emit('user-position', JSON.stringify(arrayMap)); }); this.seTimeOutInProgress = setTimeout(() => { this.shareUsersPosition(); diff --git a/back/yarn.lock b/back/yarn.lock index 27f4503e..4b4f1fe2 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -135,6 +135,13 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/uuidv4@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/uuidv4/-/uuidv4-5.0.0.tgz#2c94e67b0c06d5adb28fb7ced1a1b5f0866ecd50" + integrity sha512-xUrhYSJnkTq9CP79cU3svoKTLPCIbMMnu9Twf/tMpHATYSHCAAeDNeb2a/29YORhk5p4atHhCTMsIBU/tvdh6A== + dependencies: + uuidv4 "*" + "@typescript-eslint/eslint-plugin@^2.26.0": version "2.26.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz#04c96560c8981421e5a9caad8394192363cc423f" @@ -2094,6 +2101,18 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + +uuidv4@*, uuidv4@^6.0.7: + version "6.0.7" + resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-6.0.7.tgz#15e920848e1afbbd97b4919bc50f4f2f2278f880" + integrity sha512-4mpYRFNqO22EckzxPSJ/+xjn9GgO6SAqEJ33yt23Y+HZZoZOt/6l4U4iIjc86ZfxSN2fSCGGmHNb3kiACFNd1g== + dependencies: + uuid "7.0.3" + v8-compile-cache@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts index d6f09110..707d31c4 100644 --- a/front/src/Connexion.ts +++ b/front/src/Connexion.ts @@ -4,13 +4,7 @@ const SocketIo = require('socket.io-client'); import Axios from "axios"; import {API_URL} from "./Enum/EnvironmentVariable"; -export interface PointInterface { - x: number; - y: number; - toJson() : object; -} - -export class Message { +class Message { userId: string; roomId: string; @@ -27,7 +21,14 @@ export class Message { } } -export class Point implements PointInterface{ +export interface PointInterface { + x: number; + y: number; + direction : string; + toJson() : object; +} + +class Point implements PointInterface{ x: number; y: number; direction : string; @@ -50,7 +51,12 @@ export class Point implements PointInterface{ } } -export class MessageUserPosition extends Message{ +export interface MessageUserPositionInterface { + userId: string; + roomId: string; + position: PointInterface; +} +class MessageUserPosition extends Message implements MessageUserPositionInterface{ position: PointInterface; constructor(userId : string, roomId : string, point : Point) { @@ -69,10 +75,36 @@ export class MessageUserPosition extends Message{ } } +export interface ListMessageUserPositionInterface { + roomId : string; + listUsersPosition: Array; +} +class ListMessageUserPosition{ + roomId : string; + listUsersPosition: Array; + + constructor(roomId : string, data : any) { + this.roomId = roomId; + this.listUsersPosition = new Array(); + data.forEach((userPosition: any) => { + this.listUsersPosition.push(new MessageUserPosition( + userPosition.userId, + userPosition.roomId, + new Point( + userPosition.position.x, + userPosition.position.y, + userPosition.position.direction + ) + )); + }); + } +} + export class Connexion { socket : any; token : string; email : string; + userId: string; startedRoom : string; GameManager: GameManagerInterface; @@ -80,10 +112,14 @@ export class Connexion { constructor(email : string, GameManager: GameManagerInterface) { this.email = email; this.GameManager = GameManager; - Axios.post(`${API_URL}/login`, {email: email}) + } + + createConnexion(){ + return Axios.post(`${API_URL}/login`, {email: this.email}) .then((res) => { this.token = res.data.token; this.startedRoom = res.data.roomId; + this.userId = res.data.userId; this.socket = SocketIo(`${API_URL}`, { query: { @@ -100,6 +136,11 @@ export class Connexion { this.positionOfAllUser(); this.errorMessage(); + + return{ + userId: this.userId, + roomId: this.startedRoom + } }) .catch((err) => { console.error(err); @@ -112,7 +153,7 @@ export class Connexion { * @param roomId */ joinARoom(roomId : string){ - let messageUserPosition = new MessageUserPosition(this.email, this.startedRoom, new Point(0, 0)); + let messageUserPosition = new MessageUserPosition(this.userId, this.startedRoom, new Point(0, 0)); this.socket.emit('join-room', messageUserPosition.toString()); } @@ -127,7 +168,7 @@ export class Connexion { if(!this.socket){ return; } - let messageUserPosition = new MessageUserPosition(this.email, roomId, new Point(x, y, direction)); + let messageUserPosition = new MessageUserPosition(this.userId, roomId, new Point(x, y, direction)); this.socket.emit('user-position', messageUserPosition.toString()); } @@ -146,11 +187,12 @@ export class Connexion { * ... * ] **/ - positionOfAllUser(){ - this.socket.on("user-position", (message : string) => { - let data = JSON.parse(message); - data.forEach((UserPositions : any) => { - this.GameManager.sharedUserPosition(UserPositions); + positionOfAllUser() { + this.socket.on("user-position", (message: string) => { + let dataList = JSON.parse(message); + dataList.forEach((UserPositions: any) => { + let listMessageUserPosition = new ListMessageUserPosition(UserPositions[0], UserPositions[1]); + this.GameManager.shareUserPosition(listMessageUserPosition); }); }); } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index ff9f5b5f..2ab1d0d7 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,31 +1,76 @@ import {GameSceneInterface, GameScene} from "./GameScene"; import {ROOM} from "../../Enum/EnvironmentVariable" -import {Connexion} from "../../Connexion"; +import {Connexion, ListMessageUserPositionInterface} from "../../Connexion"; + +export enum StatusGameManagerEnum { + IN_PROGRESS = 1, + CURRENT_USER_CREATED = 2 +} export let ConnexionInstance : Connexion; export interface GameManagerInterface { GameScenes: Array; - - sharedUserPosition(UserPositions: any): void; + status : number; + createCurrentPlayer() : void; + shareUserPosition(ListMessageUserPosition : ListMessageUserPositionInterface): void; } export class GameManager implements GameManagerInterface { GameScenes: Array = []; + status: number; constructor() { - this.configureGame(); + this.status = StatusGameManagerEnum.IN_PROGRESS; ConnexionInstance = new Connexion("test@gmail.com", this); } - configureGame() { - ROOM.forEach((roomId) => { - let newGame = new GameScene(roomId, this); - this.GameScenes.push(newGame); + createGame(){ + return ConnexionInstance.createConnexion().then((data: any) => { + this.configureGame(); + /** TODO add loader in the page **/ + }).catch((err) => { + console.error(err); + throw err; }); } - sharedUserPosition(UserPositions: any) { - let Game: GameSceneInterface = this.GameScenes.find((Game: GameSceneInterface) => Game.RoomId === UserPositions.roomId); - Game.sharedUserPosition(UserPositions) + /** + * permit to config rooms + */ + configureGame() { + ROOM.forEach((roomId) => { + let newGame = new GameScene(roomId, this); + this.GameScenes.push((newGame as GameSceneInterface)); + }); + } + + /** + * Permit to create player in started room + * @param RoomId + * @param UserId + */ + createCurrentPlayer(): void { + let game: GameSceneInterface = this.GameScenes.find((Game: GameSceneInterface) => Game.RoomId === ConnexionInstance.startedRoom); + game.createCurrentPlayer(ConnexionInstance.userId); + this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; + } + + /** + * Share position in game + * @param ListMessageUserPosition + */ + shareUserPosition(ListMessageUserPosition: ListMessageUserPositionInterface): void { + if (this.status === StatusGameManagerEnum.IN_PROGRESS) { + return; + } + try { + let Game: GameSceneInterface = this.GameScenes.find((Game: GameSceneInterface) => Game.RoomId === ListMessageUserPosition.roomId); + if (!Game) { + return; + } + Game.shareUserPosition(ListMessageUserPosition.listUsersPosition) + } catch (e) { + console.error(e); + } } } \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index d3abfd15..61afdf68 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,12 +1,15 @@ import {MapManagerInterface, MapManager} from "./MapManager"; -import {GameManagerInterface} from "./GameManager"; +import {GameManagerInterface, StatusGameManagerEnum} from "./GameManager"; +import {MessageUserPositionInterface} from "../../Connexion"; export interface GameSceneInterface extends Phaser.Scene { RoomId : string; - sharedUserPosition(data : []): void; + createCurrentPlayer(UserId : string) : void; + shareUserPosition(UsersPosition : Array): void; } export class GameScene extends Phaser.Scene implements GameSceneInterface{ private MapManager : MapManagerInterface; + GameManager : GameManagerInterface; RoomId : string; constructor(RoomId : string, GameManager : GameManagerInterface) { @@ -14,6 +17,7 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ key: "GameScene" }); this.RoomId = RoomId; + this.GameManager = GameManager; } //hook preload scene @@ -33,15 +37,31 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ create(): void { //create map manager this.MapManager = new MapManager(this); + //notify game manager can to create currentUser in map + this.GameManager.createCurrentPlayer(); + } + + /** + * Create current player + * @param UserId + */ + createCurrentPlayer(UserId : string): void { + this.MapManager.createCurrentPlayer(UserId) } //hook update update(dt: number): void { + if(this.GameManager.status === StatusGameManagerEnum.IN_PROGRESS){ + return; + } this.MapManager.update(); } - sharedUserPosition(data: []): void { - //TODO share position of all user - //console.log("sharedUserPosition", data); + /** + * Share position in scene + * @param UsersPosition + */ + shareUserPosition(UsersPosition : Array): void { + this.MapManager.updateOrCreateMapPlayer(UsersPosition); } } diff --git a/front/src/Phaser/Game/MapManager.ts b/front/src/Phaser/Game/MapManager.ts index 8b3d9231..99ce2863 100644 --- a/front/src/Phaser/Game/MapManager.ts +++ b/front/src/Phaser/Game/MapManager.ts @@ -1,7 +1,8 @@ import {CameraManager, CameraManagerInterface} from "./CameraManager"; import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {Player} from "../Player/Player"; -import {GameScene, GameSceneInterface} from "./GameScene"; +import {GameSceneInterface} from "./GameScene"; +import {MessageUserPositionInterface} from "../../Connexion"; export interface MapManagerInterface { keyZ: Phaser.Input.Keyboard.Key; @@ -18,7 +19,10 @@ export interface MapManagerInterface { Terrain: Phaser.Tilemaps.Tileset; Camera: CameraManagerInterface; Scene: GameSceneInterface; + + createCurrentPlayer(UserId : string): void; update(): void; + updateOrCreateMapPlayer(UsersPosition : Array): void; } export class MapManager implements MapManagerInterface{ keyZ: Phaser.Input.Keyboard.Key; @@ -34,6 +38,7 @@ export class MapManager implements MapManagerInterface{ Terrain : Phaser.Tilemaps.Tileset; Camera: CameraManagerInterface; CurrentPlayer: Player; + MapPlayers : Player[]; Scene: GameSceneInterface; Map: Phaser.Tilemaps.Tilemap; startX = (window.innerWidth / 2) / RESOLUTION; @@ -51,11 +56,16 @@ export class MapManager implements MapManagerInterface{ //initialise keyboard this.initKeyBoard(); - //initialise camera this.Camera = new CameraManager(this.Scene, this.Scene.cameras.main, this); + //initialise list of other player + this.MapPlayers = new Array(); + } + + createCurrentPlayer(UserId : string){ //initialise player this.CurrentPlayer = new Player( + UserId, this.Scene, this.startX, this.startY, @@ -65,7 +75,6 @@ export class MapManager implements MapManagerInterface{ this.CurrentPlayer.initAnimation(); } - initKeyBoard() { this.keyShift = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT); @@ -83,4 +92,57 @@ export class MapManager implements MapManagerInterface{ update() : void { this.CurrentPlayer.move(); } + + /** + * Create new player and clean the player on the map + * @param UsersPosition + */ + updateOrCreateMapPlayer(UsersPosition : Array){ + if(!this.CurrentPlayer){ + return; + } + + //add or create new user + UsersPosition.forEach((userPosition : MessageUserPositionInterface) => { + if(userPosition.userId === this.CurrentPlayer.userId){ + return; + } + let player = this.MapPlayers.find((player: Player) => userPosition.userId === player.userId); + if(!player){ + this.addPlayer(userPosition); + }else{ + player.updatePosition(userPosition); + } + }); + + //clean map + let mapPlayers = new Array(); + this.MapPlayers.forEach((player: Player) => { + if(UsersPosition.find((message : MessageUserPositionInterface) => message.userId === player.userId)){ + mapPlayers.push(player); + return; + } + player.destroy(); + }); + this.MapPlayers = mapPlayers; + } + + /** + * Create new player + * @param MessageUserPosition + */ + addPlayer(MessageUserPosition : MessageUserPositionInterface){ + //initialise player + let player = new Player( + MessageUserPosition.userId, + this.Scene, + MessageUserPosition.position.x, + MessageUserPosition.position.y, + this.Camera, + this + ); + player.initAnimation(); + this.MapPlayers.push(player); + player.updatePosition(MessageUserPosition) + } } \ No newline at end of file diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 563843fd..74db636d 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -3,13 +3,16 @@ import {getPlayerAnimations, playAnimation, PlayerAnimationNames} from "./Animat import {GameSceneInterface} from "../Game/GameScene"; import {ConnexionInstance} from "../Game/GameManager"; import {CameraManagerInterface} from "../Game/CameraManager"; +import {MessageUserPositionInterface} from "../../Connexion"; export class Player extends Phaser.GameObjects.Sprite{ + userId : string; MapManager : MapManagerInterface; PlayerValue : string; CameraManager: CameraManagerInterface; constructor( + userId: string, Scene : GameSceneInterface, x : number, y : number, @@ -18,13 +21,13 @@ export class Player extends Phaser.GameObjects.Sprite{ PlayerValue : string = "player" ) { super(Scene, x, y, PlayerValue); + this.userId = userId; this.PlayerValue = PlayerValue; Scene.add.existing(this); this.MapManager = MapManager; this.CameraManager = CameraManager; } - initAnimation(){ getPlayerAnimations(this.PlayerValue).forEach(d => { this.scene.anims.create({ @@ -80,10 +83,9 @@ export class Player extends Phaser.GameObjects.Sprite{ } if(!haveMove){ playAnimation(this, PlayerAnimationNames.None); - }else{ - this.sharePosition(direction); + direction = PlayerAnimationNames.None; } - + this.sharePosition(direction); this.CameraManager.moveCamera(this); } @@ -108,4 +110,10 @@ export class Player extends Phaser.GameObjects.Sprite{ private CanMoveRight(){ return this.MapManager.Map.widthInPixels > this.x; } + + updatePosition(MessageUserPosition : MessageUserPositionInterface){ + playAnimation(this, MessageUserPosition.position.direction); + this.setX(MessageUserPosition.position.x); + this.setY(MessageUserPosition.position.y); + } } \ No newline at end of file diff --git a/front/src/index.ts b/front/src/index.ts index 650fd52c..2a18110d 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -14,8 +14,10 @@ const config: GameConfig = { zoom: RESOLUTION, }; -let game = new Phaser.Game(config); +gameManager.createGame().then(() => { + let game = new Phaser.Game(config); -window.addEventListener('resize', function (event) { - game.scale.resize(window.innerWidth / RESOLUTION, window.innerHeight / RESOLUTION); + window.addEventListener('resize', function (event) { + game.scale.resize(window.innerWidth / RESOLUTION, window.innerHeight / RESOLUTION); + }); });