Merge branch 'develop' of github.com:thecodingmachine/workadventure into GlobalMessageToWorld
This commit is contained in:
commit
68ff1b9e17
81 changed files with 3001 additions and 1073 deletions
|
@ -1,89 +1,107 @@
|
|||
import Axios from "axios";
|
||||
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";
|
||||
|
||||
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";
|
||||
|
||||
class ConnectionManager {
|
||||
private localUser!:LocalUser;
|
||||
private localUser!: LocalUser;
|
||||
|
||||
private connexionType?: GameConnexionTypes
|
||||
private reconnectingTimeout: NodeJS.Timeout|null = null;
|
||||
private _unloading:boolean = false;
|
||||
private connexionType?: GameConnexionTypes;
|
||||
private reconnectingTimeout: NodeJS.Timeout | null = null;
|
||||
private _unloading: boolean = false;
|
||||
|
||||
get unloading () {
|
||||
get unloading() {
|
||||
return this._unloading;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
window.addEventListener('beforeunload', () => {
|
||||
window.addEventListener("beforeunload", () => {
|
||||
this._unloading = true;
|
||||
if (this.reconnectingTimeout) clearTimeout(this.reconnectingTimeout)
|
||||
})
|
||||
if (this.reconnectingTimeout) clearTimeout(this.reconnectingTimeout);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
this.connexionType = connexionType;
|
||||
if(connexionType === GameConnexionTypes.register) {
|
||||
const organizationMemberToken = urlManager.getOrganizationToken();
|
||||
const data = await Axios.post(`${PUSHER_URL}/register`, {organizationMemberToken}).then(res => res.data);
|
||||
if (connexionType === GameConnexionTypes.register) {
|
||||
const organizationMemberToken = urlManager.getOrganizationToken();
|
||||
const data = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then(
|
||||
(res) => res.data
|
||||
);
|
||||
this.localUser = new LocalUser(data.userUuid, data.authToken, data.textures);
|
||||
localUserStore.saveUser(this.localUser);
|
||||
|
||||
const roomUrl = data.roomUrl;
|
||||
|
||||
const room = await Room.createRoom(new URL(window.location.protocol + '//' + window.location.host + roomUrl + window.location.search + window.location.hash));
|
||||
const room = await Room.createRoom(
|
||||
new URL(
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
window.location.host +
|
||||
roomUrl +
|
||||
window.location.search +
|
||||
window.location.hash
|
||||
)
|
||||
);
|
||||
urlManager.pushRoomIdToUrl(room);
|
||||
return Promise.resolve(room);
|
||||
} else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) {
|
||||
|
||||
} else if (
|
||||
connexionType === GameConnexionTypes.organization ||
|
||||
connexionType === GameConnexionTypes.anonymous ||
|
||||
connexionType === GameConnexionTypes.empty
|
||||
) {
|
||||
let localUser = localUserStore.getLocalUser();
|
||||
if (localUser && localUser.jwtToken && localUser.uuid && localUser.textures) {
|
||||
this.localUser = localUser;
|
||||
try {
|
||||
await this.verifyToken(localUser.jwtToken);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
// If the token is invalid, let's generate an anonymous one.
|
||||
console.error('JWT token invalid. Did it expire? Login anonymously instead.');
|
||||
console.error("JWT token invalid. Did it expire? Login anonymously instead.");
|
||||
await this.anonymousLogin();
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
await this.anonymousLogin();
|
||||
}
|
||||
|
||||
localUser = localUserStore.getLocalUser();
|
||||
if(!localUser){
|
||||
if (!localUser) {
|
||||
throw "Error to store local user data";
|
||||
}
|
||||
|
||||
let roomPath: string;
|
||||
if (connexionType === GameConnexionTypes.empty) {
|
||||
roomPath = window.location.protocol + '//' + window.location.host + START_ROOM_URL;
|
||||
roomPath = window.location.protocol + "//" + window.location.host + START_ROOM_URL;
|
||||
} else {
|
||||
roomPath = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search + window.location.hash;
|
||||
roomPath =
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
window.location.host +
|
||||
window.location.pathname +
|
||||
window.location.search +
|
||||
window.location.hash;
|
||||
}
|
||||
|
||||
//get detail map for anonymous login and set texture in local storage
|
||||
const room = await Room.createRoom(new URL(roomPath));
|
||||
if(room.textures != undefined && room.textures.length > 0) {
|
||||
if (room.textures != undefined && room.textures.length > 0) {
|
||||
//check if texture was changed
|
||||
if(localUser.textures.length === 0){
|
||||
if (localUser.textures.length === 0) {
|
||||
localUser.textures = room.textures;
|
||||
}else{
|
||||
} else {
|
||||
room.textures.forEach((newTexture) => {
|
||||
const alreadyExistTexture = localUser?.textures.find((c) => newTexture.id === c.id);
|
||||
if(localUser?.textures.findIndex((c) => newTexture.id === c.id) !== -1){
|
||||
if (localUser?.textures.findIndex((c) => newTexture.id === c.id) !== -1) {
|
||||
return;
|
||||
}
|
||||
localUser?.textures.push(newTexture)
|
||||
localUser?.textures.push(newTexture);
|
||||
});
|
||||
}
|
||||
this.localUser = localUser;
|
||||
|
@ -92,55 +110,79 @@ class ConnectionManager {
|
|||
return Promise.resolve(room);
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('Invalid URL'));
|
||||
return Promise.reject(new Error("Invalid URL"));
|
||||
}
|
||||
|
||||
private async verifyToken(token: string): Promise<void> {
|
||||
await Axios.get(`${PUSHER_URL}/verify`, {params: {token}});
|
||||
await Axios.get(`${PUSHER_URL}/verify`, { params: { token } });
|
||||
}
|
||||
|
||||
public async anonymousLogin(isBenchmark: boolean = false): Promise<void> {
|
||||
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then(res => res.data);
|
||||
const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data);
|
||||
this.localUser = new LocalUser(data.userUuid, data.authToken, []);
|
||||
if (!isBenchmark) { // In benchmark, we don't have a local storage.
|
||||
if (!isBenchmark) {
|
||||
// In benchmark, we don't have a local storage.
|
||||
localUserStore.saveUser(this.localUser);
|
||||
}
|
||||
}
|
||||
|
||||
public initBenchmark(): void {
|
||||
this.localUser = new LocalUser('', 'test', []);
|
||||
this.localUser = new LocalUser("", "test", []);
|
||||
}
|
||||
|
||||
public connectToRoomSocket(roomUrl: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null): Promise<OnConnectInterface> {
|
||||
public connectToRoomSocket(
|
||||
roomUrl: string,
|
||||
name: string,
|
||||
characterLayers: string[],
|
||||
position: PositionInterface,
|
||||
viewport: ViewportInterface,
|
||||
companion: string | null
|
||||
): Promise<OnConnectInterface> {
|
||||
return new Promise<OnConnectInterface>((resolve, reject) => {
|
||||
const connection = new RoomConnection(this.localUser.jwtToken, roomUrl, name, characterLayers, position, viewport, companion);
|
||||
const connection = new RoomConnection(
|
||||
this.localUser.jwtToken,
|
||||
roomUrl,
|
||||
name,
|
||||
characterLayers,
|
||||
position,
|
||||
viewport,
|
||||
companion
|
||||
);
|
||||
connection.onConnectError((error: object) => {
|
||||
console.log('An error occurred while connecting to socket server. Retrying');
|
||||
console.log("An error occurred while connecting to socket server. Retrying");
|
||||
reject(error);
|
||||
});
|
||||
|
||||
connection.onConnectingError((event: CloseEvent) => {
|
||||
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));
|
||||
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
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
connection.onConnect((connect: OnConnectInterface) => {
|
||||
resolve(connect);
|
||||
});
|
||||
|
||||
}).catch((err) => {
|
||||
// Let's retry in 4-6 seconds
|
||||
return new Promise<OnConnectInterface>((resolve, reject) => {
|
||||
this.reconnectingTimeout = setTimeout(() => {
|
||||
//todo: allow a way to break recursion?
|
||||
//todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely.
|
||||
this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then((connection) => resolve(connection));
|
||||
}, 4000 + Math.floor(Math.random() * 2000) );
|
||||
this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then(
|
||||
(connection) => resolve(connection)
|
||||
);
|
||||
}, 4000 + Math.floor(Math.random() * 2000));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get getConnexionType(){
|
||||
get getConnexionType() {
|
||||
return this.connexionType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ export enum EventMessage {
|
|||
TELEPORT = "teleport",
|
||||
USER_MESSAGE = "user-message",
|
||||
START_JITSI_ROOM = "start-jitsi-room",
|
||||
SET_VARIABLE = "set-variable",
|
||||
}
|
||||
|
||||
export interface PointInterface {
|
||||
|
@ -105,6 +106,7 @@ export interface RoomJoinedMessageInterface {
|
|||
//users: MessageUserPositionInterface[],
|
||||
//groups: GroupCreatedUpdatedMessageInterface[],
|
||||
items: { [itemId: number]: unknown };
|
||||
variables: Map<string, unknown>;
|
||||
}
|
||||
|
||||
export interface PlayGlobalMessageInterface {
|
||||
|
|
|
@ -169,6 +169,7 @@ export class Room {
|
|||
*/
|
||||
public get key(): string {
|
||||
const newUrl = new URL(this.roomUrl.toString());
|
||||
newUrl.search = "";
|
||||
newUrl.hash = "";
|
||||
return newUrl.toString();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ import {
|
|||
EmotePromptMessage,
|
||||
SendUserMessage,
|
||||
BanUserMessage,
|
||||
VariableMessage,
|
||||
ErrorMessage,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
|
||||
import type { UserSimplePeerInterface } from "../WebRtc/SimplePeer";
|
||||
|
@ -164,6 +166,12 @@ export class RoomConnection implements RoomConnection {
|
|||
} else if (subMessage.hasEmoteeventmessage()) {
|
||||
const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage;
|
||||
emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote());
|
||||
} else if (subMessage.hasErrormessage()) {
|
||||
const errorMessage = subMessage.getErrormessage() as ErrorMessage;
|
||||
console.error("An error occurred server side: " + errorMessage.getMessage());
|
||||
} else if (subMessage.hasVariablemessage()) {
|
||||
event = EventMessage.SET_VARIABLE;
|
||||
payload = subMessage.getVariablemessage();
|
||||
} else {
|
||||
throw new Error("Unexpected batch message type");
|
||||
}
|
||||
|
@ -180,6 +188,22 @@ export class RoomConnection implements RoomConnection {
|
|||
items[item.getItemid()] = JSON.parse(item.getStatejson());
|
||||
}
|
||||
|
||||
const variables = new Map<string, unknown>();
|
||||
for (const variable of roomJoinedMessage.getVariableList()) {
|
||||
try {
|
||||
variables.set(variable.getName(), JSON.parse(variable.getValue()));
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'Unable to unserialize value received from server for variable "' +
|
||||
variable.getName() +
|
||||
'". Value received: "' +
|
||||
variable.getValue() +
|
||||
'". Error: ',
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.userId = roomJoinedMessage.getCurrentuserid();
|
||||
this.tags = roomJoinedMessage.getTagList();
|
||||
|
||||
|
@ -187,6 +211,7 @@ export class RoomConnection implements RoomConnection {
|
|||
connection: this,
|
||||
room: {
|
||||
items,
|
||||
variables,
|
||||
} as RoomJoinedMessageInterface,
|
||||
});
|
||||
} else if (message.hasWorldfullmessage()) {
|
||||
|
@ -536,6 +561,17 @@ export class RoomConnection implements RoomConnection {
|
|||
this.socket.send(clientToServerMessage.serializeBinary().buffer);
|
||||
}
|
||||
|
||||
emitSetVariableEvent(name: string, value: unknown): void {
|
||||
const variableMessage = new VariableMessage();
|
||||
variableMessage.setName(name);
|
||||
variableMessage.setValue(JSON.stringify(value));
|
||||
|
||||
const clientToServerMessage = new ClientToServerMessage();
|
||||
clientToServerMessage.setVariablemessage(variableMessage);
|
||||
|
||||
this.socket.send(clientToServerMessage.serializeBinary().buffer);
|
||||
}
|
||||
|
||||
onActionableEvent(callback: (message: ItemEventMessageInterface) => void): void {
|
||||
this.onMessage(EventMessage.ITEM_EVENT, (message: ItemEventMessage) => {
|
||||
callback({
|
||||
|
@ -622,6 +658,29 @@ export class RoomConnection implements RoomConnection {
|
|||
});
|
||||
}
|
||||
|
||||
public onSetVariable(callback: (name: string, value: unknown) => void): void {
|
||||
this.onMessage(EventMessage.SET_VARIABLE, (message: VariableMessage) => {
|
||||
const name = message.getName();
|
||||
const serializedValue = message.getValue();
|
||||
let value: unknown = undefined;
|
||||
if (serializedValue) {
|
||||
try {
|
||||
value = JSON.parse(serializedValue);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'Unable to unserialize value received from server for variable "' +
|
||||
name +
|
||||
'". Value received: "' +
|
||||
serializedValue +
|
||||
'". Error: ',
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
callback(name, value);
|
||||
});
|
||||
}
|
||||
|
||||
public hasTag(tag: string): boolean {
|
||||
return this.tags.includes(tag);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue