diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 3bf00b99..e33346d0 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -199,4 +199,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - msg: Environment deployed at https://play-${{ env.GITHUB_HEAD_REF_SLUG }}.test.workadventu.re + msg: "Environment deployed at https://play-${{ env.GITHUB_HEAD_REF_SLUG }}.test.workadventu.re \nTests available at https://maps-${{ env.GITHUB_HEAD_REF_SLUG }}.test.workadventu.re/tests" diff --git a/.github/workflows/push-to-npm.yml b/.github/workflows/push-to-npm.yml index 798e2530..fd247b11 100644 --- a/.github/workflows/push-to-npm.yml +++ b/.github/workflows/push-to-npm.yml @@ -2,6 +2,7 @@ name: Push @workadventure/iframe-api-typings to NPM on: release: types: [created] + push: jobs: build: runs-on: ubuntu-latest @@ -13,10 +14,6 @@ jobs: node-version: '14.x' registry-url: 'https://registry.npmjs.org' - - name: Edit tsconfig.json to add declarations - run: "sed -i 's/\"declaration\": false/\"declaration\": true/g' tsconfig.json" - working-directory: "front" - - name: Replace version number run: 'sed -i "s#VERSION_PLACEHOLDER#${GITHUB_REF/refs\/tags\//}#g" package.json' working-directory: "front/packages/iframe-api-typings" @@ -47,15 +44,18 @@ jobs: working-directory: "front" - name: "Build" - run: yarn run build + run: yarn run build-typings env: - API_URL: "localhost:8080" + PUSHER_URL: "//localhost:8080" working-directory: "front" # We build the front to generate the typings of iframe_api, then we copy those typings in a separate package. - name: Copy typings to package dir run: cp front/dist/src/iframe_api.d.ts front/packages/iframe-api-typings/iframe_api.d.ts + - name: Copy typings to package dir (2) + run: cp -R front/dist/src/Api front/packages/iframe-api-typings/Api + - name: Install dependencies in package run: yarn install working-directory: "front/packages/iframe-api-typings" @@ -65,3 +65,4 @@ jobs: working-directory: "front/packages/iframe-api-typings" env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + if: ${{ github.event_name == 'release' }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f72510..fa3dd293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,19 @@ - Use `WA.room.getCurrentUser(): Promise` to get the ID, name and tags of the current player - Use `WA.room.getCurrentRoom(): Promise` to get the ID, JSON map file, url of the map of the current room and the layer where the current player started - Use `WA.ui.registerMenuCommand(): void` to add a custom menu + - Use `WA.room.setTiles(): void` to change an array of tiles + +## Version 1.4.3 - 1.4.4 - 1.4.5 + +## Bugfixes + +- Fixing the generation of @workadventure/iframe-api-typings + +## Version 1.4.2 + +## Updates + +- A script in an iframe opened by another script can use the IFrame API. ## Version 1.4.1 diff --git a/docs/maps/api-nav.md b/docs/maps/api-nav.md index 29323632..f5721063 100644 --- a/docs/maps/api-nav.md +++ b/docs/maps/api-nav.md @@ -52,11 +52,11 @@ WA.nav.goToRoom("/_/global/.json#start-layer-2") ### Opening/closing a web page in an iFrame ``` -WA.nav.openCoWebSite(url: string): void +WA.nav.openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void WA.nav.closeCoWebSite(): void ``` -Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. +Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. `allowApi` allows the webpage to use the "IFrame API" and execute script (it is equivalent to putting the `openWebsiteAllowApi` property in the map). `allowPolicy` grants additional access rights to the iFrame. The `allowPolicy` parameter is turned into an [`allow` feature policy in the iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow). Example: @@ -65,4 +65,3 @@ WA.nav.openCoWebSite('https://www.wikipedia.org/'); // ... WA.nav.closeCoWebSite(); ``` - diff --git a/docs/maps/api-reference.md b/docs/maps/api-reference.md index 30a11b2a..8c8205d8 100644 --- a/docs/maps/api-reference.md +++ b/docs/maps/api-reference.md @@ -9,4 +9,4 @@ - [Sound functions](api-sound.md) - [Controls functions](api-controls.md) -- [List of deprecated functions](api-deprecated.md) \ No newline at end of file +- [List of deprecated functions](api-deprecated.md) diff --git a/docs/maps/api-room.md b/docs/maps/api-room.md index d8381cc6..17e3d48e 100644 --- a/docs/maps/api-room.md +++ b/docs/maps/api-room.md @@ -112,3 +112,35 @@ WA.room.getCurrentUser().then((user) => { } }) ``` + +### Changing tiles +``` +WA.room.setTiles(tiles: TileDescriptor[]): void +``` +Replace the tile at the `x` and `y` coordinates in the layer named `layer` by the tile with the id `tile`. + +If `tile` is a string, it's not the id of the tile but the value of the property `name`. +
+
+ +
+
+ +`TileDescriptor` has the following attributes : +* **x (number) :** The coordinate x of the tile that you want to replace. +* **y (number) :** The coordinate y of the tile that you want to replace. +* **tile (number | string) :** The id of the tile that will be placed in the map. +* **layer (string) :** The name of the layer where the tile will be placed. + +**Important !** : If you use `tile` as a number, be sure to add the `firstgid` of the tileset of the tile that you want to the id of the tile in Tiled Editor. + + +Example : +```javascript +WA.room.setTiles([ + {x: 6, y: 4, tile: 'blue', layer: 'setTiles'}, + {x: 7, y: 4, tile: 109, layer: 'setTiles'}, + {x: 8, y: 4, tile: 109, layer: 'setTiles'}, + {x: 9, y: 4, tile: 'blue', layer: 'setTiles'} + ]); +``` diff --git a/front/dist/static/images/favicons/android-icon-144x144.png b/front/dist/static/images/favicons/android-icon-144x144.png index b0463804..59a7c4ce 100644 Binary files a/front/dist/static/images/favicons/android-icon-144x144.png and b/front/dist/static/images/favicons/android-icon-144x144.png differ diff --git a/front/dist/static/images/favicons/android-icon-192x192.png b/front/dist/static/images/favicons/android-icon-192x192.png index 6f764aae..e1a4b3ed 100644 Binary files a/front/dist/static/images/favicons/android-icon-192x192.png and b/front/dist/static/images/favicons/android-icon-192x192.png differ diff --git a/front/dist/static/images/favicons/android-icon-36x36.png b/front/dist/static/images/favicons/android-icon-36x36.png index cc93c290..f9d822e9 100644 Binary files a/front/dist/static/images/favicons/android-icon-36x36.png and b/front/dist/static/images/favicons/android-icon-36x36.png differ diff --git a/front/dist/static/images/favicons/android-icon-48x48.png b/front/dist/static/images/favicons/android-icon-48x48.png index a2a7f7e9..35b1f177 100644 Binary files a/front/dist/static/images/favicons/android-icon-48x48.png and b/front/dist/static/images/favicons/android-icon-48x48.png differ diff --git a/front/dist/static/images/favicons/android-icon-72x72.png b/front/dist/static/images/favicons/android-icon-72x72.png index 9ae8c47a..b04f1b25 100644 Binary files a/front/dist/static/images/favicons/android-icon-72x72.png and b/front/dist/static/images/favicons/android-icon-72x72.png differ diff --git a/front/dist/static/images/favicons/android-icon-96x96.png b/front/dist/static/images/favicons/android-icon-96x96.png index 43324e2c..380de08a 100644 Binary files a/front/dist/static/images/favicons/android-icon-96x96.png and b/front/dist/static/images/favicons/android-icon-96x96.png differ diff --git a/front/dist/static/images/favicons/apple-icon-114x114.png b/front/dist/static/images/favicons/apple-icon-114x114.png index f205a3ad..6c994217 100644 Binary files a/front/dist/static/images/favicons/apple-icon-114x114.png and b/front/dist/static/images/favicons/apple-icon-114x114.png differ diff --git a/front/dist/static/images/favicons/apple-icon-120x120.png b/front/dist/static/images/favicons/apple-icon-120x120.png index 09f4e85d..b20bf1e2 100644 Binary files a/front/dist/static/images/favicons/apple-icon-120x120.png and b/front/dist/static/images/favicons/apple-icon-120x120.png differ diff --git a/front/dist/static/images/favicons/apple-icon-144x144.png b/front/dist/static/images/favicons/apple-icon-144x144.png index b0463804..59a7c4ce 100644 Binary files a/front/dist/static/images/favicons/apple-icon-144x144.png and b/front/dist/static/images/favicons/apple-icon-144x144.png differ diff --git a/front/dist/static/images/favicons/apple-icon-152x152.png b/front/dist/static/images/favicons/apple-icon-152x152.png index fa06794f..fc905367 100644 Binary files a/front/dist/static/images/favicons/apple-icon-152x152.png and b/front/dist/static/images/favicons/apple-icon-152x152.png differ diff --git a/front/dist/static/images/favicons/apple-icon-180x180.png b/front/dist/static/images/favicons/apple-icon-180x180.png index 4b9af8b6..8f18bd51 100644 Binary files a/front/dist/static/images/favicons/apple-icon-180x180.png and b/front/dist/static/images/favicons/apple-icon-180x180.png differ diff --git a/front/dist/static/images/favicons/apple-icon-57x57.png b/front/dist/static/images/favicons/apple-icon-57x57.png index bd72841f..62c8ab07 100644 Binary files a/front/dist/static/images/favicons/apple-icon-57x57.png and b/front/dist/static/images/favicons/apple-icon-57x57.png differ diff --git a/front/dist/static/images/favicons/apple-icon-60x60.png b/front/dist/static/images/favicons/apple-icon-60x60.png index 89238b40..68c5b74b 100644 Binary files a/front/dist/static/images/favicons/apple-icon-60x60.png and b/front/dist/static/images/favicons/apple-icon-60x60.png differ diff --git a/front/dist/static/images/favicons/apple-icon-72x72.png b/front/dist/static/images/favicons/apple-icon-72x72.png index 9ae8c47a..b04f1b25 100644 Binary files a/front/dist/static/images/favicons/apple-icon-72x72.png and b/front/dist/static/images/favicons/apple-icon-72x72.png differ diff --git a/front/dist/static/images/favicons/apple-icon-76x76.png b/front/dist/static/images/favicons/apple-icon-76x76.png index fbd0c29c..a58cf3ed 100644 Binary files a/front/dist/static/images/favicons/apple-icon-76x76.png and b/front/dist/static/images/favicons/apple-icon-76x76.png differ diff --git a/front/dist/static/images/favicons/apple-icon-precomposed.png b/front/dist/static/images/favicons/apple-icon-precomposed.png index 3132ca5a..e1a4b3ed 100644 Binary files a/front/dist/static/images/favicons/apple-icon-precomposed.png and b/front/dist/static/images/favicons/apple-icon-precomposed.png differ diff --git a/front/dist/static/images/favicons/apple-icon.png b/front/dist/static/images/favicons/apple-icon.png index 3132ca5a..e1a4b3ed 100644 Binary files a/front/dist/static/images/favicons/apple-icon.png and b/front/dist/static/images/favicons/apple-icon.png differ diff --git a/front/dist/static/images/favicons/favicon-16x16.png b/front/dist/static/images/favicons/favicon-16x16.png index a49eb71a..cd608133 100644 Binary files a/front/dist/static/images/favicons/favicon-16x16.png and b/front/dist/static/images/favicons/favicon-16x16.png differ diff --git a/front/dist/static/images/favicons/favicon-32x32.png b/front/dist/static/images/favicons/favicon-32x32.png index 957f5006..a15ffef5 100644 Binary files a/front/dist/static/images/favicons/favicon-32x32.png and b/front/dist/static/images/favicons/favicon-32x32.png differ diff --git a/front/dist/static/images/favicons/favicon-96x96.png b/front/dist/static/images/favicons/favicon-96x96.png index 43324e2c..380de08a 100644 Binary files a/front/dist/static/images/favicons/favicon-96x96.png and b/front/dist/static/images/favicons/favicon-96x96.png differ diff --git a/front/dist/static/images/favicons/favicon.ico b/front/dist/static/images/favicons/favicon.ico index ec628ea2..203ce590 100644 Binary files a/front/dist/static/images/favicons/favicon.ico and b/front/dist/static/images/favicons/favicon.ico differ diff --git a/front/dist/static/images/favicons/ms-icon-144x144.png b/front/dist/static/images/favicons/ms-icon-144x144.png index b0463804..59a7c4ce 100644 Binary files a/front/dist/static/images/favicons/ms-icon-144x144.png and b/front/dist/static/images/favicons/ms-icon-144x144.png differ diff --git a/front/dist/static/images/favicons/ms-icon-150x150.png b/front/dist/static/images/favicons/ms-icon-150x150.png index 4ab38ea3..3515b43a 100644 Binary files a/front/dist/static/images/favicons/ms-icon-150x150.png and b/front/dist/static/images/favicons/ms-icon-150x150.png differ diff --git a/front/dist/static/images/favicons/ms-icon-310x310.png b/front/dist/static/images/favicons/ms-icon-310x310.png index 56ceeb95..115ce84d 100644 Binary files a/front/dist/static/images/favicons/ms-icon-310x310.png and b/front/dist/static/images/favicons/ms-icon-310x310.png differ diff --git a/front/dist/static/images/favicons/ms-icon-70x70.png b/front/dist/static/images/favicons/ms-icon-70x70.png index 3fa12cae..342deff9 100644 Binary files a/front/dist/static/images/favicons/ms-icon-70x70.png and b/front/dist/static/images/favicons/ms-icon-70x70.png differ diff --git a/front/package.json b/front/package.json index ee031668..0e0439ed 100644 --- a/front/package.json +++ b/front/package.json @@ -60,6 +60,7 @@ "templater": "cross-env ./templater.sh", "serve": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open", "build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack", + "build-typings": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production BUILD_TYPINGS=1 webpack", "test": "TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "lint": "node_modules/.bin/eslint src/ . --ext .ts", "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts", diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 7325f811..137eccad 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -18,7 +18,7 @@ import type { PlaySoundEvent } from "./PlaySoundEvent"; import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent"; import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent'; import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent"; - +import type { SetTilesEvent } from "./SetTilesEvent"; export interface TypedMessageEvent extends MessageEvent { data: T @@ -26,7 +26,6 @@ export interface TypedMessageEvent extends MessageEvent { export type IframeEventMap = { //getState: GameStateEvent, - // updateTile: UpdateTileEvent loadPage: LoadPageEvent chat: ChatEvent, openPopup: OpenPopupEvent @@ -46,7 +45,8 @@ export type IframeEventMap = { getDataLayer: undefined loadSound: LoadSoundEvent playSound: PlaySoundEvent - stopSound: null, + stopSound: null + setTiles: SetTilesEvent getState: undefined, registerMenuCommand: MenuItemRegisterEvent } diff --git a/front/src/Api/Events/OpenCoWebSiteEvent.ts b/front/src/Api/Events/OpenCoWebSiteEvent.ts index 0fbc0ce2..d2937405 100644 --- a/front/src/Api/Events/OpenCoWebSiteEvent.ts +++ b/front/src/Api/Events/OpenCoWebSiteEvent.ts @@ -5,6 +5,8 @@ import * as tg from "generic-type-guard"; export const isOpenCoWebsite = new tg.IsInterface().withProperties({ url: tg.isString, + allowApi: tg.isBoolean, + allowPolicy: tg.isString, }).get(); /** diff --git a/front/src/Api/Events/SetTilesEvent.ts b/front/src/Api/Events/SetTilesEvent.ts new file mode 100644 index 00000000..24dd2e35 --- /dev/null +++ b/front/src/Api/Events/SetTilesEvent.ts @@ -0,0 +1,15 @@ +import * as tg from "generic-type-guard"; + +export const isSetTilesEvent = + tg.isArray( + new tg.IsInterface().withProperties({ + x: tg.isNumber, + y: tg.isNumber, + tile: tg.isUnion(tg.isNumber, tg.isString), + layer: tg.isString + }).get() + ); +/** + * A message sent from the iFrame to the game to set one or many tiles. + */ +export type SetTilesEvent = tg.GuardedType; diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 9311d7b6..3143238d 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -18,7 +18,6 @@ import { TypedMessageEvent } from "./Events/IframeEvent"; import type {UserInputChatEvent} from "./Events/UserInputChatEvent"; -//import { isLoadPageEvent } from './Events/LoadPageEvent'; import {isPlaySoundEvent, PlaySoundEvent} from "./Events/PlaySoundEvent"; import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent"; import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent"; @@ -30,6 +29,7 @@ import type {GameStateEvent} from "./Events/GameStateEvent"; import type {HasPlayerMovedEvent} from "./Events/HasPlayerMovedEvent"; import {isLoadPageEvent} from "./Events/LoadPageEvent"; import {handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent} from "./Events/ui/MenuItemRegisterEvent"; +import {SetTilesEvent, isSetTilesEvent} from "./Events/SetTilesEvent"; /** * Listens to messages from iframes and turn those messages into easy to use observables. @@ -103,6 +103,9 @@ class IframeListener { private readonly _loadSoundStream: Subject = new Subject(); public readonly loadSoundStream = this._loadSoundStream.asObservable(); + private readonly _setTilesStream: Subject = new Subject(); + public readonly setTilesStream = this._setTilesStream.asObservable(); + private readonly iframes = new Set(); private readonly iframeCloseCallbacks = new Map void)[]>(); private readonly scripts = new Map(); @@ -123,11 +126,20 @@ class IframeListener { } } + const payload = message.data; + if (foundSrc === undefined) { + if (isIframeEventWrapper(payload)) { + console.warn('It seems an iFrame is trying to communicate with WorkAdventure but was not explicitly granted the permission to do so. ' + + 'If you are looking to use the WorkAdventure Scripting API inside an iFrame, you should allow the ' + + 'iFrame to communicate with WorkAdventure by using the "openWebsiteAllowApi" property in your map (or passing "true" as a second' + + 'parameter to WA.nav.openCoWebSite())'); + } return; } - const payload = message.data; + foundSrc = this.getBaseUrl(foundSrc, message.source); + if (isIframeEventWrapper(payload)) { if (payload.type === 'showLayer' && isLayerEvent(payload.data)) { this._showLayerStream.next(payload.data); @@ -161,7 +173,7 @@ class IframeListener { this._loadSoundStream.next(payload.data); } else if (payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) { - scriptUtils.openCoWebsite(payload.data.url, foundSrc); + scriptUtils.openCoWebsite(payload.data.url, foundSrc, payload.data.allowApi, payload.data.allowPolicy); } else if (payload.type === 'closeCoWebSite') { @@ -190,6 +202,8 @@ class IframeListener { this._unregisterMenuCommandStream.next(data); }) handleMenuItemRegistrationEvent(payload.data) + } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { + this._setTilesStream.next(payload.data); } } }, false); @@ -274,6 +288,15 @@ class IframeListener { } + private getBaseUrl(src: string, source: MessageEventSource | null): string{ + for (const script of this.scripts) { + if (script[1].contentWindow === source) { + return script[0]; + } + } + return src; + } + private static getIFrameId(scriptUrl: string): string { return 'script' + btoa(scriptUrl); } diff --git a/front/src/Api/ScriptUtils.ts b/front/src/Api/ScriptUtils.ts index e1c94507..75a18dc0 100644 --- a/front/src/Api/ScriptUtils.ts +++ b/front/src/Api/ScriptUtils.ts @@ -11,8 +11,8 @@ class ScriptUtils { } - public openCoWebsite(url: string, base: string) { - coWebsiteManager.loadCoWebsite(url, base); + public openCoWebsite(url: string, base: string, api: boolean, policy: string) { + coWebsiteManager.loadCoWebsite(url, base, api, policy); } public closeCoWebSite(){ diff --git a/front/src/Api/iframe/chat.ts b/front/src/Api/iframe/chat.ts index 7d8e6f71..5797df5a 100644 --- a/front/src/Api/iframe/chat.ts +++ b/front/src/Api/iframe/chat.ts @@ -1,30 +1,30 @@ -import type { ChatEvent } from '../Events/ChatEvent' -import { isUserInputChatEvent, UserInputChatEvent } from '../Events/UserInputChatEvent' -import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution' +import type { ChatEvent } from "../Events/ChatEvent"; +import { isUserInputChatEvent, UserInputChatEvent } from "../Events/UserInputChatEvent"; +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; import { apiCallback } from "./registeredCallbacks"; -import {Subject} from "rxjs"; +import { Subject } from "rxjs"; const chatStream = new Subject(); -class WorkadventureChatCommands extends IframeApiContribution { - - callbacks = [apiCallback({ - callback: (event: UserInputChatEvent) => { - chatStream.next(event.message); - }, - type: "userInputChat", - typeChecker: isUserInputChatEvent - })] - +export class WorkadventureChatCommands extends IframeApiContribution { + callbacks = [ + apiCallback({ + callback: (event: UserInputChatEvent) => { + chatStream.next(event.message); + }, + type: "userInputChat", + typeChecker: isUserInputChatEvent, + }), + ]; sendChatMessage(message: string, author: string) { sendToWorkadventure({ - type: 'chat', + type: "chat", data: { - 'message': message, - 'author': author - } - }) + message: message, + author: author, + }, + }); } /** @@ -35,4 +35,4 @@ class WorkadventureChatCommands extends IframeApiContribution { - callbacks = [] +export class WorkadventureControlsCommands extends IframeApiContribution { + callbacks = []; disablePlayerControls(): void { - sendToWorkadventure({ 'type': 'disablePlayerControls', data: null }); + sendToWorkadventure({ type: "disablePlayerControls", data: null }); } restorePlayerControls(): void { - sendToWorkadventure({ 'type': 'restorePlayerControls', data: null }); + sendToWorkadventure({ type: "restorePlayerControls", data: null }); } } - export default new WorkadventureControlsCommands(); diff --git a/front/src/Api/iframe/nav.ts b/front/src/Api/iframe/nav.ts index 0d31c1ea..f051a7c0 100644 --- a/front/src/Api/iframe/nav.ts +++ b/front/src/Api/iframe/nav.ts @@ -1,57 +1,56 @@ -import type { GoToPageEvent } from '../Events/GoToPageEvent'; -import type { OpenTabEvent } from '../Events/OpenTabEvent'; -import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; -import type {OpenCoWebSiteEvent} from "../Events/OpenCoWebSiteEvent"; -import type {LoadPageEvent} from "../Events/LoadPageEvent"; - - -class WorkadventureNavigationCommands extends IframeApiContribution { - callbacks = [] +import type { GoToPageEvent } from "../Events/GoToPageEvent"; +import type { OpenTabEvent } from "../Events/OpenTabEvent"; +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; +import type { OpenCoWebSiteEvent } from "../Events/OpenCoWebSiteEvent"; +import type { LoadPageEvent } from "../Events/LoadPageEvent"; +export class WorkadventureNavigationCommands extends IframeApiContribution { + callbacks = []; openTab(url: string): void { sendToWorkadventure({ - "type": 'openTab', - "data": { - url - } + type: "openTab", + data: { + url, + }, }); } goToPage(url: string): void { sendToWorkadventure({ - "type": 'goToPage', - "data": { - url - } + type: "goToPage", + data: { + url, + }, }); } goToRoom(url: string): void { sendToWorkadventure({ - "type": 'loadPage', - "data": { - url - } + type: "loadPage", + data: { + url, + }, }); } - openCoWebSite(url: string): void { + openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void { sendToWorkadventure({ - "type": 'openCoWebSite', - "data": { - url - } + type: "openCoWebSite", + data: { + url, + allowApi, + allowPolicy, + }, }); } closeCoWebSite(): void { sendToWorkadventure({ - "type": 'closeCoWebSite', - data: null + type: "closeCoWebSite", + data: null, }); } } - export default new WorkadventureNavigationCommands(); diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index 67c012f7..e130d3f2 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -1,29 +1,29 @@ -import {IframeApiContribution, sendToWorkadventure} from "./IframeApiContribution"; -import type {HasPlayerMovedEvent, HasPlayerMovedEventCallback} from "../Events/HasPlayerMovedEvent"; -import {Subject} from "rxjs"; -import {apiCallback} from "./registeredCallbacks"; -import {isHasPlayerMovedEvent} from "../Events/HasPlayerMovedEvent"; +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; +import type { HasPlayerMovedEvent, HasPlayerMovedEventCallback } from "../Events/HasPlayerMovedEvent"; +import { Subject } from "rxjs"; +import { apiCallback } from "./registeredCallbacks"; +import { isHasPlayerMovedEvent } from "../Events/HasPlayerMovedEvent"; const moveStream = new Subject(); -class WorkadventurePlayerCommands extends IframeApiContribution { +export class WorkadventurePlayerCommands extends IframeApiContribution { callbacks = [ apiCallback({ - type: 'hasPlayerMoved', + type: "hasPlayerMoved", typeChecker: isHasPlayerMovedEvent, callback: (payloadData) => { moveStream.next(payloadData); - } + }, }), - ] + ]; onPlayerMove(callback: HasPlayerMovedEventCallback): void { moveStream.subscribe(callback); sendToWorkadventure({ - type: 'onPlayerMove', - data: null - }) + type: "onPlayerMove", + data: null, + }); } } -export default new WorkadventurePlayerCommands(); \ No newline at end of file +export default new WorkadventurePlayerCommands(); diff --git a/front/src/Api/iframe/room.ts b/front/src/Api/iframe/room.ts index aed4d983..84e9d6ca 100644 --- a/front/src/Api/iframe/room.ts +++ b/front/src/Api/iframe/room.ts @@ -1,87 +1,92 @@ import { Subject } from "rxjs"; -import { EnterLeaveEvent, isEnterLeaveEvent } from '../Events/EnterLeaveEvent'; -import {IframeApiContribution, sendToWorkadventure} from './IframeApiContribution'; + +import { isDataLayerEvent } from "../Events/DataLayerEvent"; +import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent"; +import { isGameStateEvent } from "../Events/GameStateEvent"; + +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; import { apiCallback } from "./registeredCallbacks"; -import type {LayerEvent} from "../Events/LayerEvent"; -import type {SetPropertyEvent} from "../Events/setPropertyEvent"; -import type {GameStateEvent} from "../Events/GameStateEvent"; -import type {ITiledMap} from "../../Phaser/Map/ITiledMap"; -import type {DataLayerEvent} from "../Events/DataLayerEvent"; -import {isGameStateEvent} from "../Events/GameStateEvent"; -import {isDataLayerEvent} from "../Events/DataLayerEvent"; + +import type { ITiledMap } from "../../Phaser/Map/ITiledMap"; +import type { DataLayerEvent } from "../Events/DataLayerEvent"; +import type { GameStateEvent } from "../Events/GameStateEvent"; const enterStreams: Map> = new Map>(); const leaveStreams: Map> = new Map>(); const dataLayerResolver = new Subject(); const stateResolvers = new Subject(); -let immutableData: GameStateEvent; +let immutableDataPromise: Promise | undefined = undefined; interface Room { - id: string, - mapUrl: string, - map: ITiledMap, - startLayer: string | null + id: string; + mapUrl: string; + map: ITiledMap; + startLayer: string | null; } interface User { - id: string | undefined, - nickName: string | null, - tags: string[] + id: string | undefined; + nickName: string | null; + tags: string[]; +} + +interface TileDescriptor { + x: number + y: number + tile: number | string + layer: string } function getGameState(): Promise { - if (immutableData) { - return Promise.resolve(immutableData); - } - else { - return new Promise((resolver, thrower) => { + if (immutableDataPromise === undefined) { + immutableDataPromise = new Promise((resolver, thrower) => { stateResolvers.subscribe(resolver); - sendToWorkadventure({type: "getState", data: null}); - }) + sendToWorkadventure({ type: "getState", data: null }); + }); } + return immutableDataPromise; } function getDataLayer(): Promise { return new Promise((resolver, thrower) => { dataLayerResolver.subscribe(resolver); - sendToWorkadventure({type: "getDataLayer", data: null}) - }) + sendToWorkadventure({ type: "getDataLayer", data: null }); + }); } -class WorkadventureRoomCommands extends IframeApiContribution { +export class WorkadventureRoomCommands extends IframeApiContribution { callbacks = [ apiCallback({ callback: (payloadData: EnterLeaveEvent) => { enterStreams.get(payloadData.name)?.next(); }, type: "enterEvent", - typeChecker: isEnterLeaveEvent + typeChecker: isEnterLeaveEvent, }), apiCallback({ type: "leaveEvent", typeChecker: isEnterLeaveEvent, callback: (payloadData) => { leaveStreams.get(payloadData.name)?.next(); - } + }, }), apiCallback({ type: "gameState", typeChecker: isGameStateEvent, callback: (payloadData) => { stateResolvers.next(payloadData); - } + }, }), apiCallback({ type: "dataLayer", typeChecker: isDataLayerEvent, callback: (payloadData) => { dataLayerResolver.next(payloadData); - } + }, }), - ] - + ]; onEnterZone(name: string, callback: () => void): void { let subject = enterStreams.get(name); @@ -90,7 +95,6 @@ class WorkadventureRoomCommands extends IframeApiContribution void): void { let subject = leaveStreams.get(name); @@ -101,35 +105,45 @@ class WorkadventureRoomCommands extends IframeApiContribution { return getGameState().then((gameState) => { - return getDataLayer().then((mapJson) => { - return {id: gameState.roomId, map: mapJson.data as ITiledMap, mapUrl: gameState.mapUrl, startLayer: gameState.startLayerName}; - }) - }) + return getDataLayer().then((mapJson) => { + return { + id: gameState.roomId, + map: mapJson.data as ITiledMap, + mapUrl: gameState.mapUrl, + startLayer: gameState.startLayerName, + }; + }); + }); } getCurrentUser(): Promise { return getGameState().then((gameState) => { - return {id: gameState.uuid, nickName: gameState.nickname, tags: gameState.tags}; + return { id: gameState.uuid, nickName: gameState.nickname, tags: gameState.tags }; + }); + } + setTiles(tiles: TileDescriptor[]) { + sendToWorkadventure({ + type: 'setTiles', + data: tiles }) } } - export default new WorkadventureRoomCommands(); diff --git a/front/src/Api/iframe/sound.ts b/front/src/Api/iframe/sound.ts index 70430b46..1e5d6157 100644 --- a/front/src/Api/iframe/sound.ts +++ b/front/src/Api/iframe/sound.ts @@ -1,17 +1,15 @@ -import type { LoadSoundEvent } from '../Events/LoadSoundEvent'; -import type { PlaySoundEvent } from '../Events/PlaySoundEvent'; -import type { StopSoundEvent } from '../Events/StopSoundEvent'; -import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; -import {Sound} from "./Sound/Sound"; +import type { LoadSoundEvent } from "../Events/LoadSoundEvent"; +import type { PlaySoundEvent } from "../Events/PlaySoundEvent"; +import type { StopSoundEvent } from "../Events/StopSoundEvent"; +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; +import { Sound } from "./Sound/Sound"; -class WorkadventureSoundCommands extends IframeApiContribution { - callbacks = [] +export class WorkadventureSoundCommands extends IframeApiContribution { + callbacks = []; loadSound(url: string): Sound { return new Sound(url); } - } - export default new WorkadventureSoundCommands(); diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index c7655b84..61c7076e 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -1,53 +1,55 @@ -import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; -import { isMenuItemClickedEvent } from '../Events/ui/MenuItemClickedEvent'; -import type { MenuItemRegisterEvent } from '../Events/ui/MenuItemRegisterEvent'; -import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; +import { isButtonClickedEvent } from "../Events/ButtonClickedEvent"; +import { isMenuItemClickedEvent } from "../Events/ui/MenuItemClickedEvent"; +import type { MenuItemRegisterEvent } from "../Events/ui/MenuItemRegisterEvent"; +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; import { apiCallback } from "./registeredCallbacks"; import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor"; import { Popup } from "./Ui/Popup"; let popupId = 0; const popups: Map = new Map(); -const popupCallbacks: Map> = new Map>(); +const popupCallbacks: Map> = new Map< + number, + Map +>(); -const menuCallbacks: Map void> = new Map() +const menuCallbacks: Map void> = new Map(); interface ZonedPopupOptions { - zone: string - objectLayerName?: string, - popupText: string, - delay?: number - popupOptions: Array + zone: string; + objectLayerName?: string; + popupText: string; + delay?: number; + popupOptions: Array; } - -class WorkAdventureUiCommands extends IframeApiContribution { - - callbacks = [apiCallback({ - type: "buttonClickedEvent", - typeChecker: isButtonClickedEvent, - callback: (payloadData) => { - const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); - const popup = popups.get(payloadData.popupId); - if (popup === undefined) { - throw new Error('Could not find popup with ID "' + payloadData.popupId + '"'); - } - if (callback) { - callback(popup); - } - } - }), - apiCallback({ - type: "menuItemClicked", - typeChecker: isMenuItemClickedEvent, - callback: event => { - const callback = menuCallbacks.get(event.menuItem); - if (callback) { - callback(event.menuItem) - } - } - })]; - +export class WorkAdventureUiCommands extends IframeApiContribution { + callbacks = [ + apiCallback({ + type: "buttonClickedEvent", + typeChecker: isButtonClickedEvent, + callback: (payloadData) => { + const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); + const popup = popups.get(payloadData.popupId); + if (popup === undefined) { + throw new Error('Could not find popup with ID "' + payloadData.popupId + '"'); + } + if (callback) { + callback(popup); + } + }, + }), + apiCallback({ + type: "menuItemClicked", + typeChecker: isMenuItemClickedEvent, + callback: (event) => { + const callback = menuCallbacks.get(event.menuItem); + if (callback) { + callback(event.menuItem); + } + }, + }), + ]; openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { popupId++; @@ -66,40 +68,40 @@ class WorkAdventureUiCommands extends IframeApiContribution { return { label: button.label, - className: button.className + className: button.className, }; - }) - } + }), + }, }); - popups.set(popupId, popup) + popups.set(popupId, popup); return popup; } registerMenuCommand(commandDescriptor: string, callback: (commandDescriptor: string) => void) { menuCallbacks.set(commandDescriptor, callback); sendToWorkadventure({ - 'type': 'registerMenuCommand', - 'data': { - menutItem: commandDescriptor - } + type: "registerMenuCommand", + data: { + menutItem: commandDescriptor, + }, }); } displayBubble(): void { - sendToWorkadventure({ 'type': 'displayBubble', data: null }); + sendToWorkadventure({ type: "displayBubble", data: null }); } removeBubble(): void { - sendToWorkadventure({ 'type': 'removeBubble', data: null }); + sendToWorkadventure({ type: "removeBubble", data: null }); } } diff --git a/front/src/Components/CustomCharacterScene/CustomCharacterScene.svelte b/front/src/Components/CustomCharacterScene/CustomCharacterScene.svelte index 1807f15d..684975bb 100644 --- a/front/src/Components/CustomCharacterScene/CustomCharacterScene.svelte +++ b/front/src/Components/CustomCharacterScene/CustomCharacterScene.svelte @@ -1,11 +1,11 @@ + + +

Website opened by script.

+ + diff --git a/maps/tests/Metadata/cowebsiteAllowApi.js b/maps/tests/Metadata/cowebsiteAllowApi.js new file mode 100644 index 00000000..56c7b767 --- /dev/null +++ b/maps/tests/Metadata/cowebsiteAllowApi.js @@ -0,0 +1 @@ +WA.nav.openCoWebSite("cowebsiteAllowApi.html", true, ""); diff --git a/maps/tests/Metadata/cowebsiteAllowApi.json b/maps/tests/Metadata/cowebsiteAllowApi.json new file mode 100644 index 00000000..55ed615f --- /dev/null +++ b/maps/tests/Metadata/cowebsiteAllowApi.json @@ -0,0 +1,98 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51], + "height":10, + "id":1, + "name":"bottom", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":116.5, + "id":1, + "name":"", + "rotation":0, + "text": + { + "text":"Test : \nThe iframe is opened by script.\n\nResult : \nA message is send to the chat.", + "wrap":true + }, + "type":"", + "visible":true, + "width":295.875, + "x":11.8125, + "y":188.5 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 16, 16, 16, 0, 0, 16, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 16, 16, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":4, + "name":"mushroom", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }], + "nextlayerid":5, + "nextobjectid":2, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"cowebsiteAllowApi.js" + }], + "renderorder":"right-down", + "tiledversion":"1.4.3", + "tileheight":32, + "tilesets":[ + { + "columns":8, + "firstgid":1, + "image":"tileset_dungeon.png", + "imageheight":256, + "imagewidth":256, + "margin":0, + "name":"tileset_dungeon", + "spacing":0, + "tilecount":64, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.4, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/Metadata/map.json b/maps/tests/Metadata/map.json new file mode 100644 index 00000000..8967ed02 --- /dev/null +++ b/maps/tests/Metadata/map.json @@ -0,0 +1,230 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":1, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 33, 34, 34, 34, 34, 34, 34, 35, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 41, 42, 42, 42, 42, 42, 42, 43, 46, 46, 49, 50, 50, 50, 50, 50, 50, 51, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46], + "height":10, + "id":2, + "name":"bottom", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":4, + "name":"metadata", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":5, + "name":"floorLayer", + "objects":[], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "data":[1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19], + "height":10, + "id":3, + "name":"wall", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }], + "nextlayerid":6, + "nextobjectid":1, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"script.js" + }], + "renderorder":"right-down", + "tiledversion":"1.4.3", + "tileheight":32, + "tilesets":[ + { + "columns":8, + "firstgid":1, + "image":"tileset_dungeon.png", + "imageheight":256, + "imagewidth":256, + "margin":0, + "name":"TDungeon", + "spacing":0, + "tilecount":64, + "tileheight":32, + "tiles":[ + { + "id":0, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":1, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":2, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":3, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":4, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":8, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":9, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":10, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":11, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":12, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":16, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":17, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":18, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":19, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":20, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.4, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/Metadata/script.js b/maps/tests/Metadata/script.js new file mode 100644 index 00000000..c857d783 --- /dev/null +++ b/maps/tests/Metadata/script.js @@ -0,0 +1,9 @@ + + +/*WA.getMapUrl().then((map) => {console.log('mapUrl : ', map)}); +WA.getUuid().then((uuid) => {console.log('Uuid : ',uuid)}); +WA.getRoomId().then((roomId) => console.log('roomID : ',roomId));*/ + +//WA.onPlayerMove(console.log); +WA.setProperty('metadata', 'openWebsite', 'https://fr.wikipedia.org/'); +WA.getDataLayer().then((data) => {console.log('data 1 : ', data)}); \ No newline at end of file diff --git a/maps/tests/Metadata/setTiles.html b/maps/tests/Metadata/setTiles.html new file mode 100644 index 00000000..90b5a84d --- /dev/null +++ b/maps/tests/Metadata/setTiles.html @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/maps/tests/Metadata/setTiles.json b/maps/tests/Metadata/setTiles.json new file mode 100644 index 00000000..5b281a15 --- /dev/null +++ b/maps/tests/Metadata/setTiles.json @@ -0,0 +1,348 @@ +{ "compressionlevel":-1, + "editorsettings": + { + "export": + { + "target":"." + } + }, + "height":10, + "infinite":false, + "layers":[ + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":1, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51], + "height":10, + "id":2, + "name":"bottom", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":4, + "name":"metadata", + "opacity":1, + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"setTiles.html" + }, + { + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true + }], + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 65, 65, 65, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":8, + "name":"setTiles", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":5, + "name":"floorLayer", + "objects":[ + { + "height":191.866671635267, + "id":1, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":9, + "text":"Test : \nWalk on the grass\n\nResult : \nThe Yellow Tile opens a Jitsi with Trigger.\n\nThe Red Tile opens cowebsite Wikipedia. The highest Red Tile is found by 'name' property index, the lowest by 'number' index.\n\nThe White Tiles are silent tiles. You cannot open a bubble in it. (Even if the other player didn't activate the script.)\n\nThe Pale Tile (Lowest) is an exitUrl tile to customMenu.json.\n\nThe Blue Tile are 'collides' tile. The two tiles in the center are 'number' index. The others are 'name' property index.\n", + "wrap":true + }, + "type":"", + "visible":true, + "width":274.674838251912, + "x":32.5473600365393, + "y":128.305680721763 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":9, + "nextobjectid":2, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.4.3", + "tileheight":32, + "tilesets":[ + { + "columns":8, + "firstgid":1, + "image":"tileset_dungeon.png", + "imageheight":256, + "imagewidth":256, + "margin":0, + "name":"TDungeon", + "spacing":0, + "tilecount":64, + "tileheight":32, + "tiles":[ + { + "id":0, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":1, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":2, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":3, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":4, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":8, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":9, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":10, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":11, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":12, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":16, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":17, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":18, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":19, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":20, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }, + { + "columns":8, + "firstgid":65, + "image":"floortileset.png", + "imageheight":288, + "imagewidth":256, + "margin":0, + "name":"Floor", + "spacing":0, + "tilecount":72, + "tileheight":32, + "tiles":[ + { + "id":9, + "properties":[ + { + "name":"exitUrl", + "type":"string", + "value":"customMenu.json" + }] + }, + { + "id":27, + "properties":[ + { + "name":"jitsiRoom", + "type":"string", + "value":"TEST" + }, + { + "name":"jitsiTrigger", + "type":"string", + "value":"onaction" + }, + { + "name":"jitsiUrl", + "type":"string", + "value":"meet.jit.si" + }] + }, + { + "id":34, + "properties":[ + { + "name":"name", + "type":"string", + "value":"Red" + }, + { + "name":"openWebsite", + "type":"string", + "value":"https:\/\/fr.wikipedia.org\/wiki\/Wikip%C3%A9dia:Accueil_principal" + }] + }, + { + "id":40, + "properties":[ + { + "name":"name", + "type":"string", + "value":"" + }] + }, + { + "id":44, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }, + { + "name":"name", + "type":"string", + "value":"blue" + }] + }, + { + "id":52, + "properties":[ + { + "name":"silent", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.4, + "width":10 +} diff --git a/maps/tests/iframe.html b/maps/tests/iframe.html index aa8e55ec..038874b6 100644 --- a/maps/tests/iframe.html +++ b/maps/tests/iframe.html @@ -1,22 +1,30 @@ - +
diff --git a/maps/tests/index.html b/maps/tests/index.html index 0929ab83..1a4e3590 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -4,7 +4,7 @@ - + @@ -106,6 +106,14 @@ Test the HelpCameraSettingScene + + + + + + + + + + + +
Result
+ Success Failure Pending + + Test a iframe opened by a script can use Iframe API +
Success Failure Pending @@ -162,10 +170,41 @@ Test animated tiles
+ Success Failure Pending + + Test cowebsite opened by script is allowed to use IFrame API +
+ Success Failure Pending + + Test set tiles +