From 2448fef53af92a6bc853508179639f135ad600b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sat, 23 May 2020 17:27:49 +0200 Subject: [PATCH] Adding a notion of instances per mapAdding a notion of instances to room The URL signature becomes: https://workadventu.re/_/[instance]/[path_to_map.json] This allows us to create many instances of the same map (and therefore to create several different worlds for different people) An exit on a map can target another "instance" by passing the "exitInstance" property. --- README.md | 5 ++- back/src/Controller/MapController.ts | 3 +- front/src/Phaser/Game/GameManager.ts | 8 ++-- front/src/Phaser/Game/GameScene.ts | 53 +++++++++++++++++++++------ front/src/Phaser/Login/LogincScene.ts | 33 ++++++++--------- front/src/Phaser/Player/Player.ts | 6 --- 6 files changed, 66 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 7883a4e0..88610d0f 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,9 @@ A few things to notice: In order to place an on your scene that leads to another scene: -- You must create an specific layer. When a character reaches ANY tile of that layer, it will exit the scene. -- In layer properties, you must add "exitSceneUrl" property. It represents the map URL of the next scene. For example : `//.json`. Be careful, if you want the next map to be correctly loaded, you must check that the map files are in folder `back/src/Assets/Maps/`. The files will be accessible by url `/map/files//...`. +- You must create a specific layer. When a character reaches ANY tile of that layer, it will exit the scene. +- In layer properties, you MUST add "exitSceneUrl" property. It represents the map URL of the next scene. For example : `//.json`. Be careful, if you want the next map to be correctly loaded, you must check that the map files are in folder `back/src/Assets/Maps/`. The files will be accessible by url `/map/files//...`. +- In layer properties, you CAN add an "exitInstance" property. If set, you will join the map of the specified instance. Otherwise, you will stay on the same instance. - If you want to have multiple exits, you can create many layers with name "exit". Each layer has a different key `exitSceneUrl` and have tiles that represent exits to another scene. ![](doc/images/exit_layer_map.png) diff --git a/back/src/Controller/MapController.ts b/back/src/Controller/MapController.ts index bc7546cc..f7e78a03 100644 --- a/back/src/Controller/MapController.ts +++ b/back/src/Controller/MapController.ts @@ -20,7 +20,8 @@ export class MapController { getMaps() { this.App.get("/maps", (req: Request, res: Response) => { return res.status(OK).send({ - mapUrlStart: req.headers.host + "/map/files" + URL_ROOM_STARTED + mapUrlStart: req.headers.host + "/map/files" + URL_ROOM_STARTED, + startInstance: "global" }); }); } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 7a439fbe..c0f8150d 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -10,8 +10,6 @@ import { PointInterface } from "../../Connexion"; import {SimplePeerInterface, SimplePeer} from "../../WebRtc/SimplePeer"; -import {getMapKeyByUrl} from "../Login/LogincScene"; -import ScenePlugin = Phaser.Scenes.ScenePlugin; import {AddPlayerInterface} from "./AddPlayerInterface"; import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; @@ -169,13 +167,13 @@ export class GameManager { this.ConnexionInstance.sharePosition(event.x, event.y, event.direction, event.moving); } - loadMap(mapUrl: string, scene: ScenePlugin): string { - let sceneKey = getMapKeyByUrl(mapUrl); + loadMap(mapUrl: string, scene: Phaser.Scenes.ScenePlugin, instance: string): string { + let sceneKey = GameScene.getMapKeyByUrl(mapUrl); let gameIndex = scene.getIndex(sceneKey); let game : Phaser.Scene = null; if(gameIndex === -1){ - game = new GameScene(sceneKey, mapUrl); + game = GameScene.createFromUrl(mapUrl, instance); scene.add(sceneKey, game, false); } return sceneKey; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 2ec6ea13..4558bc7f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -40,10 +40,17 @@ export class GameScene extends Phaser.Scene { MapKey: string; MapUrlFile: string; + RoomId: string; + instance: string; PositionNextScene: Array = new Array(); - constructor(MapKey : string = "", MapUrlFile: string = "") { + static createFromUrl(mapUrlFile: string, instance: string): GameScene { + let key = GameScene.getMapKeyByUrl(mapUrlFile); + return new GameScene(key, mapUrlFile, instance); + } + + constructor(MapKey : string, MapUrlFile: string, instance: string) { super({ key: MapKey }); @@ -51,9 +58,11 @@ export class GameScene extends Phaser.Scene { this.GameManager = gameManager; this.Terrains = []; this.groups = new Map(); + this.instance = instance; - this.MapKey = MapKey; + this.MapKey = MapKey; this.MapUrlFile = MapUrlFile; + this.RoomId = this.instance + '__' + this.MapKey; } //hook preload scene @@ -158,7 +167,7 @@ export class GameScene extends Phaser.Scene { // Let's alter browser history let url = new URL(this.MapUrlFile); - let path = '/_/'+url.host+url.pathname; + let path = '/_/'+this.instance+'/'+url.host+url.pathname; if (url.hash) { // FIXME: entry should be dictated by a property passed to init() path += '#'+url.hash; @@ -178,6 +187,18 @@ export class GameScene extends Phaser.Scene { return obj.value; } + private getExitSceneInstance(layer: ITiledMapLayer): string|undefined { + let properties : any = layer.properties; + if (!properties) { + return undefined; + } + let obj = properties.find((property:any) => property.name === "exitInstance"); + if (obj === undefined) { + return undefined; + } + return obj.value; + } + /** * * @param layer @@ -187,10 +208,14 @@ export class GameScene extends Phaser.Scene { */ private loadNextGame(layer: ITiledMapLayer, mapWidth: number, tileWidth: number, tileHeight: number){ let exitSceneUrl = this.getExitSceneUrl(layer); + let instance = this.getExitSceneInstance(layer); + if (instance === undefined) { + instance = this.instance; + } // TODO: eventually compute a relative URL let absoluteExitSceneUrl = new URL(exitSceneUrl, this.MapUrlFile).href; - let exitSceneKey = gameManager.loadMap(absoluteExitSceneUrl, this.scene); + let exitSceneKey = gameManager.loadMap(absoluteExitSceneUrl, this.scene, instance); let tiles : any = layer.data; tiles.forEach((objectKey : number, key: number) => { @@ -264,11 +289,6 @@ export class GameScene extends Phaser.Scene { }); } - addSpite(Object : Phaser.Physics.Arcade.Sprite){ - Object.setImmovable(true); - this.Objects.push(Object); - } - createCollisionObject(){ this.Objects.forEach((Object : Phaser.Physics.Arcade.Sprite) => { this.physics.add.collider(this.CurrentPlayer, Object, (object1: any, object2: any) => { @@ -279,7 +299,7 @@ export class GameScene extends Phaser.Scene { createCurrentPlayer(){ //initialise player - //TODO create animation moving between exit and strat + //TODO create animation moving between exit and start this.CurrentPlayer = new Player( null, // The current player is not has no id (because the id can change if connexion is lost and we should check that id using the GameManager. this, @@ -296,7 +316,7 @@ export class GameScene extends Phaser.Scene { this.createCollisionObject(); //join room - this.GameManager.joinRoom(this.scene.key, this.startX, this.startY, PlayerAnimationNames.WalkDown, false); + this.GameManager.joinRoom(this.RoomId, this.startX, this.startY, PlayerAnimationNames.WalkDown, false); //listen event to share position of user this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) @@ -495,4 +515,15 @@ export class GameScene extends Phaser.Scene { this.groups.get(groupId).destroy(); this.groups.delete(groupId); } + + public static getMapKeyByUrl(mapUrlStart: string) : string { + // FIXME: the key should be computed from the full URL of the map. + let startPos = mapUrlStart.indexOf('://')+3; + let endPos = mapUrlStart.indexOf(".json"); + return mapUrlStart.substring(startPos, endPos); + + + let tab = mapUrlStart.split("/"); + return tab[tab.length -1].substr(0, tab[tab.length -1].indexOf(".json")); + } } diff --git a/front/src/Phaser/Login/LogincScene.ts b/front/src/Phaser/Login/LogincScene.ts index c289b999..b7199e31 100644 --- a/front/src/Phaser/Login/LogincScene.ts +++ b/front/src/Phaser/Login/LogincScene.ts @@ -8,17 +8,6 @@ import {PLAYER_RESOURCES} from "../Entity/PlayableCaracter"; import {cypressAsserter} from "../../Cypress/CypressAsserter"; import {GroupCreatedUpdatedMessageInterface, MessageUserJoined, MessageUserPositionInterface} from "../../Connexion"; -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"); - return mapUrlStart.substring(startPos, endPos); - - - let tab = mapUrlStart.split("/"); - return tab[tab.length -1].substr(0, tab[tab.length -1].indexOf(".json")); -} - //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; enum LoginTextures { @@ -104,9 +93,10 @@ export class LogincScene extends Phaser.Scene { private async login(name: string) { return gameManager.connect(name, this.selectedPlayer.texture.key).then(() => { // 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); + let instanceAndMapUrl = this.findMapUrl(); + if (instanceAndMapUrl !== null) { + let [mapUrl, instance] = instanceAndMapUrl; + let key = gameManager.loadMap(mapUrl, this.scene, instance); this.scene.start(key); return mapUrl; } else { @@ -115,7 +105,7 @@ export class LogincScene extends Phaser.Scene { if (!scene) { return; } - let key = gameManager.loadMap(window.location.protocol+"//"+scene.mapUrlStart, this.scene); + let key = gameManager.loadMap(window.location.protocol + "//" + scene.mapUrlStart, this.scene, scene.startInstance); this.scene.start(key); return scene; }).catch((err) => { @@ -129,12 +119,21 @@ export class LogincScene extends Phaser.Scene { }); } - private findMapUrl(): string|null { + /** + * Returns the map URL and the instance from the current URL + */ + private findMapUrl(): [string, string]|null { let path = window.location.pathname; if (!path.startsWith('/_/')) { return null; } - return window.location.protocol+'//'+path.substr(3); + let instanceAndMap = path.substr(3); + let firstSlash = instanceAndMap.indexOf('/'); + if (firstSlash === -1) { + return null; + } + let instance = instanceAndMap.substr(0, firstSlash); + return [window.location.protocol+'//'+instanceAndMap.substr(firstSlash+1), instance]; } Map: Phaser.Tilemaps.Tilemap; diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 850be563..fe8e5b7f 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -45,12 +45,6 @@ export class Player extends PlayableCaracter implements CurrentGamerInterface, G this.setImmovable(false); this.initAnimation(); - console.warn("Start direction for added player ", direction) - console.warn("Position ", x, y) - /*this.play(`${PlayerTexture}-${direction}`, true); - if (!moving) { - this.stop(); - }*/ this.playAnimation(direction, moving); }