Merge branch 'develop' of https://github.com/thecodingmachine/workadventure into SoundInMapScript
# Conflicts: # front/src/Api/IframeListener.ts # front/src/Phaser/Game/GameScene.ts # front/src/iframe_api.ts # maps/Tuto/scriptTuto.js # maps/Village/Village.json
This commit is contained in:
commit
0c3b9ccfbf
189 changed files with 11326 additions and 2993 deletions
|
@ -1,7 +1,55 @@
|
|||
export interface IframeEvent {
|
||||
type: string;
|
||||
data: unknown;
|
||||
|
||||
|
||||
import type { ButtonClickedEvent } from './ButtonClickedEvent';
|
||||
import type { ChatEvent } from './ChatEvent';
|
||||
import type { ClosePopupEvent } from './ClosePopupEvent';
|
||||
import type { EnterLeaveEvent } from './EnterLeaveEvent';
|
||||
import type { GoToPageEvent } from './GoToPageEvent';
|
||||
import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
|
||||
import type { OpenPopupEvent } from './OpenPopupEvent';
|
||||
import type { OpenTabEvent } from './OpenTabEvent';
|
||||
import type { UserInputChatEvent } from './UserInputChatEvent';
|
||||
|
||||
|
||||
export interface TypedMessageEvent<T> extends MessageEvent {
|
||||
data: T
|
||||
}
|
||||
|
||||
export type IframeEventMap = {
|
||||
//getState: GameStateEvent,
|
||||
// updateTile: UpdateTileEvent
|
||||
chat: ChatEvent,
|
||||
openPopup: OpenPopupEvent
|
||||
closePopup: ClosePopupEvent
|
||||
openTab: OpenTabEvent
|
||||
goToPage: GoToPageEvent
|
||||
openCoWebSite: OpenCoWebSiteEvent
|
||||
closeCoWebSite: null
|
||||
disablePlayerControls: null
|
||||
restorePlayerControls: null
|
||||
displayBubble: null
|
||||
removeBubble: null
|
||||
}
|
||||
export interface IframeEvent<T extends keyof IframeEventMap> {
|
||||
type: T;
|
||||
data: IframeEventMap[T];
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const isIframeEventWrapper = (event: any): event is IframeEvent<keyof IframeEventMap> => typeof event.type === 'string';
|
||||
|
||||
export interface IframeResponseEventMap {
|
||||
userInputChat: UserInputChatEvent
|
||||
enterEvent: EnterLeaveEvent
|
||||
leaveEvent: EnterLeaveEvent
|
||||
buttonClickedEvent: ButtonClickedEvent
|
||||
// gameState: GameStateEvent
|
||||
}
|
||||
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
|
||||
type: T;
|
||||
data: IframeResponseEventMap[T];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const isIframeEventWrapper = (event: any): event is IframeEvent => typeof event.type === 'string';
|
||||
export const isIframeResponseEventWrapper = (event: { type?: string }): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === 'string';
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
import {Subject} from "rxjs";
|
||||
import {ChatEvent, isChatEvent} from "./Events/ChatEvent";
|
||||
import {IframeEvent, isIframeEventWrapper} from "./Events/IframeEvent";
|
||||
import {UserInputChatEvent} from "./Events/UserInputChatEvent";
|
||||
import * as crypto from "crypto";
|
||||
import {HtmlUtils} from "../WebRtc/HtmlUtils";
|
||||
import {EnterLeaveEvent} from "./Events/EnterLeaveEvent";
|
||||
import {isOpenPopupEvent, OpenPopupEvent} from "./Events/OpenPopupEvent";
|
||||
import {isOpenTabEvent, OpenTabEvent} from "./Events/OpenTabEvent";
|
||||
import {ButtonClickedEvent} from "./Events/ButtonClickedEvent";
|
||||
import {ClosePopupEvent, isClosePopupEvent} from "./Events/ClosePopupEvent";
|
||||
import {scriptUtils} from "./ScriptUtils";
|
||||
import {GoToPageEvent, isGoToPageEvent} from "./Events/GoToPageEvent";
|
||||
import {isOpenCoWebsite, OpenCoWebSiteEvent} from "./Events/OpenCoWebSiteEvent";
|
||||
|
||||
import { Subject } from "rxjs";
|
||||
import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
|
||||
import { HtmlUtils } from "../WebRtc/HtmlUtils";
|
||||
import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
|
||||
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
|
||||
import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
|
||||
import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
|
||||
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
|
||||
import { scriptUtils } from "./ScriptUtils";
|
||||
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
|
||||
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
|
||||
import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent";
|
||||
import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
|
||||
import {isPlaySoundEvent, PlaySoundEvent} from "./Events/PlaySoundEvent";
|
||||
import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent";
|
||||
import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent";
|
||||
|
||||
|
||||
/**
|
||||
* Listens to messages from iframes and turn those messages into easy to use observables.
|
||||
* Also allows to send messages to those iframes.
|
||||
|
@ -68,18 +66,18 @@ class IframeListener {
|
|||
private readonly scripts = new Map<string, HTMLIFrameElement>();
|
||||
|
||||
init() {
|
||||
window.addEventListener("message", (message) => {
|
||||
window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => {
|
||||
// Do we trust the sender of this message?
|
||||
// Let's only accept messages from the iframe that are allowed.
|
||||
// Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain).
|
||||
let found = false;
|
||||
let foundSrc: string | null = null;
|
||||
for (const iframe of this.iframes) {
|
||||
if (iframe.contentWindow === message.source) {
|
||||
found = true;
|
||||
foundSrc = iframe.src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
if (!foundSrc) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -92,37 +90,43 @@ class IframeListener {
|
|||
} else if (payload.type === 'closePopup' && isClosePopupEvent(payload.data)) {
|
||||
this._closePopupStream.next(payload.data);
|
||||
}
|
||||
else if(payload.type === 'openTab' && isOpenTabEvent(payload.data)) {
|
||||
else if (payload.type === 'openTab' && isOpenTabEvent(payload.data)) {
|
||||
scriptUtils.openTab(payload.data.url);
|
||||
}
|
||||
else if(payload.type === 'goToPage' && isGoToPageEvent(payload.data)) {
|
||||
else if (payload.type === 'goToPage' && isGoToPageEvent(payload.data)) {
|
||||
scriptUtils.goToPage(payload.data.url);
|
||||
}
|
||||
else if(payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) {
|
||||
scriptUtils.openCoWebsite(payload.data.url);
|
||||
}
|
||||
else if(payload.type === 'playSound' && isPlaySoundEvent(payload.data)) {
|
||||
else if (payload.type === 'playSound' && isPlaySoundEvent(payload.data)) {
|
||||
this._playSoundStream.next(payload.data);
|
||||
}
|
||||
else if(payload.type === 'stopSound' && isStopSoundEvent(payload.data)) {
|
||||
else if (payload.type === 'stopSound' && isStopSoundEvent(payload.data)) {
|
||||
this._stopSoundStream.next(payload.data);
|
||||
}
|
||||
else if(payload.type === 'loadSound' && isLoadSoundEvent(payload.data)) {
|
||||
else if (payload.type === 'loadSound' && isLoadSoundEvent(payload.data)) {
|
||||
this._loadSoundStream.next(payload.data);
|
||||
}
|
||||
else if(payload.type === 'closeCoWebSite') {
|
||||
else if (payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) {
|
||||
const scriptUrl = [...this.scripts.keys()].find(key => {
|
||||
return this.scripts.get(key)?.contentWindow == message.source
|
||||
})
|
||||
|
||||
scriptUtils.openCoWebsite(payload.data.url, scriptUrl || foundSrc);
|
||||
}
|
||||
|
||||
else if (payload.type === 'closeCoWebSite') {
|
||||
scriptUtils.closeCoWebSite();
|
||||
}
|
||||
else if (payload.type === 'disablePlayerControl'){
|
||||
|
||||
else if (payload.type === 'disablePlayerControls') {
|
||||
this._disablePlayerControlStream.next();
|
||||
}
|
||||
else if (payload.type === 'restorePlayerControl'){
|
||||
else if (payload.type === 'restorePlayerControls') {
|
||||
this._enablePlayerControlStream.next();
|
||||
}
|
||||
else if (payload.type === 'displayBubble'){
|
||||
else if (payload.type === 'displayBubble') {
|
||||
this._displayBubbleStream.next();
|
||||
}
|
||||
else if (payload.type === 'removeBubble'){
|
||||
else if (payload.type === 'removeBubble') {
|
||||
this._removeBubbleStream.next();
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +155,7 @@ class IframeListener {
|
|||
const iframe = document.createElement('iframe');
|
||||
iframe.id = this.getIFrameId(scriptUrl);
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = '/iframe.html?script='+encodeURIComponent(scriptUrl);
|
||||
iframe.src = '/iframe.html?script=' + encodeURIComponent(scriptUrl);
|
||||
|
||||
// We are putting a sandbox on this script because it will run in the same domain as the main website.
|
||||
iframe.sandbox.add('allow-scripts');
|
||||
|
@ -175,8 +179,8 @@ class IframeListener {
|
|||
'\n' +
|
||||
'<html lang="en">\n' +
|
||||
'<head>\n' +
|
||||
'<script src="'+window.location.protocol+'//'+window.location.host+'/iframe_api.js" ></script>\n' +
|
||||
'<script src="'+scriptUrl+'" ></script>\n' +
|
||||
'<script src="' + window.location.protocol + '//' + window.location.host + '/iframe_api.js" ></script>\n' +
|
||||
'<script src="' + scriptUrl + '" ></script>\n' +
|
||||
'</head>\n' +
|
||||
'</html>\n';
|
||||
|
||||
|
@ -193,14 +197,14 @@ class IframeListener {
|
|||
}
|
||||
|
||||
private getIFrameId(scriptUrl: string): string {
|
||||
return 'script'+crypto.createHash('md5').update(scriptUrl).digest("hex");
|
||||
return 'script' + btoa(scriptUrl);
|
||||
}
|
||||
|
||||
unregisterScript(scriptUrl: string): void {
|
||||
const iFrameId = this.getIFrameId(scriptUrl);
|
||||
const iframe = HtmlUtils.getElementByIdOrFail<HTMLIFrameElement>(iFrameId);
|
||||
if (!iframe) {
|
||||
throw new Error('Unknown iframe for script "'+scriptUrl+'"');
|
||||
throw new Error('Unknown iframe for script "' + scriptUrl + '"');
|
||||
}
|
||||
this.unregisterIframe(iframe);
|
||||
iframe.remove();
|
||||
|
@ -248,7 +252,7 @@ class IframeListener {
|
|||
/**
|
||||
* Sends the message... to all allowed iframes.
|
||||
*/
|
||||
private postMessage(message: IframeEvent) {
|
||||
private postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
|
||||
for (const iframe of this.iframes) {
|
||||
iframe.contentWindow?.postMessage(message, '*');
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ class ScriptUtils {
|
|||
|
||||
}
|
||||
|
||||
public openCoWebsite(url : string){
|
||||
coWebsiteManager.loadCoWebsite(url,url);
|
||||
public openCoWebsite(url: string, base: string) {
|
||||
coWebsiteManager.loadCoWebsite(url, base);
|
||||
}
|
||||
|
||||
public closeCoWebSite(){
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue