2020-10-13 16:46:46 +02:00
import Axios from "axios" ;
2021-06-23 12:42:24 +02:00
import { PUSHER_URL } from "../Enum/EnvironmentVariable" ;
import type { CharacterTexture } from "./LocalUser" ;
2021-06-03 13:07:52 +02:00
2021-06-23 12:42:24 +02:00
export class MapDetail {
2021-07-13 19:09:07 +02:00
constructor ( public readonly mapUrl : string , public readonly textures : CharacterTexture [ ] | undefined ) { }
}
export interface RoomRedirect {
redirectUrl : string ;
2021-06-03 13:07:52 +02:00
}
2020-10-13 16:46:46 +02:00
2020-10-12 16:23:07 +02:00
export class Room {
2020-10-13 16:46:46 +02:00
public readonly id : string ;
public readonly isPublic : boolean ;
2021-07-13 19:09:07 +02:00
private _mapUrl : string | undefined ;
private _textures : CharacterTexture [ ] | undefined ;
2021-06-23 12:42:24 +02:00
private instance : string | undefined ;
2021-07-13 19:09:07 +02:00
private readonly _search : URLSearchParams ;
2020-10-13 16:46:46 +02:00
2021-07-13 19:09:07 +02:00
private constructor ( private roomUrl : URL ) {
this . id = roomUrl . pathname ;
2021-03-30 16:08:49 +02:00
2021-07-13 19:09:07 +02:00
if ( this . id . startsWith ( "/" ) ) {
2021-03-30 16:08:49 +02:00
this . id = this . id . substr ( 1 ) ;
2020-10-13 16:46:46 +02:00
}
2021-07-13 19:09:07 +02:00
if ( this . id . startsWith ( "_/" ) ) {
2020-10-13 16:46:46 +02:00
this . isPublic = true ;
2021-07-13 19:09:07 +02:00
} else if ( this . id . startsWith ( "@/" ) ) {
2020-10-13 16:46:46 +02:00
this . isPublic = false ;
} else {
2021-07-13 19:09:07 +02:00
throw new Error ( "Invalid room ID" ) ;
}
this . _search = new URLSearchParams ( roomUrl . search ) ;
}
/ * *
* Creates a "Room" object representing the room .
* This method will follow room redirects if necessary , so the instance returned is a "real" room .
* /
public static async createRoom ( roomUrl : URL ) : Promise < Room > {
let redirectCount = 0 ;
while ( redirectCount < 32 ) {
const room = new Room ( roomUrl ) ;
const result = await room . getMapDetail ( ) ;
if ( result instanceof MapDetail ) {
return room ;
}
redirectCount ++ ;
roomUrl = new URL ( result . redirectUrl ) ;
2020-10-13 16:46:46 +02:00
}
2021-07-13 19:09:07 +02:00
throw new Error ( "Room resolving seems stuck in a redirect loop after 32 redirect attempts" ) ;
}
2020-10-15 15:50:51 +02:00
2021-07-13 19:09:07 +02:00
public static getRoomPathFromExitUrl ( exitUrl : string , currentRoomUrl : string ) : URL {
const url = new URL ( exitUrl , currentRoomUrl ) ;
return url ;
2020-10-12 16:23:07 +02:00
}
2021-01-17 20:34:35 +01:00
2021-07-13 19:09:07 +02:00
/ * *
* @deprecated USage of exitSceneUrl is deprecated and therefore , this method is deprecated too .
* /
public static getRoomPathFromExitSceneUrl (
exitSceneUrl : string ,
currentRoomUrl : string ,
currentMapUrl : string
) : URL {
const absoluteExitSceneUrl = new URL ( exitSceneUrl , currentMapUrl ) ;
const baseUrl = new URL ( currentRoomUrl ) ;
const currentRoom = new Room ( baseUrl ) ;
let instance : string = "global" ;
if ( currentRoom . isPublic ) {
instance = currentRoom . instance as string ;
}
baseUrl . pathname = "/_/" + instance + "/" + absoluteExitSceneUrl . host + absoluteExitSceneUrl . pathname ;
if ( absoluteExitSceneUrl . hash ) {
baseUrl . hash = absoluteExitSceneUrl . hash ;
}
return baseUrl ;
}
/ * *
* @deprecated
* /
public static getIdFromIdentifier (
identifier : string ,
baseUrl : string ,
currentInstance : string
) : { roomId : string ; hash : string | null } {
let roomId = "" ;
2021-06-23 12:42:24 +02:00
let hash = null ;
2021-07-13 19:09:07 +02:00
if ( ! identifier . startsWith ( "/_/" ) && ! identifier . startsWith ( "/@/" ) ) {
//relative file link
2021-01-05 17:08:33 +01:00
//Relative identifier can be deep enough to rewrite the base domain, so we cannot use the variable 'baseUrl' as the actual base url for the URL objects.
//We instead use 'workadventure' as a dummy base value.
const baseUrlObject = new URL ( baseUrl ) ;
2021-07-13 19:09:07 +02:00
const absoluteExitSceneUrl = new URL (
identifier ,
"http://workadventure/_/" + currentInstance + "/" + baseUrlObject . hostname + baseUrlObject . pathname
) ;
2021-01-05 17:08:33 +01:00
roomId = absoluteExitSceneUrl . pathname ; //in case of a relative url, we need to create a public roomId
roomId = roomId . substring ( 1 ) ; //remove the leading slash
2020-11-18 18:15:57 +01:00
hash = absoluteExitSceneUrl . hash ;
hash = hash . substring ( 1 ) ; //remove the leading diese
2021-06-23 15:06:38 +02:00
if ( ! hash . length ) {
2021-07-13 19:09:07 +02:00
hash = null ;
2021-06-23 15:06:38 +02:00
}
2021-07-13 19:09:07 +02:00
} else {
//absolute room Id
const parts = identifier . split ( "#" ) ;
2020-11-18 18:15:57 +01:00
roomId = parts [ 0 ] ;
roomId = roomId . substring ( 1 ) ; //remove the leading slash
if ( parts . length > 1 ) {
2021-07-13 19:09:07 +02:00
hash = parts [ 1 ] ;
2020-11-18 18:15:57 +01:00
}
}
2021-07-13 19:09:07 +02:00
return { roomId , hash } ;
2020-11-18 18:15:57 +01:00
}
2020-10-13 16:46:46 +02:00
2021-07-13 19:09:07 +02:00
private async getMapDetail ( ) : Promise < MapDetail | RoomRedirect > {
const result = await Axios . get ( ` ${ PUSHER_URL } /map ` , {
params : {
playUri : this.roomUrl.toString ( ) ,
} ,
2020-10-13 16:46:46 +02:00
} ) ;
2021-07-13 19:09:07 +02:00
const data = result . data ;
if ( data . redirectUrl ) {
return {
redirectUrl : data.redirectUrl as string ,
} ;
}
console . log ( "Map " , this . id , " resolves to URL " , data . mapUrl ) ;
this . _mapUrl = data . mapUrl ;
this . _textures = data . textures ;
return new MapDetail ( data . mapUrl , data . textures ) ;
2020-10-13 16:46:46 +02:00
}
2020-10-13 18:44:50 +02:00
/ * *
* Instance name is :
* - In a public URL : the second part of the URL ( _ / [ instance ] / map . json )
* - In a private URL : [ organizationId / worldId ]
* /
public getInstance ( ) : string {
if ( this . instance !== undefined ) {
return this . instance ;
}
if ( this . isPublic ) {
const match = /_\/([^/]+)\/.+/ . exec ( this . id ) ;
2021-06-23 12:42:24 +02:00
if ( ! match ) throw new Error ( 'Could not extract instance from "' + this . id + '"' ) ;
2020-10-13 18:44:50 +02:00
this . instance = match [ 1 ] ;
return this . instance ;
} else {
2020-10-14 10:37:00 +02:00
const match = /@\/([^/]+)\/([^/]+)\/.+/ . exec ( this . id ) ;
2021-06-23 12:42:24 +02:00
if ( ! match ) throw new Error ( 'Could not extract instance from "' + this . id + '"' ) ;
2021-07-13 19:09:07 +02:00
this . instance = match [ 1 ] + "/" + match [ 2 ] ;
2020-10-13 18:44:50 +02:00
return this . instance ;
}
}
2021-07-13 19:09:07 +02:00
/ * *
* @deprecated
* /
private parsePrivateUrl ( url : string ) : { organizationSlug : string ; worldSlug : string ; roomSlug? : string } {
2020-10-13 17:30:53 +02:00
const regex = /@\/([^/]+)\/([^/]+)(?:\/([^/]*))?/gm ;
2020-10-13 16:46:46 +02:00
const match = regex . exec ( url ) ;
if ( ! match ) {
2021-07-13 19:09:07 +02:00
throw new Error ( "Invalid URL " + url ) ;
2020-10-13 16:46:46 +02:00
}
2021-07-13 19:09:07 +02:00
const results : { organizationSlug : string ; worldSlug : string ; roomSlug? : string } = {
2020-10-13 16:46:46 +02:00
organizationSlug : match [ 1 ] ,
worldSlug : match [ 2 ] ,
2021-07-13 19:09:07 +02:00
} ;
2020-10-13 16:46:46 +02:00
if ( match [ 3 ] !== undefined ) {
results . roomSlug = match [ 3 ] ;
}
return results ;
}
2021-03-30 16:08:49 +02:00
2021-06-23 12:42:24 +02:00
public isDisconnected ( ) : boolean {
2021-07-13 19:09:07 +02:00
const alone = this . _search . get ( "alone" ) ;
if ( alone && alone !== "0" && alone . toLowerCase ( ) !== "false" ) {
2021-03-30 16:08:49 +02:00
return true ;
}
return false ;
}
public get search ( ) : URLSearchParams {
return this . _search ;
}
2021-07-13 19:09:07 +02:00
/ * *
* 2 rooms are equal if they share the same path ( but not necessarily the same hash )
* @param room
* /
public isEqual ( room : Room ) : boolean {
return room . key === this . key ;
}
/ * *
* A key representing this room
* /
public get key ( ) : string {
const newUrl = new URL ( this . roomUrl . toString ( ) ) ;
newUrl . hash = "" ;
return newUrl . toString ( ) ;
}
get textures ( ) : CharacterTexture [ ] | undefined {
return this . _textures ;
}
get mapUrl ( ) : string {
if ( ! this . _mapUrl ) {
throw new Error ( "Map URL not fetched yet" ) ;
}
return this . _mapUrl ;
}
2020-10-13 16:46:46 +02:00
}