diff --git a/back/src/Assets/Maps/Floor0/floor0.json b/back/src/Assets/Maps/Floor0/floor0.json index 631cb84c..be96afaf 100644 --- a/back/src/Assets/Maps/Floor0/floor0.json +++ b/back/src/Assets/Maps/Floor0/floor0.json @@ -49,7 +49,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"\/Floor1\/floor1.json" + "value":"..\/Floor1\/floor1.json" }], "type":"tilelayer", "visible":true, @@ -67,7 +67,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"\/Lyon\/lyon.json" + "value":"..\/Lyon\/lyon.json" }], "type":"tilelayer", "visible":true, diff --git a/back/src/Assets/Maps/Floor1/floor1.json b/back/src/Assets/Maps/Floor1/floor1.json index 6791b98a..b443e68f 100644 --- a/back/src/Assets/Maps/Floor1/floor1.json +++ b/back/src/Assets/Maps/Floor1/floor1.json @@ -43,7 +43,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"\/Floor0\/floor0.json" + "value":"..\/Floor0\/floor0.json" }], "type":"tilelayer", "visible":true, diff --git a/back/src/Assets/Maps/Lyon/floortileset.png b/back/src/Assets/Maps/Lyon/floortileset.png new file mode 100644 index 00000000..c672dcbb Binary files /dev/null and b/back/src/Assets/Maps/Lyon/floortileset.png differ diff --git a/back/src/Assets/Maps/Lyon/lyon.json b/back/src/Assets/Maps/Lyon/lyon.json index 69fdff1d..0ed62a9c 100644 --- a/back/src/Assets/Maps/Lyon/lyon.json +++ b/back/src/Assets/Maps/Lyon/lyon.json @@ -37,7 +37,7 @@ { "name":"exitSceneUrl", "type":"string", - "value":"\/Floor0\/floor0.json" + "value":"..\/Floor0\/floor0.json" }], "type":"tilelayer", "visible":true, diff --git a/back/src/Assets/Maps/Lyon/tilesets_deviant_milkian_1.png b/back/src/Assets/Maps/Lyon/tilesets_deviant_milkian_1.png new file mode 100644 index 00000000..47400131 Binary files /dev/null and b/back/src/Assets/Maps/Lyon/tilesets_deviant_milkian_1.png differ diff --git a/back/src/Controller/MapController.ts b/back/src/Controller/MapController.ts index 65ca7956..bc7546cc 100644 --- a/back/src/Controller/MapController.ts +++ b/back/src/Controller/MapController.ts @@ -20,7 +20,7 @@ export class MapController { getMaps() { this.App.get("/maps", (req: Request, res: Response) => { return res.status(OK).send({ - mapUrlStart: URL_ROOM_STARTED + mapUrlStart: req.headers.host + "/map/files" + URL_ROOM_STARTED }); }); } diff --git a/front/dist/.htaccess b/front/dist/.htaccess new file mode 100644 index 00000000..53979e53 --- /dev/null +++ b/front/dist/.htaccess @@ -0,0 +1,23 @@ +DirectoryIndex index.html + +# By default, Apache does not evaluate symbolic links if you did not enable this +# feature in your server configuration. Uncomment the following line if you +# install assets as symlinks or if you experience problems related to symlinks +# when compiling LESS/Sass/CoffeScript assets. +# Options FollowSymlinks + +# Disabling MultiViews prevents unwanted negotiation, e.g. "/index" should not resolve +# to the front controller "/index.php" but be rewritten to "/index.php/index". + + Options -MultiViews + + +RewriteEngine On + +RewriteBase / + +# If the requested filename exists, simply serve it. +# We only want to let Apache serve files and not directories. +# Rewrite all other queries starting with _ to index.ts. +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule "^_/" "/index.html" [L] diff --git a/front/dist/index.html b/front/dist/index.html index 85aced1e..51d52705 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -5,6 +5,7 @@ + Document diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts index 69b6647b..f7d1e7db 100644 --- a/front/src/Connexion.ts +++ b/front/src/Connexion.ts @@ -140,7 +140,6 @@ export interface ConnexionInterface { token: string; email: string; userId: string; - startedRoom: string; createConnexion(characterSelected: string): Promise; @@ -167,7 +166,6 @@ export class Connexion implements ConnexionInterface { token: string; email: string; userId: string; - startedRoom: string; GameManager: GameManager; @@ -183,8 +181,6 @@ export class Connexion implements ConnexionInterface { return Axios.post(`${API_URL}/login`, {email: this.email}) .then((res) => { this.token = res.data.token; - - this.startedRoom = getMapKeyByUrl(res.data.mapUrlStart); this.userId = res.data.userId; this.socket = SocketIo(`${API_URL}`, { @@ -194,10 +190,10 @@ export class Connexion implements ConnexionInterface { }); //join the room - this.joinARoom(this.startedRoom, characterSelected); + //this.joinARoom(this.startedRoom, characterSelected); //share your first position - this.sharePosition(0, 0, characterSelected, this.startedRoom); + //this.sharePosition(0, 0, characterSelected, this.startedRoom); this.positionOfAllUser(); diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index 50296fe6..30250d5e 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -3,13 +3,11 @@ const API_URL = process.env.API_URL || "http://api.workadventure.localhost"; const ROOM = [process.env.ROOM || "THECODINGMACHINE"]; const RESOLUTION = 3; const ZOOM_LEVEL = 1/*3/4*/; -const MAP_FILE_URL = `${API_URL}/map/files`; export { DEBUG_MODE, API_URL, RESOLUTION, ZOOM_LEVEL, - ROOM, - MAP_FILE_URL + ROOM } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 78019f5d..78436429 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -5,6 +5,9 @@ import { ListMessageUserPositionInterface } from "../../Connexion"; import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer"; +import {getMapKeyByUrl} from "../Login/LogincScene"; +import SceneManager = Phaser.Scenes.SceneManager; +import ScenePlugin = Phaser.Scenes.ScenePlugin; export enum StatusGameManagerEnum { IN_PROGRESS = 1, @@ -124,6 +127,18 @@ export class GameManager { pushPlayerPosition(event: HasMovedEvent) { this.ConnexionInstance.sharePosition(event.x, event.y, event.character, this.currentGameScene.scene.key, event.direction); } + + loadMap(mapUrl: string, scene: ScenePlugin): string { + let sceneKey = getMapKeyByUrl(mapUrl); + + let gameIndex = scene.getIndex(sceneKey); + let game : Phaser.Scene = null; + if(gameIndex === -1){ + game = new GameScene(sceneKey, mapUrl); + scene.add(sceneKey, game, false); + } + return sceneKey; + } } export const gameManager = new GameManager(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9120378d..701cbe31 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,14 +1,13 @@ import {GameManager, gameManager, HasMovedEvent, MapObject, StatusGameManagerEnum} from "./GameManager"; import {GroupCreatedUpdatedMessageInterface, MessageUserPositionInterface} from "../../Connexion"; import {CurrentGamerInterface, GamerInterface, hasMovedEventName, Player} from "../Player/Player"; -import { DEBUG_MODE, MAP_FILE_URL, RESOLUTION, ROOM, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; +import { DEBUG_MODE, RESOLUTION, ROOM, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; import {ITiledMap, ITiledMapLayer, ITiledTileSet} from "../Map/ITiledMap"; import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; import Texture = Phaser.Textures.Texture; import Sprite = Phaser.GameObjects.Sprite; import CanvasTexture = Phaser.Textures.CanvasTexture; import CreateSceneFromObjectConfig = Phaser.Types.Scenes.CreateSceneFromObjectConfig; -import {getMapKeyByUrl} from "../Login/LogincScene"; export enum Textures { Player = "male1" @@ -61,7 +60,7 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface, Creat // Triggered when the map is loaded // Load tiles attached to the map recursively this.map = data.data; - let url = this.MapUrlFile.substr(0, this.MapUrlFile.indexOf(`${this.MapKey}.json`)); + let url = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); this.map.tilesets.forEach((tileset) => { if (typeof tileset.name === 'undefined' || typeof tileset.image === 'undefined') { console.warn("Don't know how to handle tileset ", tileset) @@ -151,6 +150,15 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface, Creat context.strokeStyle = '#ffffff'; context.stroke(); this.circleTexture.refresh(); + + // Let's alter browser history + let url = new URL(this.MapUrlFile); + let path = '/_/'+url.host+url.pathname; + if (url.hash) { + // FIXME: entry should be dictated by a property passed to init() + path += '#'+url.hash; + } + window.history.pushState({}, null, path); } private getExitSceneUrl(layer: ITiledMapLayer): string|undefined { @@ -175,19 +183,11 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface, Creat private loadNextGame(layer: ITiledMapLayer, mapWidth: number, tileWidth: number, tileHeight: number){ let exitSceneUrl = this.getExitSceneUrl(layer); - let exitSceneKey = getMapKeyByUrl(exitSceneUrl); + // TODO: eventually compute a relative URL + let absoluteExitSceneUrl = new URL(exitSceneUrl, this.MapUrlFile).href; + console.log('absoluteExitSceneUrl ', absoluteExitSceneUrl); + let exitSceneKey = gameManager.loadMap(absoluteExitSceneUrl, this.scene); - let gameIndex = this.scene.getIndex(exitSceneKey); - let game : Phaser.Scene = null; - if(gameIndex === -1){ - game = new GameScene(exitSceneKey, `${MAP_FILE_URL}${exitSceneUrl}`); - this.scene.add(exitSceneKey, game, false); - }else{ - game = this.scene.get(exitSceneKey); - } - if(!game){ - return; - } let tiles : any = layer.data; tiles.forEach((objectKey : number, key: number) => { if(objectKey === 0){ diff --git a/front/src/Phaser/Login/LogincScene.ts b/front/src/Phaser/Login/LogincScene.ts index 49487376..3136060c 100644 --- a/front/src/Phaser/Login/LogincScene.ts +++ b/front/src/Phaser/Login/LogincScene.ts @@ -8,9 +8,15 @@ import Rectangle = Phaser.GameObjects.Rectangle; import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; import {cypressAsserter} from "../../Cypress/CypressAsserter"; import {GroupCreatedUpdatedMessageInterface, MessageUserPositionInterface} from "../../Connexion"; -import {MAP_FILE_URL} from "../../Enum/EnvironmentVariable"; export function getMapKeyByUrl(mapUrlStart: string){ + // FIXME: the key should be computed from the full URL of the map. + let startPos = mapUrlStart.indexOf('://')+3; + let endPos = mapUrlStart.indexOf(".json"); + console.log('MAP KEY '+mapUrlStart.substring(startPos, endPos)); + return mapUrlStart.substring(startPos, endPos); + + let tab = mapUrlStart.split("/"); return tab[tab.length -1].substr(0, tab[tab.length -1].indexOf(".json")); } @@ -99,25 +105,40 @@ export class LogincScene extends Phaser.Scene implements GameSceneInterface { private async login(name: string) { return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => { - return gameManager.loadMaps().then((scene : any) => { - if (!scene) { - return; - } - let key = getMapKeyByUrl(scene.mapUrlStart); - let game = new GameScene(key,`${MAP_FILE_URL}${scene.mapUrlStart}`); - this.scene.add(key, game, false); + // Do we have a start URL in the address bar? If so, let's redirect to this address + let mapUrl = this.findMapUrl(); + if (mapUrl !== null) { + let key = gameManager.loadMap(mapUrl, this.scene); this.scene.start(key); - return scene; - }).catch((err) => { - console.error(err); - throw err; - }); + return mapUrl; + } else { + // If we do not have a map address in the URL, let's ask the server for a start map. + return gameManager.loadMaps().then((scene : any) => { + if (!scene) { + return; + } + let key = gameManager.loadMap(window.location.protocol+"//"+scene.mapUrlStart, this.scene); + this.scene.start(key); + return scene; + }).catch((err) => { + console.error(err); + throw err; + }); + } }).catch((err) => { console.error(err); throw err; }); } + private findMapUrl(): string|null { + let path = window.location.pathname; + if (!path.startsWith('/_/')) { + return null; + } + return window.location.protocol+'//'+path.substr(3); + } + Map: Phaser.Tilemaps.Tilemap; initAnimation(): void { diff --git a/front/webpack.config.js b/front/webpack.config.js index ff164804..d1bccd6f 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -8,6 +8,12 @@ module.exports = { contentBase: './dist', host: '0.0.0.0', disableHostCheck: true, + historyApiFallback: { + rewrites: [ + { from: /^_\/.*$/, to: '/index.html' } + ], + disableDotRule: true + }, }, module: { rules: [ @@ -24,6 +30,7 @@ module.exports = { output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), + publicPath: '/' }, plugins: [ new webpack.ProvidePlugin({