Merge pull request #198 from thecodingmachine/connectioninscene
Refactoring connection to be part of a GameScene
This commit is contained in:
commit
aa2daac63e
@ -1,4 +1,4 @@
|
|||||||
import {GameManager} from "./Phaser/Game/GameManager";
|
import {gameManager, GameManager} from "./Phaser/Game/GameManager";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import {API_URL} from "./Enum/EnvironmentVariable";
|
import {API_URL} from "./Enum/EnvironmentVariable";
|
||||||
import {MessageUI} from "./Logger/MessageUI";
|
import {MessageUI} from "./Logger/MessageUI";
|
||||||
@ -118,37 +118,13 @@ export interface WebRtcSignalMessageInterface {
|
|||||||
signal: SignalData
|
signal: SignalData
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConnectionInterface {
|
|
||||||
socket: Socket|null;
|
|
||||||
token: string|null;
|
|
||||||
name: string|null;
|
|
||||||
userId: string|null;
|
|
||||||
|
|
||||||
createConnection(name: string, characterSelected: string): Promise<ConnectionInterface>;
|
|
||||||
|
|
||||||
loadStartMap(): Promise<StartMapInterface>;
|
|
||||||
|
|
||||||
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 {
|
export interface StartMapInterface {
|
||||||
mapUrlStart: string,
|
mapUrlStart: string,
|
||||||
startInstance: string
|
startInstance: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Connection implements ConnectionInterface {
|
export class Connection implements Connection {
|
||||||
socket: Socket|null = null;
|
socket: Socket;
|
||||||
token: string|null = null;
|
token: string|null = null;
|
||||||
name: string|null = null; // TODO: drop "name" storage here
|
name: string|null = null; // TODO: drop "name" storage here
|
||||||
character: string|null = null;
|
character: string|null = null;
|
||||||
@ -159,102 +135,78 @@ export class Connection implements ConnectionInterface {
|
|||||||
lastPositionShared: PointInterface|null = null;
|
lastPositionShared: PointInterface|null = null;
|
||||||
lastRoom: string|null = null;
|
lastRoom: string|null = null;
|
||||||
|
|
||||||
constructor(GameManager: GameManager) {
|
private constructor(GameManager: GameManager, name: string, character: string, token: string) {
|
||||||
this.GameManager = GameManager;
|
this.GameManager = GameManager;
|
||||||
}
|
|
||||||
|
|
||||||
createConnection(name: string, characterSelected: string): Promise<ConnectionInterface> {
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.character = characterSelected;
|
this.character = character;
|
||||||
return Axios.post(`${API_URL}/login`, {name: name})
|
this.token = token;
|
||||||
.then((res) => {
|
|
||||||
this.token = res.data.token;
|
|
||||||
this.socket = SocketIo(`${API_URL}`, {
|
this.socket = SocketIo(`${API_URL}`, {
|
||||||
query: {
|
query: {
|
||||||
token: this.token
|
token: this.token
|
||||||
}
|
},
|
||||||
|
reconnection: false // Reconnection is handled by the application itself
|
||||||
});
|
});
|
||||||
|
|
||||||
//listen event
|
this.socket.on(EventMessage.MESSAGE_ERROR, (message: string) => {
|
||||||
this.disconnectServer();
|
console.error(EventMessage.MESSAGE_ERROR, message);
|
||||||
this.errorMessage();
|
})
|
||||||
this.groupUpdatedOrCreated();
|
}
|
||||||
this.groupDeleted();
|
|
||||||
this.onUserJoins();
|
|
||||||
this.onUserMoved();
|
|
||||||
this.onUserLeft();
|
|
||||||
|
|
||||||
return this.connectSocketServer();
|
public static createConnection(name: string, characterSelected: string): Promise<Connection> {
|
||||||
|
return Axios.post(`${API_URL}/login`, {name: name})
|
||||||
|
.then((res) => {
|
||||||
|
|
||||||
|
return new Promise<Connection>((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);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err);
|
// Let's retry in 4-6 seconds
|
||||||
throw err;
|
return new Promise<Connection>((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 {
|
public closeConnection(): void {
|
||||||
if (this.socket === null) {
|
this.socket?.close();
|
||||||
throw new Error('Socket not initialized while using Connection')
|
this.lastPositionShared = null;
|
||||||
}
|
this.lastRoom = null;
|
||||||
return this.socket;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param character
|
|
||||||
*/
|
|
||||||
connectSocketServer(): Promise<ConnectionInterface>{
|
|
||||||
return new Promise<ConnectionInterface>((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
|
joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean): Promise<MessageUserPositionInterface[]> {
|
||||||
/*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<StartMapInterface> {
|
|
||||||
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 {
|
|
||||||
const point = new Point(startX, startY, direction, moving);
|
const point = new Point(startX, startY, direction, moving);
|
||||||
this.lastPositionShared = point;
|
this.lastPositionShared = point;
|
||||||
this.getSocket().emit(EventMessage.JOIN_ROOM, { roomId, position: {x: startX, y: startY, direction, moving }}, (userPositions: MessageUserPositionInterface[]) => {
|
const promise = new Promise<MessageUserPositionInterface[]>((resolve, reject) => {
|
||||||
this.GameManager.initUsersPosition(userPositions);
|
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;
|
this.lastRoom = roomId;
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
sharePosition(x : number, y : number, direction : string, moving: boolean) : void{
|
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);
|
const point = new Point(x, y, direction, moving);
|
||||||
this.lastPositionShared = point;
|
this.lastPositionShared = point;
|
||||||
this.getSocket().emit(EventMessage.USER_POSITION, point);
|
this.socket.emit(EventMessage.USER_POSITION, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onUserJoins(): void {
|
public onUserJoins(callback: (message: MessageUserJoined) => void): void {
|
||||||
this.getSocket().on(EventMessage.JOIN_ROOM, (message: MessageUserJoined) => {
|
this.socket.on(EventMessage.JOIN_ROOM, callback);
|
||||||
this.GameManager.onUserJoins(message);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onUserMoved(): void {
|
public onUserMoved(callback: (message: MessageUserMovedInterface) => void): void {
|
||||||
this.getSocket().on(EventMessage.USER_MOVED, (message: MessageUserMovedInterface) => {
|
this.socket.on(EventMessage.USER_MOVED, callback);
|
||||||
this.GameManager.onUserMoved(message);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onUserLeft(): void {
|
public onUserLeft(callback: (userId: string) => void): void {
|
||||||
this.getSocket().on(EventMessage.USER_LEFT, (userId: string) => {
|
this.socket.on(EventMessage.USER_LEFT, callback);
|
||||||
this.GameManager.onUserLeft(userId);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private groupUpdatedOrCreated(): void {
|
public onGroupUpdatedOrCreated(callback: (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => void): void {
|
||||||
this.getSocket().on(EventMessage.GROUP_CREATE_UPDATE, (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => {
|
this.socket.on(EventMessage.GROUP_CREATE_UPDATE, callback);
|
||||||
//console.log('Group ', groupCreateUpdateMessage.groupId, " position :", groupCreateUpdateMessage.position.x, groupCreateUpdateMessage.position.y)
|
|
||||||
this.GameManager.shareGroupPosition(groupCreateUpdateMessage);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private groupDeleted(): void {
|
public onGroupDeleted(callback: (groupId: string) => void): void {
|
||||||
this.getSocket().on(EventMessage.GROUP_DELETE, (groupId: string) => {
|
this.socket.on(EventMessage.GROUP_DELETE, callback)
|
||||||
this.GameManager.deleteGroup(groupId);
|
}
|
||||||
})
|
|
||||||
|
public onConnectError(callback: (error: object) => void): void {
|
||||||
|
this.socket.on(EventMessage.CONNECT_ERROR, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendWebrtcSignal(signal: unknown, roomId: string, userId? : string|null, receiverId? : string) {
|
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,
|
userId: userId ? userId : this.userId,
|
||||||
receiverId: receiverId ? receiverId : this.userId,
|
receiverId: receiverId ? receiverId : this.userId,
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
@ -307,47 +252,53 @@ export class Connection implements ConnectionInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
receiveWebrtcStart(callback: (message: WebRtcStartMessageInterface) => void) {
|
receiveWebrtcStart(callback: (message: WebRtcStartMessageInterface) => void) {
|
||||||
this.getSocket().on(EventMessage.WEBRTC_START, callback);
|
this.socket.on(EventMessage.WEBRTC_START, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveWebrtcSignal(callback: (message: WebRtcSignalMessageInterface) => void) {
|
receiveWebrtcSignal(callback: (message: WebRtcSignalMessageInterface) => void) {
|
||||||
return this.getSocket().on(EventMessage.WEBRTC_SIGNAL, callback);
|
return this.socket.on(EventMessage.WEBRTC_SIGNAL, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private errorMessage(): void {
|
public onServerDisconnected(callback: (reason: string) => void): void {
|
||||||
this.getSocket().on(EventMessage.MESSAGE_ERROR, (message: string) => {
|
/*this.socket.on(EventMessage.CONNECT_ERROR, (error: object) => {
|
||||||
console.error(EventMessage.MESSAGE_ERROR, message);
|
callback(error);
|
||||||
})
|
});*/
|
||||||
}
|
|
||||||
|
|
||||||
private disconnectServer(): void {
|
this.socket.on('disconnect', (reason: string) => {
|
||||||
this.getSocket().on(EventMessage.CONNECT_ERROR, () => {
|
if (reason === 'io client disconnect') {
|
||||||
this.GameManager.switchToDisconnectedScene();
|
// 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');
|
console.log('Trying to reconnect');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getSocket().on(EventMessage.RECONNECT_ERROR, () => {
|
this.socket.on(EventMessage.RECONNECT_ERROR, () => {
|
||||||
console.log('Error while trying to reconnect.');
|
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.');
|
console.error('Reconnection failed. Giving up.');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getSocket().on(EventMessage.RECONNECT, () => {
|
this.socket.on(EventMessage.RECONNECT, () => {
|
||||||
console.log('Reconnect event triggered');
|
console.log('Reconnect event triggered');
|
||||||
this.connectSocketServer();
|
this.connectSocketServer();
|
||||||
if (this.lastPositionShared === null) {
|
if (this.lastPositionShared === null) {
|
||||||
throw new Error('No last position shared found while reconnecting');
|
throw new Error('No last position shared found while reconnecting');
|
||||||
}
|
}
|
||||||
this.GameManager.reconnectToGameScene(this.lastPositionShared);
|
this.GameManager.reconnectToGameScene(this.lastPositionShared);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void {
|
disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void {
|
||||||
this.getSocket().on(EventMessage.WEBRTC_DISCONNECT, callback);
|
this.socket.on(EventMessage.WEBRTC_DISCONNECT, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
import {GameScene} from "./GameScene";
|
import {GameScene} from "./GameScene";
|
||||||
import {
|
import {
|
||||||
Connection, ConnectionInterface,
|
Connection,
|
||||||
GroupCreatedUpdatedMessageInterface,
|
GroupCreatedUpdatedMessageInterface,
|
||||||
ListMessageUserPositionInterface,
|
ListMessageUserPositionInterface,
|
||||||
MessageUserJoined,
|
MessageUserJoined,
|
||||||
MessageUserMovedInterface,
|
MessageUserMovedInterface,
|
||||||
MessageUserPositionInterface,
|
MessageUserPositionInterface,
|
||||||
Point,
|
Point,
|
||||||
PointInterface
|
PointInterface, StartMapInterface
|
||||||
} from "../../Connection";
|
} from "../../Connection";
|
||||||
import {SimplePeer} from "../../WebRtc/SimplePeer";
|
import {SimplePeer} from "../../WebRtc/SimplePeer";
|
||||||
import {AddPlayerInterface} from "./AddPlayerInterface";
|
import {AddPlayerInterface} from "./AddPlayerInterface";
|
||||||
import {ReconnectingScene, ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
import {ReconnectingScene, ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
||||||
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
||||||
import {Scene} from "phaser";
|
import {Scene} from "phaser";
|
||||||
|
import Axios from "axios";
|
||||||
/*export enum StatusGameManagerEnum {
|
import {API_URL} from "../../Enum/EnvironmentVariable";
|
||||||
IN_PROGRESS = 1,
|
|
||||||
CURRENT_USER_CREATED = 2
|
|
||||||
}*/
|
|
||||||
|
|
||||||
export interface HasMovedEvent {
|
export interface HasMovedEvent {
|
||||||
direction: string;
|
direction: string;
|
||||||
@ -33,126 +30,32 @@ export interface MapObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class GameManager {
|
export class GameManager {
|
||||||
//status: number;
|
|
||||||
private ConnectionInstance: Connection;
|
|
||||||
private currentGameScene: GameScene|null = null;
|
|
||||||
private playerName: string;
|
private playerName: string;
|
||||||
SimplePeer : SimplePeer;
|
|
||||||
private characterUserSelected: string;
|
private characterUserSelected: string;
|
||||||
|
|
||||||
constructor() {
|
public storePlayerDetails(name: string, characterUserSelected : string): void {
|
||||||
//this.status = StatusGameManagerEnum.IN_PROGRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public connect(name: string, characterUserSelected : string): Promise<ConnectionInterface> {
|
|
||||||
this.playerName = name;
|
this.playerName = name;
|
||||||
this.characterUserSelected = characterUserSelected;
|
this.characterUserSelected = characterUserSelected;
|
||||||
this.ConnectionInstance = new Connection(this);
|
}
|
||||||
return this.ConnectionInstance.createConnection(name, characterUserSelected).then((data : ConnectionInterface) => {
|
|
||||||
this.SimplePeer = new SimplePeer(this.ConnectionInstance);
|
loadStartMap() : Promise<StartMapInterface> {
|
||||||
return data;
|
return Axios.get(`${API_URL}/start-map`)
|
||||||
|
.then((res) => {
|
||||||
|
return res.data;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
throw 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getPlayerName(): string {
|
getPlayerName(): string {
|
||||||
return this.playerName;
|
return this.playerName;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlayerId(): string|null {
|
|
||||||
return this.ConnectionInstance.userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCharacterSelected(): string {
|
getCharacterSelected(): string {
|
||||||
return this.characterUserSelected;
|
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 {
|
loadMap(mapUrl: string, scene: Phaser.Scenes.ScenePlugin, instance: string): string {
|
||||||
const sceneKey = GameScene.getMapKeyByUrl(mapUrl);
|
const sceneKey = GameScene.getMapKeyByUrl(mapUrl);
|
||||||
|
|
||||||
@ -163,64 +66,6 @@ export class GameManager {
|
|||||||
}
|
}
|
||||||
return sceneKey;
|
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();
|
export const gameManager = new GameManager();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {GameManager, gameManager, HasMovedEvent} from "./GameManager";
|
import {GameManager, gameManager, HasMovedEvent} from "./GameManager";
|
||||||
import {
|
import {
|
||||||
GroupCreatedUpdatedMessageInterface,
|
Connection,
|
||||||
|
GroupCreatedUpdatedMessageInterface, MessageUserJoined,
|
||||||
MessageUserMovedInterface,
|
MessageUserMovedInterface,
|
||||||
MessageUserPositionInterface, PointInterface, PositionInterface
|
MessageUserPositionInterface, PointInterface, PositionInterface
|
||||||
} from "../../Connection";
|
} from "../../Connection";
|
||||||
@ -23,6 +24,8 @@ import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
|
|||||||
import {RemotePlayer} from "../Entity/RemotePlayer";
|
import {RemotePlayer} from "../Entity/RemotePlayer";
|
||||||
import GameObject = Phaser.GameObjects.GameObject;
|
import GameObject = Phaser.GameObjects.GameObject;
|
||||||
import { Queue } from 'queue-typescript';
|
import { Queue } from 'queue-typescript';
|
||||||
|
import {SimplePeer} from "../../WebRtc/SimplePeer";
|
||||||
|
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
||||||
|
|
||||||
|
|
||||||
export enum Textures {
|
export enum Textures {
|
||||||
@ -81,6 +84,9 @@ export class GameScene extends Phaser.Scene {
|
|||||||
pendingEvents: Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface> = new Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface>();
|
pendingEvents: Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface> = new Queue<InitUserPositionEventInterface|AddPlayerEventInterface|RemovePlayerEventInterface|UserMovedEventInterface|GroupCreatedUpdatedEventInterface|DeleteGroupEventInterface>();
|
||||||
private initPosition: PositionInterface|null = null;
|
private initPosition: PositionInterface|null = null;
|
||||||
private playersPositionInterpolator = new PlayersPositionInterpolator();
|
private playersPositionInterpolator = new PlayersPositionInterpolator();
|
||||||
|
private connection: Connection;
|
||||||
|
private simplePeer : SimplePeer;
|
||||||
|
private connectionPromise: Promise<Connection>
|
||||||
|
|
||||||
MapKey: string;
|
MapKey: string;
|
||||||
MapUrlFile: string;
|
MapUrlFile: string;
|
||||||
@ -99,14 +105,17 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private PositionNextScene: Array<Array<{ key: string, hash: string }>> = new Array<Array<{ key: string, hash: string }>>();
|
private PositionNextScene: Array<Array<{ key: string, hash: string }>> = new Array<Array<{ key: string, hash: string }>>();
|
||||||
private startLayerName: string|undefined;
|
private startLayerName: string|undefined;
|
||||||
|
|
||||||
static createFromUrl(mapUrlFile: string, instance: string): GameScene {
|
static createFromUrl(mapUrlFile: string, instance: string, key: string|null = null): GameScene {
|
||||||
const key = GameScene.getMapKeyByUrl(mapUrlFile);
|
const mapKey = GameScene.getMapKeyByUrl(mapUrlFile);
|
||||||
return new GameScene(key, mapUrlFile, instance);
|
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({
|
super({
|
||||||
key: MapKey
|
key: key
|
||||||
});
|
});
|
||||||
|
|
||||||
this.GameManager = gameManager;
|
this.GameManager = gameManager;
|
||||||
@ -116,12 +125,11 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
this.MapKey = MapKey;
|
this.MapKey = MapKey;
|
||||||
this.MapUrlFile = MapUrlFile;
|
this.MapUrlFile = MapUrlFile;
|
||||||
this.RoomId = this.instance + '__' + this.MapKey;
|
this.RoomId = this.instance + '__' + MapKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
//hook preload scene
|
//hook preload scene
|
||||||
preload(): void {
|
preload(): void {
|
||||||
this.GameManager.setCurrentGameScene(this);
|
|
||||||
this.load.on('filecomplete-tilemapJSON-'+this.MapKey, (key: string, type: string, data: unknown) => {
|
this.load.on('filecomplete-tilemapJSON-'+this.MapKey, (key: string, type: string, data: unknown) => {
|
||||||
this.onMapLoad(data);
|
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.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.
|
// 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;
|
path += '#'+this.startLayerName;
|
||||||
}
|
}
|
||||||
window.history.pushState({}, 'WorkAdventure', path);
|
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 {
|
private getExitSceneUrl(layer: ITiledMapLayer): string|undefined {
|
||||||
@ -430,10 +510,14 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.createCollisionObject();
|
this.createCollisionObject();
|
||||||
|
|
||||||
//join room
|
//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
|
//listen event to share position of user
|
||||||
this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))
|
this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pushPlayerPosition(event: HasMovedEvent) {
|
pushPlayerPosition(event: HasMovedEvent) {
|
||||||
@ -465,7 +549,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private doPushPlayerPosition(event: HasMovedEvent): void {
|
private doPushPlayerPosition(event: HasMovedEvent): void {
|
||||||
this.lastMoveEventSent = event;
|
this.lastMoveEventSent = event;
|
||||||
this.lastSentTick = this.currentTick;
|
this.lastSentTick = this.currentTick;
|
||||||
this.GameManager.pushPlayerPosition(event);
|
this.connection.sharePosition(event.x, event.y, event.direction, event.moving);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventToClickOnTile(){
|
EventToClickOnTile(){
|
||||||
@ -525,6 +609,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
const nextSceneKey = this.checkToExit();
|
const nextSceneKey = this.checkToExit();
|
||||||
if(nextSceneKey){
|
if(nextSceneKey){
|
||||||
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
// 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.remove(this.scene.key);
|
||||||
this.scene.start(nextSceneKey.key, {
|
this.scene.start(nextSceneKey.key, {
|
||||||
startLayerName: nextSceneKey.hash
|
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.
|
* 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({
|
this.pendingEvents.enqueue({
|
||||||
type: "InitUserPositionEvent",
|
type: "InitUserPositionEvent",
|
||||||
event: usersPosition
|
event: usersPosition
|
||||||
@ -561,7 +647,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
* Put all the players on the map on map load.
|
* Put all the players on the map on map load.
|
||||||
*/
|
*/
|
||||||
private doInitUsersPosition(usersPosition: MessageUserPositionInterface[]): void {
|
private doInitUsersPosition(usersPosition: MessageUserPositionInterface[]): void {
|
||||||
const currentPlayerId = this.GameManager.getPlayerId();
|
const currentPlayerId = this.connection.userId;
|
||||||
|
|
||||||
// clean map
|
// clean map
|
||||||
this.MapPlayersByKey.forEach((player: RemotePlayer) => {
|
this.MapPlayersByKey.forEach((player: RemotePlayer) => {
|
||||||
@ -633,7 +719,6 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private doRemovePlayer(userId: string) {
|
private doRemovePlayer(userId: string) {
|
||||||
//console.log('Removing player ', userId)
|
|
||||||
const player = this.MapPlayersByKey.get(userId);
|
const player = this.MapPlayersByKey.get(userId);
|
||||||
if (player === undefined) {
|
if (player === undefined) {
|
||||||
console.error('Cannot find user with id ', userId);
|
console.error('Cannot find user with id ', userId);
|
||||||
|
@ -117,7 +117,8 @@ export class SelectCharacterScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async login(name: string): Promise<StartMapInterface> {
|
private async login(name: string): Promise<StartMapInterface> {
|
||||||
return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => {
|
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
|
// Do we have a start URL in the address bar? If so, let's redirect to this address
|
||||||
const instanceAndMapUrl = this.findMapUrl();
|
const instanceAndMapUrl = this.findMapUrl();
|
||||||
if (instanceAndMapUrl !== null) {
|
if (instanceAndMapUrl !== null) {
|
||||||
@ -141,10 +142,6 @@ export class SelectCharacterScene extends Phaser.Scene {
|
|||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
ConnectionInterface,
|
Connection,
|
||||||
WebRtcDisconnectMessageInterface,
|
WebRtcDisconnectMessageInterface,
|
||||||
WebRtcSignalMessageInterface,
|
WebRtcSignalMessageInterface,
|
||||||
WebRtcStartMessageInterface
|
WebRtcStartMessageInterface
|
||||||
@ -18,7 +18,7 @@ export interface UserSimplePeer{
|
|||||||
* This class manages connections to all the peers in the same group as me.
|
* This class manages connections to all the peers in the same group as me.
|
||||||
*/
|
*/
|
||||||
export class SimplePeer {
|
export class SimplePeer {
|
||||||
private Connection: ConnectionInterface;
|
private Connection: Connection;
|
||||||
private WebRtcRoomId: string;
|
private WebRtcRoomId: string;
|
||||||
private Users: Array<UserSimplePeer> = new Array<UserSimplePeer>();
|
private Users: Array<UserSimplePeer> = new Array<UserSimplePeer>();
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ export class SimplePeer {
|
|||||||
|
|
||||||
private PeerConnectionArray: Map<string, SimplePeerNamespace.Instance> = new Map<string, SimplePeerNamespace.Instance>();
|
private PeerConnectionArray: Map<string, SimplePeerNamespace.Instance> = new Map<string, SimplePeerNamespace.Instance>();
|
||||||
|
|
||||||
constructor(Connection: ConnectionInterface, WebRtcRoomId: string = "test-webrtc") {
|
constructor(Connection: Connection, WebRtcRoomId: string = "test-webrtc") {
|
||||||
this.Connection = Connection;
|
this.Connection = Connection;
|
||||||
this.WebRtcRoomId = WebRtcRoomId;
|
this.WebRtcRoomId = WebRtcRoomId;
|
||||||
this.MediaManager = new MediaManager((stream : MediaStream) => {
|
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
|
* @param userId
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"module": "CommonJS",
|
"module": "CommonJS",
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
|
"downlevelIteration": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user