implementation of DataLayerEvent

update GetGameState to add nickname to the returned data
update GameMap to separate phaserLayer and mapLayer
This commit is contained in:
GRL 2021-05-18 15:41:16 +02:00
parent 201fcf6afa
commit aa78bf44ef
10 changed files with 94 additions and 67 deletions

View File

@ -2,7 +2,7 @@ import * as tg from "generic-type-guard";
export const isHasDataLayerChangedEvent = export const isDataLayerEvent =
new tg.IsInterface().withProperties({ new tg.IsInterface().withProperties({
data: tg.isObject data: tg.isObject
}).get(); }).get();
@ -10,7 +10,4 @@ export const isHasDataLayerChangedEvent =
/** /**
* A message sent from the game to the iFrame when the data of the layers change after the iFrame send a message to the game that it want to listen to the data of the layers * A message sent from the game to the iFrame when the data of the layers change after the iFrame send a message to the game that it want to listen to the data of the layers
*/ */
export type DataLayerEvent = tg.GuardedType<typeof isHasDataLayerChangedEvent>; export type DataLayerEvent = tg.GuardedType<typeof isDataLayerEvent>;
export type HasDataLayerChangedEventCallback = (event: DataLayerEvent) => void

View File

@ -18,6 +18,7 @@ export const isGameStateEvent =
new tg.IsInterface().withProperties({ new tg.IsInterface().withProperties({
roomId: tg.isString, roomId: tg.isString,
mapUrl: tg.isString, mapUrl: tg.isString,
nickname: tg.isUnion(tg.isString, tg.isNull),
uuid: tg.isUnion(tg.isString, tg.isUndefined), uuid: tg.isUnion(tg.isString, tg.isUndefined),
startLayerName: tg.isUnion(tg.isString, tg.isNull) startLayerName: tg.isUnion(tg.isString, tg.isNull)
}).get(); }).get();

View File

@ -10,7 +10,7 @@ import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
import type { OpenPopupEvent } from './OpenPopupEvent'; import type { OpenPopupEvent } from './OpenPopupEvent';
import type { OpenTabEvent } from './OpenTabEvent'; import type { OpenTabEvent } from './OpenTabEvent';
import type { UserInputChatEvent } from './UserInputChatEvent'; import type { UserInputChatEvent } from './UserInputChatEvent';
import type { HasDataLayerChangedEvent } from "./HasDataLayerChangedEvent"; import type { DataLayerEvent } from "./DataLayerEvent";
import type { LayerEvent } from './LayerEvent'; import type { LayerEvent } from './LayerEvent';
import type { SetPropertyEvent } from "./setPropertyEvent"; import type { SetPropertyEvent } from "./setPropertyEvent";
@ -37,6 +37,7 @@ export type IframeEventMap = {
showLayer: LayerEvent showLayer: LayerEvent
hideLayer: LayerEvent hideLayer: LayerEvent
setProperty: SetPropertyEvent setProperty: SetPropertyEvent
getDataLayer: undefined
} }
export interface IframeEvent<T extends keyof IframeEventMap> { export interface IframeEvent<T extends keyof IframeEventMap> {
type: T; type: T;
@ -54,7 +55,7 @@ export interface IframeResponseEventMap {
buttonClickedEvent: ButtonClickedEvent buttonClickedEvent: ButtonClickedEvent
gameState: GameStateEvent gameState: GameStateEvent
hasPlayerMoved: HasPlayerMovedEvent hasPlayerMoved: HasPlayerMovedEvent
hasDataLayerChanged: HasDataLayerChangedEvent dataLayer: DataLayerEvent
} }
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> { export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
type: T; type: T;

View File

@ -13,11 +13,10 @@ import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMa
import type { UserInputChatEvent } from "./Events/UserInputChatEvent"; import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
import { isLayerEvent, LayerEvent } from "./Events/LayerEvent"; import { isLayerEvent, LayerEvent } from "./Events/LayerEvent";
import { isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent"; import { isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent";
import { GameStateEvent } from './Events/GameStateEvent'; import type { GameStateEvent } from './Events/GameStateEvent';
import { deepFreezeClone as deepFreezeClone } from '../utility'; import type { HasPlayerMovedEvent } from './Events/HasPlayerMovedEvent';
import { HasPlayerMovedEvent } from './Events/HasPlayerMovedEvent';
import { Math } from 'phaser'; import { Math } from 'phaser';
import { HasDataLayerChangedEvent } from "./Events/HasDataLayerChangedEvent"; import type { DataLayerEvent } from "./Events/DataLayerEvent";
@ -72,11 +71,12 @@ class IframeListener {
private readonly _gameStateStream: Subject<void> = new Subject(); private readonly _gameStateStream: Subject<void> = new Subject();
public readonly gameStateStream = this._gameStateStream.asObservable(); public readonly gameStateStream = this._gameStateStream.asObservable();
private readonly _dataLayerChangeStream: Subject<void> = new Subject();
public readonly dataLayerChangeStream = this._dataLayerChangeStream.asObservable();
private readonly iframes = new Set<HTMLIFrameElement>(); private readonly iframes = new Set<HTMLIFrameElement>();
private readonly scripts = new Map<string, HTMLIFrameElement>(); private readonly scripts = new Map<string, HTMLIFrameElement>();
private sendPlayerMove: boolean = false; private sendPlayerMove: boolean = false;
private sendDataLayerChange: boolean = false;
init() { init() {
window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => { window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => {
@ -138,21 +138,26 @@ class IframeListener {
this._gameStateStream.next(); this._gameStateStream.next();
} else if (payload.type == "onPlayerMove") { } else if (payload.type == "onPlayerMove") {
this.sendPlayerMove = true this.sendPlayerMove = true
} else if (payload.type == "onDataLayerChange") { } else if (payload.type == "getDataLayer") {
this.sendDataLayerChange = true this._dataLayerChangeStream.next();
} }
} }
}, false); }, false);
} }
sendDataLayerEvent(dataLayerEvent: DataLayerEvent) {
this.postMessage({
'type' : 'dataLayer',
'data' : dataLayerEvent
})
}
sendFrozenGameStateEvent(gameStateEvent: GameStateEvent) { sendFrozenGameStateEvent(gameStateEvent: GameStateEvent) {
this.postMessage({ this.postMessage({
'type': 'gameState', 'type': 'gameState',
'data': gameStateEvent //deepFreezeClone(gameStateEvent) 'data': gameStateEvent
}); });
} }
@ -268,15 +273,6 @@ class IframeListener {
} }
} }
hasDataLayerChanged(event: HasDataLayerChangedEvent) {
if (this.sendDataLayerChange) {
this.postMessage({
'type' : 'hasDataLayerChanged',
'data' : event
});
}
}
sendButtonClickedEvent(popupId: number, buttonId: number): void { sendButtonClickedEvent(popupId: number, buttonId: number): void {
this.postMessage({ this.postMessage({
'type': 'buttonClickedEvent', 'type': 'buttonClickedEvent',

View File

@ -1,5 +1,7 @@
import type {ITiledMap, ITiledMapLayer, ITiledMapTileLayer} from "../Map/ITiledMap"; import type {ITiledMap, ITiledMapLayer, ITiledMapTileLayer} from "../Map/ITiledMap";
import { flattenGroupLayersMap } from "../Map/LayersFlattener"; import { flattenGroupLayersMap } from "../Map/LayersFlattener";
import {iframeListener} from "../../Api/IframeListener";
import TilemapLayer = Phaser.Tilemaps.TilemapLayer;
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void; export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
@ -12,13 +14,14 @@ export class GameMap {
private lastProperties = new Map<string, string|boolean|number>(); private lastProperties = new Map<string, string|boolean|number>();
private callbacks = new Map<string, Array<PropertyChangeCallback>>(); private callbacks = new Map<string, Array<PropertyChangeCallback>>();
public readonly flatLayers: ITiledMapLayer[]; public readonly flatLayers: ITiledMapLayer[];
public readonly phaserLayers: TilemapLayer[] = [];
public constructor(private map: ITiledMap, phaserMap: Phaser.Tilemaps.Tilemap, terrains: Array<Phaser.Tilemaps.Tileset>) { public constructor(private map: ITiledMap, phaserMap: Phaser.Tilemaps.Tilemap, terrains: Array<Phaser.Tilemaps.Tileset>) {
this.flatLayers = flattenGroupLayersMap(map); this.flatLayers = flattenGroupLayersMap(map);
let depth = -2; let depth = -2;
for (const layer of this.flatLayers) { for (const layer of this.flatLayers) {
if(layer.type === 'tilelayer'){ if(layer.type === 'tilelayer'){
layer.phaserLayer = phaserMap.createLayer(layer.name, terrains, 0, 0).setDepth(depth); this.phaserLayers.push(phaserMap.createLayer(layer.name, terrains, 0, 0).setDepth(depth));
} }
if (layer.type === 'objectgroup' && layer.name === 'floorLayer') { if (layer.type === 'objectgroup' && layer.name === 'floorLayer') {
depth = 10000; depth = 10000;
@ -89,6 +92,10 @@ export class GameMap {
return properties; return properties;
} }
public getMap(): ITiledMap{
return this.map;
}
private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) { private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) {
const callbacksArray = this.callbacks.get(propName); const callbacksArray = this.callbacks.get(propName);
if (callbacksArray !== undefined) { if (callbacksArray !== undefined) {
@ -127,4 +134,21 @@ export class GameMap {
return undefined; return undefined;
} }
public findPhaserLayer(layerName: string): TilemapLayer | undefined {
let i = 0;
let found = false;
while (!found && i<this.flatLayers.length) {
if (this.flatLayers[i].name === layerName) {
found = true;
}
else {
i++;
}
}
if (found) {
return this.phaserLayers[i];
}
return undefined;
}
} }

View File

@ -1,4 +1,4 @@
import {gameManager, HasMovedEvent} from "./GameManager"; import {gameManager} from "./GameManager";
import type { import type {
GroupCreatedUpdatedMessageInterface, GroupCreatedUpdatedMessageInterface,
MessageUserJoined, MessageUserJoined,
@ -91,9 +91,7 @@ import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { PinchManager } from "../UserInput/PinchManager"; import { PinchManager } from "../UserInput/PinchManager";
import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick"; import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick";
import { waScaleManager } from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent'; import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent';
import {LayerEvent} from "../../Api/Events/LayerEvent";
import {SetPropertyEvent} from "../../Api/Events/setPropertyEve
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface | null, initPosition: PointInterface | null,
@ -869,6 +867,7 @@ ${escapedMessage}
mapUrl: this.MapUrlFile, mapUrl: this.MapUrlFile,
startLayerName: this.startLayerName, startLayerName: this.startLayerName,
uuid: localUserStore.getLocalUser()?.uuid, uuid: localUserStore.getLocalUser()?.uuid,
nickname: localUserStore.getName(),
roomId: this.RoomId, roomId: this.RoomId,
}) })
})); }));
@ -896,6 +895,10 @@ ${escapedMessage}
this.setPropertyLayer(setProperty.layerName, setProperty.propertyName, setProperty.propertyValue); this.setPropertyLayer(setProperty.layerName, setProperty.propertyName, setProperty.propertyValue);
})); }));
this.iframeSubscriptionList.push(iframeListener.dataLayerChangeStream.subscribe(() => {
iframeListener.sendDataLayerEvent({data: this.gameMap.getMap()});
}))
} }
private setPropertyLayer(layerName: string, propertyName: string, propertyValue: string | number | boolean | undefined): void { private setPropertyLayer(layerName: string, propertyName: string, propertyValue: string | number | boolean | undefined): void {
@ -909,21 +912,21 @@ ${escapedMessage}
layer.properties = []; layer.properties = [];
layer.properties.push({name : propertyName, type : typeof propertyValue, value : propertyValue}); layer.properties.push({name : propertyName, type : typeof propertyValue, value : propertyValue});
return; return;
} }
property.value = propertyValue; property.value = propertyValue;
} }
private setLayerVisibility(layerName: string, visible: boolean): void { private setLayerVisibility(layerName: string, visible: boolean): void {
const layer = this.gameMap.findLayer(layerName); const phaserlayer = this.gameMap.findPhaserLayer(layerName);
if (layer === undefined) { if (phaserlayer === undefined) {
console.warn('Could not find layer "' + layerName + '" when calling WA.hideLayer / WA.showLayer'); console.warn('Could not find layer "' + layerName + '" when calling WA.hideLayer / WA.showLayer');
return; return;
} }
if(layer.type != "tilelayer"){ if(phaserlayer.type != "tilelayer"){
console.warn('The layer "' + layerName + '" is not a tilelayer. It can not be show/hide'); console.warn('The layer "' + layerName + '" is not a tilelayer. It can not be show/hide');
return; return;
} }
layer.phaserLayer?.setVisible(visible); phaserlayer.setVisible(visible);
this.dirty = true; this.dirty = true;
} }
@ -1131,18 +1134,15 @@ ${escapedMessage}
this.physics.disableUpdate(); this.physics.disableUpdate();
this.physicsEnabled = false; this.physicsEnabled = false;
//add collision layer //add collision layer
for (const Layer of this.gameMap.flatLayers) { for (const phaserLayer of this.gameMap.phaserLayers) {
if (Layer.type == "tilelayer") { if (phaserLayer.type == "tilelayer") {
if (Layer.phaserLayer === undefined) { this.physics.add.collider(this.CurrentPlayer, phaserLayer, (object1: GameObject, object2: GameObject) => {
throw new Error('phaserLayer of layer "' + Layer.name + '" is undefined');
}
this.physics.add.collider(this.CurrentPlayer, Layer.phaserLayer, (object1: GameObject, object2: GameObject) => {
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name) //this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
}); });
Layer.phaserLayer.setCollisionByProperty({collides: true}); phaserLayer.setCollisionByProperty({collides: true});
if (DEBUG_MODE) { if (DEBUG_MODE) {
//debug code to see the collision hitbox of the object in the top layer //debug code to see the collision hitbox of the object in the top layer
Layer.phaserLayer.renderDebug(this.add.graphics(), { phaserLayer.renderDebug(this.add.graphics(), {
tileColor: null, //non-colliding tiles tileColor: null, //non-colliding tiles
collidingTileColor: new Phaser.Display.Color(243, 134, 48, 200), // Colliding tiles, collidingTileColor: new Phaser.Display.Color(243, 134, 48, 200), // Colliding tiles,
faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Colliding face edges faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Colliding face edges

View File

@ -1,4 +1,3 @@
import type {HasMovedEvent} from "./GameManager";
import { MAX_EXTRAPOLATION_TIME } from "../../Enum/EnvironmentVariable"; import { MAX_EXTRAPOLATION_TIME } from "../../Enum/EnvironmentVariable";
import type { PositionInterface } from "../../Connexion/ConnexionModels"; import type { PositionInterface } from "../../Connexion/ConnexionModels";
import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent'; import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent';

View File

@ -14,9 +14,8 @@ function flattenGroupLayers(layers : ITiledMapLayer[], prefix : string, flatLaye
if (layer.type === 'group') { if (layer.type === 'group') {
flattenGroupLayers(layer.layers, prefix + layer.name + '/', flatLayers); flattenGroupLayers(layer.layers, prefix + layer.name + '/', flatLayers);
} else { } else {
const layerWithNewName = { ...layer }; layer.name = prefix+layer.name
layerWithNewName.name = prefix+layerWithNewName.name; flatLayers.push(layer);
flatLayers.push(layerWithNewName);
} }
} }
} }

View File

@ -13,7 +13,7 @@ import type { LayerEvent } from "./Api/Events/LayerEvent";
import type { SetPropertyEvent } from "./Api/Events/setPropertyEvent"; import type { SetPropertyEvent } from "./Api/Events/setPropertyEvent";
import { GameStateEvent, isGameStateEvent } from './Api/Events/GameStateEvent'; import { GameStateEvent, isGameStateEvent } from './Api/Events/GameStateEvent';
import { HasPlayerMovedEvent, HasPlayerMovedEventCallback, isHasPlayerMovedEvent } from './Api/Events/HasPlayerMovedEvent'; import { HasPlayerMovedEvent, HasPlayerMovedEventCallback, isHasPlayerMovedEvent } from './Api/Events/HasPlayerMovedEvent';
import { HasDataLayerChangedEvent, HasDataLayerChangedEventCallback, isHasDataLayerChangedEvent} from "./Api/Events/HasDataLayerChangedEvent"; import { DataLayerEvent, isDataLayerEvent } from "./Api/Events/DataLayerEvent";
interface WorkAdventureApi { interface WorkAdventureApi {
sendChatMessage(message: string, author: string): void; sendChatMessage(message: string, author: string): void;
@ -40,10 +40,11 @@ interface WorkAdventureApi {
getUuid(): Promise<string | undefined>; getUuid(): Promise<string | undefined>;
getRoomId(): Promise<string>; getRoomId(): Promise<string>;
getStartLayerName(): Promise<string | null>; getStartLayerName(): Promise<string | null>;
getNickName(): Promise<string | null>;
onPlayerMove(callback: (playerMovedEvent: HasPlayerMovedEvent) => void): void onPlayerMove(callback: (playerMovedEvent: HasPlayerMovedEvent) => void): void
onDataLayerChange(callback: (dataLayerChangedEvent: HasDataLayerChangedEvent) => void): void getDataLayer(): Promise<DataLayerEvent>
} }
declare global { declare global {
@ -105,7 +106,7 @@ function getGameState(): Promise<GameStateEvent> {
} }
else { else {
return new Promise<GameStateEvent>((resolver, thrower) => { return new Promise<GameStateEvent>((resolver, thrower) => {
stateResolvers.push(resolver); gameStateResolver.push(resolver);
window.parent.postMessage({ window.parent.postMessage({
type: "getState" type: "getState"
}, "*") }, "*")
@ -113,11 +114,11 @@ function getGameState(): Promise<GameStateEvent> {
} }
} }
const stateResolvers: Array<(event: GameStateEvent) => void> = [] const gameStateResolver: Array<(event: GameStateEvent) => void> = []
const dataLayerResolver: Array<(event: DataLayerEvent) => void> = []
let immutableData: GameStateEvent; let immutableData: GameStateEvent;
const callbackPlayerMoved: { [type: string]: HasPlayerMovedEventCallback | ((arg?: HasPlayerMovedEvent | never) => void) } = {} const callbackPlayerMoved: { [type: string]: HasPlayerMovedEventCallback | ((arg?: HasPlayerMovedEvent | never) => void) } = {}
const callbackDataLayerChanged: { [type: string]: HasDataLayerChangedEventCallback | ((arg?: HasDataLayerChangedEvent | never) => void) } = {}
function postToParent(content: IframeEvent<keyof IframeEventMap>) { function postToParent(content: IframeEvent<keyof IframeEventMap>) {
@ -136,14 +137,21 @@ window.WA = {
}) })
}, },
onDataLayerChange(callback: HasDataLayerChangedEventCallback): void { getDataLayer(): Promise<DataLayerEvent> {
callbackDataLayerChanged['test'] = callback; return new Promise<DataLayerEvent>((resolver, thrower) => {
postToParent({ dataLayerResolver.push(resolver);
type : "onDataLayerChange", postToParent({
data: undefined type: "getDataLayer",
data: undefined
})
}) })
}, },
getNickName() {
return getGameState().then((res) => {
return res.nickname;
})
},
getMapUrl() { getMapUrl() {
return getGameState().then((res) => { return getGameState().then((res) => {
@ -345,14 +353,16 @@ window.addEventListener('message', message => {
callback(popup); callback(popup);
} }
} else if (payload.type == "gameState" && isGameStateEvent(payloadData)) { } else if (payload.type == "gameState" && isGameStateEvent(payloadData)) {
stateResolvers.forEach(resolver => { gameStateResolver.forEach(resolver => {
resolver(payloadData); resolver(payloadData);
}) })
immutableData = payloadData; immutableData = payloadData;
} else if (payload.type == "hasPlayerMoved" && isHasPlayerMovedEvent(payloadData) && playerUuid) { } else if (payload.type == "hasPlayerMoved" && isHasPlayerMovedEvent(payloadData) && playerUuid) {
callbackPlayerMoved[playerUuid](payloadData) callbackPlayerMoved[playerUuid](payloadData)
} else if (payload.type == "hasDataLayerChanged" && isHasDataLayerChangedEvent(payloadData)) { } else if (payload.type == "dataLayer" && isDataLayerEvent(payloadData)) {
callbackDataLayerChanged['test'](payloadData) dataLayerResolver.forEach(resolver => {
resolver(payloadData);
})
} }
} }

View File

@ -1,9 +1,9 @@
WA.getMapUrl().then((map) => {console.log('mapUrl : ', map)}); /*WA.getMapUrl().then((map) => {console.log('mapUrl : ', map)});
WA.getUuid().then((uuid) => {console.log('Uuid : ',uuid)}); WA.getUuid().then((uuid) => {console.log('Uuid : ',uuid)});
WA.getRoomId().then((roomId) => console.log('roomID : ',roomId)); WA.getRoomId().then((roomId) => console.log('roomID : ',roomId));*/
WA.listenPositionPlayer(console.log);
//WA.onPlayerMove(console.log);
WA.setProperty('metadata', 'openWebsite', 'https://fr.wikipedia.org/');
WA.getDataLayer().then((data) => {console.log('data 1 : ', data)});