diff --git a/front/src/Connection.ts b/front/src/Connection.ts index b6b251f0..cad639f9 100644 --- a/front/src/Connection.ts +++ b/front/src/Connection.ts @@ -1,4 +1,4 @@ -import {GameManager} from "./Phaser/Game/GameManager"; +import {gameManager, GameManager} from "./Phaser/Game/GameManager"; import Axios from "axios"; import {API_URL} from "./Enum/EnvironmentVariable"; import {MessageUI} from "./Logger/MessageUI"; @@ -118,37 +118,13 @@ export interface WebRtcSignalMessageInterface { signal: SignalData } -export interface ConnectionInterface { - socket: Socket|null; - token: string|null; - name: string|null; - userId: string|null; - - createConnection(name: string, characterSelected: string): Promise; - - loadStartMap(): Promise; - - joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): void; - - sharePosition(x: number, y: number, direction: string, moving: boolean): void; - - /*webrtc*/ - sendWebrtcSignal(signal: unknown, roomId: string, userId?: string|null, receiverId?: string): void; - - receiveWebrtcSignal(callBack: Function): void; - - receiveWebrtcStart(callBack: (message: WebRtcStartMessageInterface) => void): void; - - disconnectMessage(callBack: (message: WebRtcDisconnectMessageInterface) => void): void; -} - export interface StartMapInterface { mapUrlStart: string, startInstance: string } -export class Connection implements ConnectionInterface { - socket: Socket|null = null; +export class Connection implements Connection { + socket: Socket; token: string|null = null; name: string|null = null; // TODO: drop "name" storage here character: string|null = null; @@ -159,102 +135,78 @@ export class Connection implements ConnectionInterface { lastPositionShared: PointInterface|null = null; lastRoom: string|null = null; - constructor(GameManager: GameManager) { + private constructor(GameManager: GameManager, name: string, character: string, token: string) { this.GameManager = GameManager; + this.name = name; + this.character = character; + this.token = token; + + this.socket = SocketIo(`${API_URL}`, { + query: { + token: this.token + }, + reconnection: false // Reconnection is handled by the application itself + }); + + this.socket.on(EventMessage.MESSAGE_ERROR, (message: string) => { + console.error(EventMessage.MESSAGE_ERROR, message); + }) } - createConnection(name: string, characterSelected: string): Promise { - this.name = name; - this.character = characterSelected; + public static createConnection(name: string, characterSelected: string): Promise { return Axios.post(`${API_URL}/login`, {name: name}) .then((res) => { - this.token = res.data.token; - this.socket = SocketIo(`${API_URL}`, { - query: { - token: this.token - } + + return new Promise((resolve, reject) => { + const connection = new Connection(gameManager, name, characterSelected, res.data.token); + + connection.onConnectError((error: object) => { + console.log('An error occurred while connecting to socket server. Retrying'); + reject(error); + }); + + connection.socket.emit(EventMessage.SET_PLAYER_DETAILS, { + name: connection.name, + character: connection.character + } as SetPlayerDetailsMessage, (id: string) => { + connection.userId = id; + }); + + resolve(connection); }); - - //listen event - this.disconnectServer(); - this.errorMessage(); - this.groupUpdatedOrCreated(); - this.groupDeleted(); - this.onUserJoins(); - this.onUserMoved(); - this.onUserLeft(); - - return this.connectSocketServer(); }) .catch((err) => { - console.error(err); - throw err; + // Let's retry in 4-6 seconds + return new Promise((resolve, reject) => { + setTimeout(() => { + Connection.createConnection(name, characterSelected).then((connection) => resolve(connection)) + .catch((error) => reject(error)); + }, 4000 + Math.floor(Math.random() * 2000) ); + }); + + //console.error(err); + //throw err; }); } - private getSocket(): Socket { - if (this.socket === null) { - throw new Error('Socket not initialized while using Connection') - } - return this.socket; + public closeConnection(): void { + this.socket?.close(); + this.lastPositionShared = null; + this.lastRoom = null; } - /** - * - * @param character - */ - connectSocketServer(): Promise{ - return new Promise((resolve, reject) => { - this.getSocket().emit(EventMessage.SET_PLAYER_DETAILS, { - name: this.name, - character: this.character - } as SetPlayerDetailsMessage, (id: string) => { - this.userId = id; - }); - //if try to reconnect with last position - /*if(this.lastRoom) { - //join the room - this.joinARoom(this.lastRoom, - this.lastPositionShared ? this.lastPositionShared.x : 0, - this.lastPositionShared ? this.lastPositionShared.y : 0, - this.lastPositionShared ? this.lastPositionShared.direction : PlayerAnimationNames.WalkDown, - this.lastPositionShared ? this.lastPositionShared.moving : false); - }*/ - - /*if(this.lastPositionShared) { - - //share your first position - this.sharePosition( - this.lastPositionShared ? this.lastPositionShared.x : 0, - this.lastPositionShared ? this.lastPositionShared.y : 0, - this.lastPositionShared.direction, - this.lastPositionShared.moving - ); - }*/ - - resolve(this); - }); - } - - //TODO add middleware with access token to secure api - loadStartMap() : Promise { - return Axios.get(`${API_URL}/start-map`) - .then((res) => { - return res.data; - }).catch((err) => { - console.error(err); - throw err; - }); - } - - joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): void { + joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): Promise { const point = new Point(startX, startY, direction, moving); this.lastPositionShared = point; - this.getSocket().emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (userPositions: MessageUserPositionInterface[]) => { - this.GameManager.initUsersPosition(userPositions); - }); + const promise = new Promise((resolve, reject) => { + this.socket.emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (userPositions: MessageUserPositionInterface[]) => { + //this.GameManager.initUsersPosition(userPositions); + resolve(userPositions); + }); + }) this.lastRoom = roomId; + return promise; } sharePosition(x : number, y : number, direction : string, moving: boolean) : void{ @@ -263,42 +215,35 @@ export class Connection implements ConnectionInterface { } const point = new Point(x, y, direction, moving); this.lastPositionShared = point; - this.getSocket().emit(EventMessage.USER_POSITION, point); + this.socket.emit(EventMessage.USER_POSITION, point); } - private onUserJoins(): void { - this.getSocket().on(EventMessage.JOIN_ROOM, (message: MessageUserJoined) => { - this.GameManager.onUserJoins(message); - }); + public onUserJoins(callback: (message: MessageUserJoined) => void): void { + this.socket.on(EventMessage.JOIN_ROOM, callback); } - private onUserMoved(): void { - this.getSocket().on(EventMessage.USER_MOVED, (message: MessageUserMovedInterface) => { - this.GameManager.onUserMoved(message); - }); + public onUserMoved(callback: (message: MessageUserMovedInterface) => void): void { + this.socket.on(EventMessage.USER_MOVED, callback); } - private onUserLeft(): void { - this.getSocket().on(EventMessage.USER_LEFT, (userId: string) => { - this.GameManager.onUserLeft(userId); - }); + public onUserLeft(callback: (userId: string) => void): void { + this.socket.on(EventMessage.USER_LEFT, callback); } - private groupUpdatedOrCreated(): void { - this.getSocket().on(EventMessage.GROUP_CREATE_UPDATE, (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => { - //console.log('Group ', groupCreateUpdateMessage.groupId, " position :", groupCreateUpdateMessage.position.x, groupCreateUpdateMessage.position.y) - this.GameManager.shareGroupPosition(groupCreateUpdateMessage); - }) + public onGroupUpdatedOrCreated(callback: (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => void): void { + this.socket.on(EventMessage.GROUP_CREATE_UPDATE, callback); } - private groupDeleted(): void { - this.getSocket().on(EventMessage.GROUP_DELETE, (groupId: string) => { - this.GameManager.deleteGroup(groupId); - }) + public onGroupDeleted(callback: (groupId: string) => void): void { + this.socket.on(EventMessage.GROUP_DELETE, callback) + } + + public onConnectError(callback: (error: object) => void): void { + this.socket.on(EventMessage.CONNECT_ERROR, callback) } sendWebrtcSignal(signal: unknown, roomId: string, userId? : string|null, receiverId? : string) { - return this.getSocket().emit(EventMessage.WEBRTC_SIGNAL, { + return this.socket.emit(EventMessage.WEBRTC_SIGNAL, { userId: userId ? userId : this.userId, receiverId: receiverId ? receiverId : this.userId, roomId: roomId, @@ -307,47 +252,53 @@ export class Connection implements ConnectionInterface { } receiveWebrtcStart(callback: (message: WebRtcStartMessageInterface) => void) { - this.getSocket().on(EventMessage.WEBRTC_START, callback); + this.socket.on(EventMessage.WEBRTC_START, callback); } receiveWebrtcSignal(callback: (message: WebRtcSignalMessageInterface) => void) { - return this.getSocket().on(EventMessage.WEBRTC_SIGNAL, callback); + return this.socket.on(EventMessage.WEBRTC_SIGNAL, callback); } - private errorMessage(): void { - this.getSocket().on(EventMessage.MESSAGE_ERROR, (message: string) => { - console.error(EventMessage.MESSAGE_ERROR, message); - }) - } + public onServerDisconnected(callback: (reason: string) => void): void { + /*this.socket.on(EventMessage.CONNECT_ERROR, (error: object) => { + callback(error); + });*/ - private disconnectServer(): void { - this.getSocket().on(EventMessage.CONNECT_ERROR, () => { - this.GameManager.switchToDisconnectedScene(); + this.socket.on('disconnect', (reason: string) => { + if (reason === 'io client disconnect') { + // The client asks for disconnect, let's not trigger any event. + return; + } + callback(reason); }); - this.getSocket().on(EventMessage.RECONNECTING, () => { + /*this.socket.on(EventMessage.CONNECT_ERROR, (error: object) => { + this.GameManager.switchToDisconnectedScene(); + });*/ + + /*this.socket.on(EventMessage.RECONNECTING, () => { console.log('Trying to reconnect'); }); - this.getSocket().on(EventMessage.RECONNECT_ERROR, () => { + this.socket.on(EventMessage.RECONNECT_ERROR, () => { console.log('Error while trying to reconnect.'); }); - this.getSocket().on(EventMessage.RECONNECT_FAILED, () => { + this.socket.on(EventMessage.RECONNECT_FAILED, () => { console.error('Reconnection failed. Giving up.'); }); - this.getSocket().on(EventMessage.RECONNECT, () => { + this.socket.on(EventMessage.RECONNECT, () => { console.log('Reconnect event triggered'); this.connectSocketServer(); if (this.lastPositionShared === null) { throw new Error('No last position shared found while reconnecting'); } this.GameManager.reconnectToGameScene(this.lastPositionShared); - }); + });*/ } disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void { - this.getSocket().on(EventMessage.WEBRTC_DISCONNECT, callback); + this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback); } } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index ec20411f..071f1780 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,24 +1,21 @@ import {GameScene} from "./GameScene"; import { - Connection, ConnectionInterface, + Connection, GroupCreatedUpdatedMessageInterface, ListMessageUserPositionInterface, MessageUserJoined, MessageUserMovedInterface, MessageUserPositionInterface, Point, - PointInterface + PointInterface, StartMapInterface } from "../../Connection"; import {SimplePeer} from "../../WebRtc/SimplePeer"; import {AddPlayerInterface} from "./AddPlayerInterface"; import {ReconnectingScene, ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; import ScenePlugin = Phaser.Scenes.ScenePlugin; import {Scene} from "phaser"; - -/*export enum StatusGameManagerEnum { - IN_PROGRESS = 1, - CURRENT_USER_CREATED = 2 -}*/ +import Axios from "axios"; +import {API_URL} from "../../Enum/EnvironmentVariable"; export interface HasMovedEvent { direction: string; @@ -33,126 +30,32 @@ export interface MapObject { } export class GameManager { - //status: number; - private ConnectionInstance: Connection; - private currentGameScene: GameScene|null = null; private playerName: string; - SimplePeer : SimplePeer; private characterUserSelected: string; - constructor() { - //this.status = StatusGameManagerEnum.IN_PROGRESS; - } - - public connect(name: string, characterUserSelected : string): Promise { + public storePlayerDetails(name: string, characterUserSelected : string): void { this.playerName = name; this.characterUserSelected = characterUserSelected; - this.ConnectionInstance = new Connection(this); - return this.ConnectionInstance.createConnection(name, characterUserSelected).then((data : ConnectionInterface) => { - this.SimplePeer = new SimplePeer(this.ConnectionInstance); - return data; - }).catch((err) => { - throw err; - }); } - loadStartMap(){ - return this.ConnectionInstance.loadStartMap().then((data) => { - return data; - }).catch((err) => { - throw err; - }); - } - - setCurrentGameScene(gameScene: GameScene) { - this.currentGameScene = gameScene; - } - - - /** - * Permit to create player in started room - */ - /*createCurrentPlayer(): void { - //Get started room send by the backend - this.currentGameScene.createCurrentPlayer(); - //this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; - }*/ - - joinRoom(sceneKey: string, startX: number, startY: number, direction: string, moving: boolean){ - this.ConnectionInstance.joinARoom(sceneKey, startX, startY, direction, moving); - } - - onUserJoins(message: MessageUserJoined): void { - const userMessage: AddPlayerInterface = { - userId: message.userId, - character: message.character, - name: message.name, - position: message.position - } - this.getCurrentGameScene().addPlayer(userMessage); - } - - onUserMoved(message: MessageUserMovedInterface): void { - this.getCurrentGameScene().updatePlayerPosition(message); - } - - onUserLeft(userId: string): void { - this.getCurrentGameScene().removePlayer(userId); - } - - initUsersPosition(usersPosition: MessageUserPositionInterface[]): void { - // Shall we wait for room to be loaded? - /*if (this.status === StatusGameManagerEnum.IN_PROGRESS) { - return; - }*/ - try { - this.getCurrentGameScene().initUsersPosition(usersPosition) - } catch (e) { - console.error(e); - } - } - - /** - * Share group position in game - */ - shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface): void { - /*if (this.status === StatusGameManagerEnum.IN_PROGRESS) { - return; - }*/ - try { - this.getCurrentGameScene().shareGroupPosition(groupPositionMessage) - } catch (e) { - console.error(e); - } - } - - deleteGroup(groupId: string): void { - /*if (this.status === StatusGameManagerEnum.IN_PROGRESS) { - return; - }*/ - try { - this.getCurrentGameScene().deleteGroup(groupId) - } catch (e) { - console.error(e); - } + loadStartMap() : Promise { + return Axios.get(`${API_URL}/start-map`) + .then((res) => { + return res.data; + }).catch((err) => { + console.error(err); + throw err; + }); } getPlayerName(): string { return this.playerName; } - getPlayerId(): string|null { - return this.ConnectionInstance.userId; - } - getCharacterSelected(): string { return this.characterUserSelected; } - pushPlayerPosition(event: HasMovedEvent) { - this.ConnectionInstance.sharePosition(event.x, event.y, event.direction, event.moving); - } - loadMap(mapUrl: string, scene: Phaser.Scenes.ScenePlugin, instance: string): string { const sceneKey = GameScene.getMapKeyByUrl(mapUrl); @@ -163,64 +66,6 @@ export class GameManager { } return sceneKey; } - - private oldSceneKey : string; - private oldMapUrlFile : string; - private oldInstance : string; - private scenePlugin: ScenePlugin; - private reconnectScene: Scene|null = null; - switchToDisconnectedScene(): void { - if (this.currentGameScene === null) { - return; - } - console.log('Switching to disconnected scene'); - this.oldSceneKey = this.currentGameScene.scene.key; - this.oldMapUrlFile = this.currentGameScene.MapUrlFile; - this.oldInstance = this.currentGameScene.instance; - this.currentGameScene.scene.start(ReconnectingSceneName); - this.reconnectScene = this.currentGameScene.scene.get(ReconnectingSceneName); - // Let's completely delete an purge the disconnected scene. We will start again from 0. - this.currentGameScene.scene.remove(this.oldSceneKey); - this.scenePlugin = this.currentGameScene.scene; - this.currentGameScene = null; - } - - private timeoutCallback: NodeJS.Timeout|null = null; - reconnectToGameScene(lastPositionShared: PointInterface): void { - if (this.timeoutCallback !== null) { - console.log('Reconnect called but setTimeout in progress for the reconnection'); - return; - } - if (this.reconnectScene === null) { - console.log('Reconnect called without switchToDisconnectedScene called first'); - - if (!this.currentGameScene) { - console.error('Reconnect called but we are not on a GameScene'); - return; - } - - // In case we are asked to reconnect even if switchToDisconnectedScene was not triggered (can happen when a laptop goes to sleep) - this.switchToDisconnectedScene(); - // Wait a bit for scene to load. Otherwise, starting ReconnectingSceneName and then starting GameScene one after the other fails for some reason. - this.timeoutCallback = setTimeout(() => { - console.log('Reconnecting to game scene from setTimeout'); - this.timeoutCallback = null; - this.reconnectToGameScene(lastPositionShared); - }, 500); - return; - } - console.log('Reconnecting to game scene'); - const game : Phaser.Scene = GameScene.createFromUrl(this.oldMapUrlFile, this.oldInstance); - this.reconnectScene.scene.add(this.oldSceneKey, game, true, { initPosition: lastPositionShared }); - this.reconnectScene = null; - } - - private getCurrentGameScene(): GameScene { - if (this.currentGameScene === null) { - throw new Error('No current game scene enabled'); - } - return this.currentGameScene; - } } export const gameManager = new GameManager(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index c6647cd9..67e953f2 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,6 +1,7 @@ import {GameManager, gameManager, HasMovedEvent} from "./GameManager"; import { - GroupCreatedUpdatedMessageInterface, + Connection, + GroupCreatedUpdatedMessageInterface, MessageUserJoined, MessageUserMovedInterface, MessageUserPositionInterface, PointInterface, PositionInterface } from "../../Connection"; @@ -23,6 +24,8 @@ import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; import {RemotePlayer} from "../Entity/RemotePlayer"; import GameObject = Phaser.GameObjects.GameObject; import { Queue } from 'queue-typescript'; +import {SimplePeer} from "../../WebRtc/SimplePeer"; +import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; export enum Textures { @@ -81,6 +84,9 @@ export class GameScene extends Phaser.Scene { pendingEvents: Queue = new Queue(); private initPosition: PositionInterface|null = null; private playersPositionInterpolator = new PlayersPositionInterpolator(); + private connection: Connection; + private simplePeer : SimplePeer; + private connectionPromise: Promise MapKey: string; MapUrlFile: string; @@ -99,14 +105,17 @@ export class GameScene extends Phaser.Scene { private PositionNextScene: Array> = new Array>(); private startLayerName: string|undefined; - static createFromUrl(mapUrlFile: string, instance: string): GameScene { - const key = GameScene.getMapKeyByUrl(mapUrlFile); - return new GameScene(key, mapUrlFile, instance); + static createFromUrl(mapUrlFile: string, instance: string, key: string|null = null): GameScene { + const mapKey = GameScene.getMapKeyByUrl(mapUrlFile); + if (key === null) { + key = mapKey; + } + return new GameScene(mapKey, mapUrlFile, instance, key); } - constructor(MapKey : string, MapUrlFile: string, instance: string) { + constructor(MapKey : string, MapUrlFile: string, instance: string, key: string) { super({ - key: MapKey + key: key }); this.GameManager = gameManager; @@ -116,12 +125,11 @@ export class GameScene extends Phaser.Scene { this.MapKey = MapKey; this.MapUrlFile = MapUrlFile; - this.RoomId = this.instance + '__' + this.MapKey; + this.RoomId = this.instance + '__' + MapKey; } //hook preload scene preload(): void { - this.GameManager.setCurrentGameScene(this); this.load.on('filecomplete-tilemapJSON-'+this.MapKey, (key: string, type: string, data: unknown) => { this.onMapLoad(data); }); @@ -144,6 +152,67 @@ export class GameScene extends Phaser.Scene { }); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + + this.connectionPromise = Connection.createConnection(gameManager.getPlayerName(), gameManager.getCharacterSelected()).then((connection : Connection) => { + this.connection = connection; + + connection.onUserJoins((message: MessageUserJoined) => { + const userMessage: AddPlayerInterface = { + userId: message.userId, + character: message.character, + name: message.name, + position: message.position + } + this.addPlayer(userMessage); + }); + + connection.onUserMoved((message: MessageUserMovedInterface) => { + this.updatePlayerPosition(message); + }); + + connection.onUserLeft((userId: string) => { + this.removePlayer(userId); + }); + + connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { + this.shareGroupPosition(groupPositionMessage); + }) + + connection.onGroupDeleted((groupId: string) => { + try { + this.deleteGroup(groupId); + } catch (e) { + console.error(e); + } + }) + + connection.onServerDisconnected(() => { + console.log('Player disconnected from server. Reloading scene.'); + + this.simplePeer.closeAllConnections(); + + const key = 'somekey'+Math.round(Math.random()*10000); + const game : Phaser.Scene = GameScene.createFromUrl(this.MapUrlFile, this.instance, key); + this.scene.add(key, game, true, + { + initPosition: { + x: this.CurrentPlayer.x, + y: this.CurrentPlayer.y + } + }); + + this.scene.stop(this.scene.key); + this.scene.remove(this.scene.key); + }) + + // When connection is performed, let's connect SimplePeer + this.simplePeer = new SimplePeer(this.connection); + + this.scene.wake(); + this.scene.sleep(ReconnectingSceneName); + + return connection; + }); } // FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving. @@ -272,6 +341,17 @@ export class GameScene extends Phaser.Scene { path += '#'+this.startLayerName; } window.history.pushState({}, 'WorkAdventure', path); + + // Let's pause the scene if the connection is not established yet + if (this.connection === undefined) { + // Let's wait 0.5 seconds before printing the "connecting" screen to avoid blinking + setTimeout(() => { + if (this.connection === undefined) { + this.scene.sleep(); + this.scene.launch(ReconnectingSceneName); + } + }, 500); + } } private getExitSceneUrl(layer: ITiledMapLayer): string|undefined { @@ -430,10 +510,14 @@ export class GameScene extends Phaser.Scene { this.createCollisionObject(); //join room - this.GameManager.joinRoom(this.RoomId, this.startX, this.startY, PlayerAnimationNames.WalkDown, false); + this.connectionPromise.then((connection: Connection) => { + connection.joinARoom(this.RoomId, this.startX, this.startY, PlayerAnimationNames.WalkDown, false).then((userPositions: MessageUserPositionInterface[]) => { + this.initUsersPosition(userPositions); + }); - //listen event to share position of user - this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) + //listen event to share position of user + this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) + }); } pushPlayerPosition(event: HasMovedEvent) { @@ -465,7 +549,7 @@ export class GameScene extends Phaser.Scene { private doPushPlayerPosition(event: HasMovedEvent): void { this.lastMoveEventSent = event; this.lastSentTick = this.currentTick; - this.GameManager.pushPlayerPosition(event); + this.connection.sharePosition(event.x, event.y, event.direction, event.moving); } EventToClickOnTile(){ @@ -525,6 +609,8 @@ export class GameScene extends Phaser.Scene { const nextSceneKey = this.checkToExit(); if(nextSceneKey){ // We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map. + this.connection.closeConnection(); + this.scene.stop(); this.scene.remove(this.scene.key); this.scene.start(nextSceneKey.key, { startLayerName: nextSceneKey.hash @@ -549,7 +635,7 @@ export class GameScene extends Phaser.Scene { /** * Called by the connexion when the full list of user position is received. */ - public initUsersPosition(usersPosition: MessageUserPositionInterface[]): void { + private initUsersPosition(usersPosition: MessageUserPositionInterface[]): void { this.pendingEvents.enqueue({ type: "InitUserPositionEvent", event: usersPosition @@ -561,7 +647,7 @@ export class GameScene extends Phaser.Scene { * Put all the players on the map on map load. */ private doInitUsersPosition(usersPosition: MessageUserPositionInterface[]): void { - const currentPlayerId = this.GameManager.getPlayerId(); + const currentPlayerId = this.connection.userId; // clean map this.MapPlayersByKey.forEach((player: RemotePlayer) => { @@ -633,7 +719,6 @@ export class GameScene extends Phaser.Scene { } private doRemovePlayer(userId: string) { - //console.log('Removing player ', userId) const player = this.MapPlayersByKey.get(userId); if (player === undefined) { console.error('Cannot find user with id ', userId); diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 316ee897..5175a7b8 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -117,34 +117,31 @@ export class SelectCharacterScene extends Phaser.Scene { } private async login(name: string): Promise { - return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => { - // Do we have a start URL in the address bar? If so, let's redirect to this address - const instanceAndMapUrl = this.findMapUrl(); - if (instanceAndMapUrl !== null) { - const [mapUrl, instance] = instanceAndMapUrl; - const key = gameManager.loadMap(mapUrl, this.scene, instance); - this.scene.start(key, { - startLayerName: window.location.hash ? window.location.hash.substr(1) : undefined - } as GameSceneInitInterface); - return { - mapUrlStart: mapUrl, - startInstance: instance - }; - } else { - // If we do not have a map address in the URL, let's ask the server for a start map. - return gameManager.loadStartMap().then((startMap: StartMapInterface) => { - const key = gameManager.loadMap(window.location.protocol + "//" + startMap.mapUrlStart, this.scene, startMap.startInstance); - this.scene.start(key); - return startMap; - }).catch((err) => { - console.error(err); - throw err; - }); - } - }).catch((err) => { - console.error(err); - throw err; - }); + gameManager.storePlayerDetails(name, this.selectedPlayer.texture.key); + + // Do we have a start URL in the address bar? If so, let's redirect to this address + const instanceAndMapUrl = this.findMapUrl(); + if (instanceAndMapUrl !== null) { + const [mapUrl, instance] = instanceAndMapUrl; + const key = gameManager.loadMap(mapUrl, this.scene, instance); + this.scene.start(key, { + startLayerName: window.location.hash ? window.location.hash.substr(1) : undefined + } as GameSceneInitInterface); + return { + mapUrlStart: mapUrl, + startInstance: instance + }; + } else { + // If we do not have a map address in the URL, let's ask the server for a start map. + return gameManager.loadStartMap().then((startMap: StartMapInterface) => { + const key = gameManager.loadMap(window.location.protocol + "//" + startMap.mapUrlStart, this.scene, startMap.startInstance); + this.scene.start(key); + return startMap; + }).catch((err) => { + console.error(err); + throw err; + }); + } } /** diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index f5c8e7ef..381b3ac2 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -1,5 +1,5 @@ import { - ConnectionInterface, + Connection, WebRtcDisconnectMessageInterface, WebRtcSignalMessageInterface, WebRtcStartMessageInterface @@ -18,7 +18,7 @@ export interface UserSimplePeer{ * This class manages connections to all the peers in the same group as me. */ export class SimplePeer { - private Connection: ConnectionInterface; + private Connection: Connection; private WebRtcRoomId: string; private Users: Array = new Array(); @@ -26,7 +26,7 @@ export class SimplePeer { private PeerConnectionArray: Map = new Map(); - constructor(Connection: ConnectionInterface, WebRtcRoomId: string = "test-webrtc") { + constructor(Connection: Connection, WebRtcRoomId: string = "test-webrtc") { this.Connection = Connection; this.WebRtcRoomId = WebRtcRoomId; this.MediaManager = new MediaManager((stream : MediaStream) => { @@ -209,6 +209,12 @@ export class SimplePeer { } } + public closeAllConnections() { + for (const userId of this.PeerConnectionArray.keys()) { + this.closeConnection(userId); + } + } + /** * * @param userId diff --git a/front/tsconfig.json b/front/tsconfig.json index 84882e74..1661efa2 100644 --- a/front/tsconfig.json +++ b/front/tsconfig.json @@ -6,6 +6,7 @@ "noImplicitAny": true, "module": "CommonJS", "target": "es5", + "downlevelIteration": true, "jsx": "react", "allowJs": true,