2020-09-25 18:29:22 +02:00
|
|
|
import Axios from "axios";
|
2021-07-16 11:22:36 +02:00
|
|
|
import { PUSHER_URL, START_ROOM_URL } from "../Enum/EnvironmentVariable";
|
|
|
|
import { RoomConnection } from "./RoomConnection";
|
|
|
|
import type { OnConnectInterface, PositionInterface, ViewportInterface } from "./ConnexionModels";
|
|
|
|
import { GameConnexionTypes, urlManager } from "../Url/UrlManager";
|
|
|
|
import { localUserStore } from "./LocalUserStore";
|
|
|
|
import { CharacterTexture, LocalUser } from "./LocalUser";
|
|
|
|
import { Room } from "./Room";
|
2021-07-29 16:42:31 +02:00
|
|
|
import { _ServiceWorker } from "../Network/ServiceWorker";
|
2020-09-28 15:02:37 +02:00
|
|
|
|
2020-09-25 18:29:22 +02:00
|
|
|
class ConnectionManager {
|
2021-07-16 11:22:36 +02:00
|
|
|
private localUser!: LocalUser;
|
2020-09-29 17:12:28 +02:00
|
|
|
|
2021-07-16 11:22:36 +02:00
|
|
|
private connexionType?: GameConnexionTypes;
|
|
|
|
private reconnectingTimeout: NodeJS.Timeout | null = null;
|
|
|
|
private _unloading: boolean = false;
|
2021-07-27 16:37:01 +02:00
|
|
|
private authToken: string | null = null;
|
2021-03-30 16:08:49 +02:00
|
|
|
|
2021-07-29 16:42:31 +02:00
|
|
|
private serviceWorker?: _ServiceWorker;
|
2021-03-30 16:08:49 +02:00
|
|
|
|
2021-07-16 11:22:36 +02:00
|
|
|
get unloading() {
|
2021-03-26 14:12:22 +01:00
|
|
|
return this._unloading;
|
|
|
|
}
|
2021-03-30 16:08:49 +02:00
|
|
|
|
2021-03-26 14:12:22 +01:00
|
|
|
constructor() {
|
2021-07-16 11:22:36 +02:00
|
|
|
window.addEventListener("beforeunload", () => {
|
2021-03-26 14:12:22 +01:00
|
|
|
this._unloading = true;
|
2021-07-16 11:22:36 +02:00
|
|
|
if (this.reconnectingTimeout) clearTimeout(this.reconnectingTimeout);
|
|
|
|
});
|
2021-03-26 14:12:22 +01:00
|
|
|
}
|
2021-07-27 16:37:01 +02:00
|
|
|
|
|
|
|
public loadOpenIDScreen() {
|
|
|
|
localUserStore.setAuthToken(null);
|
|
|
|
const state = localUserStore.generateState();
|
|
|
|
const nonce = localUserStore.generateNonce();
|
2021-08-08 15:47:53 +02:00
|
|
|
|
|
|
|
let loginUrl = `${PUSHER_URL}/login-screen?state=${state}&nonce=${nonce}`
|
|
|
|
|
|
|
|
if (loginUrl.startsWith("/")) {
|
|
|
|
loginUrl = window.location.protocol +
|
|
|
|
"//" +
|
|
|
|
window.location.host +
|
|
|
|
loginUrl;
|
|
|
|
} else {
|
|
|
|
loginUrl = `http://` + loginUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
window.location.assign(loginUrl);
|
2021-07-27 16:37:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public logout() {
|
|
|
|
localUserStore.setAuthToken(null);
|
|
|
|
window.location.reload();
|
|
|
|
}
|
|
|
|
|
2020-10-12 16:23:07 +02:00
|
|
|
/**
|
|
|
|
* Tries to login to the node server and return the starting map url to be loaded
|
|
|
|
*/
|
|
|
|
public async initGameConnexion(): Promise<Room> {
|
|
|
|
const connexionType = urlManager.getGameConnexionType();
|
2020-11-22 12:40:03 +01:00
|
|
|
this.connexionType = connexionType;
|
2021-07-29 16:42:31 +02:00
|
|
|
let room: Room | null = null;
|
2021-07-27 16:37:01 +02:00
|
|
|
if (connexionType === GameConnexionTypes.jwt) {
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
const code = urlParams.get("code");
|
|
|
|
const state = urlParams.get("state");
|
|
|
|
if (!state || !localUserStore.verifyState(state)) {
|
|
|
|
throw "Could not validate state!";
|
|
|
|
}
|
|
|
|
if (!code) {
|
|
|
|
throw "No Auth code provided";
|
|
|
|
}
|
|
|
|
const nonce = localUserStore.getNonce();
|
|
|
|
const { authToken } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce } }).then(
|
|
|
|
(res) => res.data
|
|
|
|
);
|
|
|
|
localUserStore.setAuthToken(authToken);
|
|
|
|
this.authToken = authToken;
|
2021-08-10 10:25:24 +02:00
|
|
|
|
|
|
|
let roomPath: string;
|
|
|
|
|
|
|
|
roomPath = window.location.protocol + "//" + window.location.host + START_ROOM_URL;
|
|
|
|
|
|
|
|
const lastRoomUrl = localUserStore.getLastRoomUrl();
|
|
|
|
if (lastRoomUrl != undefined && lastRoomUrl != "") {
|
|
|
|
roomPath = lastRoomUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
room = await Room.createRoom(new URL(roomPath));
|
2021-07-27 16:37:01 +02:00
|
|
|
urlManager.pushRoomIdToUrl(room);
|
|
|
|
} else if (connexionType === GameConnexionTypes.register) {
|
|
|
|
//@deprecated
|
2021-07-16 11:22:36 +02:00
|
|
|
const organizationMemberToken = urlManager.getOrganizationToken();
|
|
|
|
const data = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then(
|
|
|
|
(res) => res.data
|
|
|
|
);
|
2021-07-27 16:37:01 +02:00
|
|
|
this.localUser = new LocalUser(data.userUuid, data.textures);
|
|
|
|
this.authToken = data.authToken;
|
2020-10-12 16:23:07 +02:00
|
|
|
localUserStore.saveUser(this.localUser);
|
2021-07-27 16:37:01 +02:00
|
|
|
localUserStore.setAuthToken(this.authToken);
|
2020-10-13 16:46:46 +02:00
|
|
|
|
2021-07-13 19:09:07 +02:00
|
|
|
const roomUrl = data.roomUrl;
|
2020-10-13 16:46:46 +02:00
|
|
|
|
2021-07-29 16:42:31 +02:00
|
|
|
room = await Room.createRoom(
|
2021-07-16 11:22:36 +02:00
|
|
|
new URL(
|
|
|
|
window.location.protocol +
|
|
|
|
"//" +
|
|
|
|
window.location.host +
|
|
|
|
roomUrl +
|
|
|
|
window.location.search +
|
|
|
|
window.location.hash
|
|
|
|
)
|
|
|
|
);
|
2021-01-21 09:40:11 +01:00
|
|
|
urlManager.pushRoomIdToUrl(room);
|
2021-07-16 11:22:36 +02:00
|
|
|
} else if (
|
|
|
|
connexionType === GameConnexionTypes.organization ||
|
|
|
|
connexionType === GameConnexionTypes.anonymous ||
|
|
|
|
connexionType === GameConnexionTypes.empty
|
|
|
|
) {
|
2021-07-27 16:37:01 +02:00
|
|
|
this.authToken = localUserStore.getAuthToken();
|
|
|
|
//todo: add here some kind of warning if authToken has expired.
|
|
|
|
if (!this.authToken) {
|
2021-08-10 10:25:24 +02:00
|
|
|
await this.anonymousLogin();
|
|
|
|
// this.loadOpenIDScreen();
|
2020-10-12 16:23:07 +02:00
|
|
|
}
|
2021-07-27 16:37:01 +02:00
|
|
|
this.localUser = localUserStore.getLocalUser() as LocalUser; //if authToken exist in localStorage then localUser cannot be null
|
2021-06-03 13:07:52 +02:00
|
|
|
|
2021-07-13 19:09:07 +02:00
|
|
|
let roomPath: string;
|
2020-10-13 17:20:20 +02:00
|
|
|
if (connexionType === GameConnexionTypes.empty) {
|
2021-07-16 11:22:36 +02:00
|
|
|
roomPath = window.location.protocol + "//" + window.location.host + START_ROOM_URL;
|
2021-08-04 13:33:58 +02:00
|
|
|
//get last room path from cache api
|
|
|
|
try {
|
|
|
|
const lastRoomUrl = await localUserStore.getLastRoomUrlCacheApi();
|
|
|
|
if (lastRoomUrl != undefined) {
|
|
|
|
roomPath = lastRoomUrl;
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
console.error(err);
|
|
|
|
}
|
2020-10-13 17:20:20 +02:00
|
|
|
} else {
|
2021-07-16 11:22:36 +02:00
|
|
|
roomPath =
|
|
|
|
window.location.protocol +
|
|
|
|
"//" +
|
|
|
|
window.location.host +
|
|
|
|
window.location.pathname +
|
|
|
|
window.location.search +
|
|
|
|
window.location.hash;
|
2020-10-13 17:20:20 +02:00
|
|
|
}
|
2021-06-03 13:07:52 +02:00
|
|
|
|
|
|
|
//get detail map for anonymous login and set texture in local storage
|
2021-07-29 16:42:31 +02:00
|
|
|
room = await Room.createRoom(new URL(roomPath));
|
2021-07-16 11:22:36 +02:00
|
|
|
if (room.textures != undefined && room.textures.length > 0) {
|
2021-06-03 13:07:52 +02:00
|
|
|
//check if texture was changed
|
2021-07-27 16:37:01 +02:00
|
|
|
if (this.localUser.textures.length === 0) {
|
|
|
|
this.localUser.textures = room.textures;
|
2021-07-16 11:22:36 +02:00
|
|
|
} else {
|
2021-07-13 19:09:07 +02:00
|
|
|
room.textures.forEach((newTexture) => {
|
2021-07-27 16:37:01 +02:00
|
|
|
const alreadyExistTexture = this.localUser.textures.find((c) => newTexture.id === c.id);
|
|
|
|
if (this.localUser.textures.findIndex((c) => newTexture.id === c.id) !== -1) {
|
2021-06-03 13:07:52 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-07-27 16:37:01 +02:00
|
|
|
this.localUser.textures.push(newTexture);
|
2021-06-03 13:07:52 +02:00
|
|
|
});
|
|
|
|
}
|
2021-07-27 16:37:01 +02:00
|
|
|
localUserStore.saveUser(this.localUser);
|
2021-06-03 13:07:52 +02:00
|
|
|
}
|
2021-07-29 16:42:31 +02:00
|
|
|
}
|
|
|
|
if (room == undefined) {
|
|
|
|
return Promise.reject(new Error("Invalid URL"));
|
2021-08-02 22:06:24 +02:00
|
|
|
}
|
2020-10-13 17:20:20 +02:00
|
|
|
|
2021-07-29 16:42:31 +02:00
|
|
|
this.serviceWorker = new _ServiceWorker();
|
|
|
|
return Promise.resolve(room);
|
2020-10-15 16:48:42 +02:00
|
|
|
}
|
|
|
|
|
2020-10-21 14:55:18 +02:00
|
|
|
public async anonymousLogin(isBenchmark: boolean = false): Promise<void> {
|
2021-07-16 11:22:36 +02:00
|
|
|
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data);
|
2021-07-27 16:37:01 +02:00
|
|
|
this.localUser = new LocalUser(data.userUuid, []);
|
|
|
|
this.authToken = data.authToken;
|
2021-07-16 11:22:36 +02:00
|
|
|
if (!isBenchmark) {
|
|
|
|
// In benchmark, we don't have a local storage.
|
2020-10-21 14:55:18 +02:00
|
|
|
localUserStore.saveUser(this.localUser);
|
2021-07-27 16:37:01 +02:00
|
|
|
localUserStore.setAuthToken(this.authToken);
|
2020-10-21 14:55:18 +02:00
|
|
|
}
|
2020-10-15 16:48:42 +02:00
|
|
|
}
|
|
|
|
|
2020-09-30 12:12:24 +02:00
|
|
|
public initBenchmark(): void {
|
2021-07-27 16:37:01 +02:00
|
|
|
this.localUser = new LocalUser("", []);
|
2020-09-30 12:12:24 +02:00
|
|
|
}
|
|
|
|
|
2021-07-16 11:22:36 +02:00
|
|
|
public connectToRoomSocket(
|
|
|
|
roomUrl: string,
|
|
|
|
name: string,
|
|
|
|
characterLayers: string[],
|
|
|
|
position: PositionInterface,
|
|
|
|
viewport: ViewportInterface,
|
|
|
|
companion: string | null
|
|
|
|
): Promise<OnConnectInterface> {
|
2020-12-03 16:39:44 +01:00
|
|
|
return new Promise<OnConnectInterface>((resolve, reject) => {
|
2021-07-16 11:22:36 +02:00
|
|
|
const connection = new RoomConnection(
|
2021-07-27 16:37:01 +02:00
|
|
|
this.authToken,
|
2021-07-16 11:22:36 +02:00
|
|
|
roomUrl,
|
|
|
|
name,
|
|
|
|
characterLayers,
|
|
|
|
position,
|
|
|
|
viewport,
|
|
|
|
companion
|
|
|
|
);
|
2021-07-29 16:42:31 +02:00
|
|
|
|
2020-09-28 15:02:37 +02:00
|
|
|
connection.onConnectError((error: object) => {
|
2021-07-16 11:22:36 +02:00
|
|
|
console.log("An error occurred while connecting to socket server. Retrying");
|
2020-09-28 15:02:37 +02:00
|
|
|
reject(error);
|
2020-09-25 18:29:22 +02:00
|
|
|
});
|
2020-11-13 18:00:22 +01:00
|
|
|
|
2020-12-03 16:39:44 +01:00
|
|
|
connection.onConnectingError((event: CloseEvent) => {
|
2021-07-16 11:22:36 +02:00
|
|
|
console.log("An error occurred while connecting to socket server. Retrying");
|
|
|
|
reject(
|
|
|
|
new Error(
|
|
|
|
"An error occurred while connecting to socket server. Retrying. Code: " +
|
|
|
|
event.code +
|
|
|
|
", Reason: " +
|
|
|
|
event.reason
|
|
|
|
)
|
|
|
|
);
|
2020-12-03 16:39:44 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
connection.onConnect((connect: OnConnectInterface) => {
|
2021-07-29 16:42:31 +02:00
|
|
|
//save last room url connected
|
|
|
|
localUserStore.setLastRoomUrl(roomUrl);
|
|
|
|
|
2020-12-03 16:39:44 +01:00
|
|
|
resolve(connect);
|
|
|
|
});
|
2020-09-28 15:02:37 +02:00
|
|
|
}).catch((err) => {
|
|
|
|
// Let's retry in 4-6 seconds
|
2020-12-03 16:39:44 +01:00
|
|
|
return new Promise<OnConnectInterface>((resolve, reject) => {
|
2021-03-26 14:12:22 +01:00
|
|
|
this.reconnectingTimeout = setTimeout(() => {
|
2020-12-03 16:39:44 +01:00
|
|
|
//todo: allow a way to break recursion?
|
|
|
|
//todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely.
|
2021-07-16 11:22:36 +02:00
|
|
|
this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then(
|
|
|
|
(connection) => resolve(connection)
|
|
|
|
);
|
|
|
|
}, 4000 + Math.floor(Math.random() * 2000));
|
2020-09-28 15:02:37 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2020-11-22 12:40:03 +01:00
|
|
|
|
2021-07-16 11:22:36 +02:00
|
|
|
get getConnexionType() {
|
2020-11-22 12:40:03 +01:00
|
|
|
return this.connexionType;
|
|
|
|
}
|
2020-09-25 18:29:22 +02:00
|
|
|
}
|
|
|
|
|
2020-09-29 17:12:28 +02:00
|
|
|
export const connectionManager = new ConnectionManager();
|