Creates player state and uses it to get and set values from local storage
This commit is contained in:
parent
e025c1dc8e
commit
3490daed6b
@ -10,6 +10,7 @@ export const isGameStateEvent = new tg.IsInterface()
|
|||||||
tags: tg.isArray(tg.isString),
|
tags: tg.isArray(tg.isString),
|
||||||
variables: tg.isObject,
|
variables: tg.isObject,
|
||||||
userRoomToken: tg.isUnion(tg.isString, tg.isUndefined),
|
userRoomToken: tg.isUnion(tg.isString, tg.isUndefined),
|
||||||
|
playerVariables: tg.isObject,
|
||||||
})
|
})
|
||||||
.get();
|
.get();
|
||||||
/**
|
/**
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import * as tg from "generic-type-guard";
|
|
||||||
|
|
||||||
export const isPlayerPropertyEvent = new tg.IsInterface()
|
|
||||||
.withProperties({
|
|
||||||
propertyName: tg.isString,
|
|
||||||
propertyValue: tg.isUnknown,
|
|
||||||
})
|
|
||||||
.get();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message sent from the iFrame to set player-related properties.
|
|
||||||
*/
|
|
||||||
export type PlayerPropertyEvent = tg.GuardedType<typeof isPlayerPropertyEvent>;
|
|
@ -4,6 +4,7 @@ export const isSetVariableEvent = new tg.IsInterface()
|
|||||||
.withProperties({
|
.withProperties({
|
||||||
key: tg.isString,
|
key: tg.isString,
|
||||||
value: tg.isUnknown,
|
value: tg.isUnknown,
|
||||||
|
target: tg.isSingletonStringUnion("global", "player"),
|
||||||
})
|
})
|
||||||
.get();
|
.get();
|
||||||
/**
|
/**
|
||||||
|
@ -3,7 +3,7 @@ import type { HasPlayerMovedEvent, HasPlayerMovedEventCallback } from "../Events
|
|||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { apiCallback } from "./registeredCallbacks";
|
import { apiCallback } from "./registeredCallbacks";
|
||||||
import { isHasPlayerMovedEvent } from "../Events/HasPlayerMovedEvent";
|
import { isHasPlayerMovedEvent } from "../Events/HasPlayerMovedEvent";
|
||||||
import type { PlayerPropertyEvent } from "../Events/PlayerPropertyEvent";
|
import { createState } from "./state";
|
||||||
|
|
||||||
const moveStream = new Subject<HasPlayerMovedEvent>();
|
const moveStream = new Subject<HasPlayerMovedEvent>();
|
||||||
|
|
||||||
@ -32,6 +32,8 @@ export const setUuid = (_uuid: string | undefined) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class WorkadventurePlayerCommands extends IframeApiContribution<WorkadventurePlayerCommands> {
|
export class WorkadventurePlayerCommands extends IframeApiContribution<WorkadventurePlayerCommands> {
|
||||||
|
readonly state = createState("player");
|
||||||
|
|
||||||
callbacks = [
|
callbacks = [
|
||||||
apiCallback({
|
apiCallback({
|
||||||
type: "hasPlayerMoved",
|
type: "hasPlayerMoved",
|
||||||
|
@ -8,75 +8,84 @@ import { isSetVariableEvent, SetVariableEvent } from "../Events/SetVariableEvent
|
|||||||
|
|
||||||
import type { ITiledMap } from "../../Phaser/Map/ITiledMap";
|
import type { ITiledMap } from "../../Phaser/Map/ITiledMap";
|
||||||
|
|
||||||
const setVariableResolvers = new Subject<SetVariableEvent>();
|
export class WorkadventureStateCommands extends IframeApiContribution<WorkadventureStateCommands> {
|
||||||
const variables = new Map<string, unknown>();
|
private setVariableResolvers = new Subject<SetVariableEvent>();
|
||||||
const variableSubscribers = new Map<string, Subject<unknown>>();
|
private variables = new Map<string, unknown>();
|
||||||
|
private variableSubscribers = new Map<string, Subject<unknown>>();
|
||||||
|
|
||||||
export const initVariables = (_variables: Map<string, unknown>): void => {
|
constructor(private target: "global" | "player") {
|
||||||
for (const [name, value] of _variables.entries()) {
|
super();
|
||||||
// In case the user already decided to put values in the variables (before onInit), let's make sure onInit does not override this.
|
|
||||||
if (!variables.has(name)) {
|
|
||||||
variables.set(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setVariableResolvers.subscribe((event) => {
|
this.setVariableResolvers.subscribe((event) => {
|
||||||
const oldValue = variables.get(event.key);
|
const oldValue = this.variables.get(event.key);
|
||||||
// If we are setting the same value, no need to do anything.
|
// If we are setting the same value, no need to do anything.
|
||||||
// No need to do this check since it is already performed in SharedVariablesManager
|
// No need to do this check since it is already performed in SharedVariablesManager
|
||||||
/*if (JSON.stringify(oldValue) === JSON.stringify(event.value)) {
|
/*if (JSON.stringify(oldValue) === JSON.stringify(event.value)) {
|
||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
variables.set(event.key, event.value);
|
this.variables.set(event.key, event.value);
|
||||||
const subject = variableSubscribers.get(event.key);
|
const subject = this.variableSubscribers.get(event.key);
|
||||||
if (subject !== undefined) {
|
if (subject !== undefined) {
|
||||||
subject.next(event.value);
|
subject.next(event.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export class WorkadventureStateCommands extends IframeApiContribution<WorkadventureStateCommands> {
|
|
||||||
callbacks = [
|
callbacks = [
|
||||||
apiCallback({
|
apiCallback({
|
||||||
type: "setVariable",
|
type: "setVariable",
|
||||||
typeChecker: isSetVariableEvent,
|
typeChecker: isSetVariableEvent,
|
||||||
callback: (payloadData) => {
|
callback: (payloadData) => {
|
||||||
setVariableResolvers.next(payloadData);
|
if (payloadData.target === this.target) {
|
||||||
|
this.setVariableResolvers.next(payloadData);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// TODO: see how we can remove this method from types exposed to WA.state object
|
||||||
|
initVariables(_variables: Map<string, unknown>): void {
|
||||||
|
for (const [name, value] of _variables.entries()) {
|
||||||
|
// In case the user already decided to put values in the variables (before onInit), let's make sure onInit does not override this.
|
||||||
|
if (!this.variables.has(name)) {
|
||||||
|
this.variables.set(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
saveVariable(key: string, value: unknown): Promise<void> {
|
saveVariable(key: string, value: unknown): Promise<void> {
|
||||||
variables.set(key, value);
|
this.variables.set(key, value);
|
||||||
return queryWorkadventure({
|
return queryWorkadventure({
|
||||||
type: "setVariable",
|
type: "setVariable",
|
||||||
data: {
|
data: {
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
|
target: this.target,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadVariable(key: string): unknown {
|
loadVariable(key: string): unknown {
|
||||||
return variables.get(key);
|
return this.variables.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasVariable(key: string): boolean {
|
hasVariable(key: string): boolean {
|
||||||
return variables.has(key);
|
return this.variables.has(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
onVariableChange(key: string): Observable<unknown> {
|
onVariableChange(key: string): Observable<unknown> {
|
||||||
let subject = variableSubscribers.get(key);
|
let subject = this.variableSubscribers.get(key);
|
||||||
if (subject === undefined) {
|
if (subject === undefined) {
|
||||||
subject = new Subject<unknown>();
|
subject = new Subject<unknown>();
|
||||||
variableSubscribers.set(key, subject);
|
this.variableSubscribers.set(key, subject);
|
||||||
}
|
}
|
||||||
return subject.asObservable();
|
return subject.asObservable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const proxyCommand = new Proxy(new WorkadventureStateCommands(), {
|
export function createState(target: "global" | "player"): WorkadventureStateCommands & { [key: string]: unknown } {
|
||||||
|
return new Proxy(new WorkadventureStateCommands(target), {
|
||||||
get(target: WorkadventureStateCommands, p: PropertyKey, receiver: unknown): unknown {
|
get(target: WorkadventureStateCommands, p: PropertyKey, receiver: unknown): unknown {
|
||||||
if (p in target) {
|
if (p in target) {
|
||||||
return Reflect.get(target, p, receiver);
|
return Reflect.get(target, p, receiver);
|
||||||
@ -95,6 +104,5 @@ const proxyCommand = new Proxy(new WorkadventureStateCommands(), {
|
|||||||
}
|
}
|
||||||
return target.hasVariable(p.toString());
|
return target.hasVariable(p.toString());
|
||||||
},
|
},
|
||||||
}) as WorkadventureStateCommands & { [key: string]: unknown };
|
}) as WorkadventureStateCommands & { [key: string]: unknown };
|
||||||
|
}
|
||||||
export default proxyCommand;
|
|
||||||
|
@ -220,12 +220,26 @@ class LocalUserStore {
|
|||||||
const cameraSetupValues = localStorage.getItem(cameraSetup);
|
const cameraSetupValues = localStorage.getItem(cameraSetup);
|
||||||
return cameraSetupValues != undefined ? JSON.parse(cameraSetupValues) : undefined;
|
return cameraSetupValues != undefined ? JSON.parse(cameraSetupValues) : undefined;
|
||||||
}
|
}
|
||||||
getUserProperty(name: string): string | null {
|
|
||||||
return localStorage.getItem(userProperties + "_" + name);
|
getAllUserProperties(): Map<string, unknown> {
|
||||||
|
const result = new Map<string, string>();
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if (key) {
|
||||||
|
if (key.startsWith(userProperties + "_")) {
|
||||||
|
const value = localStorage.getItem(key);
|
||||||
|
if (value) {
|
||||||
|
const userKey = key.substr((userProperties + "_").length);
|
||||||
|
result.set(userKey, JSON.parse(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserProperty(name: string, value: string): void {
|
setUserProperty(name: string, value: unknown): void {
|
||||||
localStorage.setItem(userProperties + "_" + name, value);
|
localStorage.setItem(userProperties + "_" + name, JSON.stringify(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,19 +1223,6 @@ ${escapedMessage}
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO : move Player Properties related-code
|
|
||||||
iframeListener.registerAnswerer("setPlayerProperty", (event) => {
|
|
||||||
localUserStore.setUserProperty(event.propertyName, event.propertyValue as string);
|
|
||||||
});
|
|
||||||
|
|
||||||
iframeListener.registerAnswerer("getPlayerProperty", (event) => {
|
|
||||||
return {
|
|
||||||
propertyName: event,
|
|
||||||
propertyValue: localUserStore.getUserProperty(event),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
//END TODO
|
|
||||||
|
|
||||||
iframeListener.registerAnswerer("getState", async () => {
|
iframeListener.registerAnswerer("getState", async () => {
|
||||||
// The sharedVariablesManager is not instantiated before the connection is established. So we need to wait
|
// The sharedVariablesManager is not instantiated before the connection is established. So we need to wait
|
||||||
// for the connection to send back the answer.
|
// for the connection to send back the answer.
|
||||||
@ -1248,6 +1235,7 @@ ${escapedMessage}
|
|||||||
roomId: this.roomUrl,
|
roomId: this.roomUrl,
|
||||||
tags: this.connection ? this.connection.getAllTags() : [],
|
tags: this.connection ? this.connection.getAllTags() : [],
|
||||||
variables: this.sharedVariablesManager.variables,
|
variables: this.sharedVariablesManager.variables,
|
||||||
|
playerVariables: localUserStore.getAllUserProperties(),
|
||||||
userRoomToken: this.connection ? this.connection.userRoomToken : "",
|
userRoomToken: this.connection ? this.connection.userRoomToken : "",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -1338,6 +1326,22 @@ ${escapedMessage}
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
iframeListener.registerAnswerer("setVariable", (event, source) => {
|
||||||
|
switch (event.target) {
|
||||||
|
case "global": {
|
||||||
|
this.sharedVariablesManager.setVariable(event, source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "player": {
|
||||||
|
localUserStore.setUserProperty(event.key, event.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
const _exhaustiveCheck: never = event.target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
iframeListener.registerAnswerer("removeActionMessage", (message) => {
|
iframeListener.registerAnswerer("removeActionMessage", (message) => {
|
||||||
layoutManagerActionStore.removeAction(message.uuid);
|
layoutManagerActionStore.removeAction(message.uuid);
|
||||||
});
|
});
|
||||||
@ -1480,6 +1484,7 @@ ${escapedMessage}
|
|||||||
iframeListener.unregisterAnswerer("openCoWebsite");
|
iframeListener.unregisterAnswerer("openCoWebsite");
|
||||||
iframeListener.unregisterAnswerer("getCoWebsites");
|
iframeListener.unregisterAnswerer("getCoWebsites");
|
||||||
iframeListener.unregisterAnswerer("setPlayerOutline");
|
iframeListener.unregisterAnswerer("setPlayerOutline");
|
||||||
|
iframeListener.unregisterAnswerer("setVariable");
|
||||||
this.sharedVariablesManager?.close();
|
this.sharedVariablesManager?.close();
|
||||||
this.embeddedWebsiteManager?.close();
|
this.embeddedWebsiteManager?.close();
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { iframeListener } from "../../Api/IframeListener";
|
|||||||
import type { GameMap } from "./GameMap";
|
import type { GameMap } from "./GameMap";
|
||||||
import type { ITiledMapLayer, ITiledMapObject } from "../Map/ITiledMap";
|
import type { ITiledMapLayer, ITiledMapObject } from "../Map/ITiledMap";
|
||||||
import { GameMapProperties } from "./GameMapProperties";
|
import { GameMapProperties } from "./GameMapProperties";
|
||||||
|
import type { SetVariableEvent } from "../../Api/Events/SetVariableEvent";
|
||||||
|
|
||||||
interface Variable {
|
interface Variable {
|
||||||
defaultValue: unknown;
|
defaultValue: unknown;
|
||||||
@ -48,11 +49,12 @@ export class SharedVariablesManager {
|
|||||||
iframeListener.setVariable({
|
iframeListener.setVariable({
|
||||||
key: name,
|
key: name,
|
||||||
value: value,
|
value: value,
|
||||||
|
target: "global",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// When a variable is modified from an iFrame
|
public setVariable(event: SetVariableEvent, source: MessageEventSource | null): void {
|
||||||
iframeListener.registerAnswerer("setVariable", (event, source) => {
|
|
||||||
const key = event.key;
|
const key = event.key;
|
||||||
|
|
||||||
const object = this.variableObjects.get(key);
|
const object = this.variableObjects.get(key);
|
||||||
@ -92,7 +94,6 @@ export class SharedVariablesManager {
|
|||||||
|
|
||||||
// Dispatch to other iframes
|
// Dispatch to other iframes
|
||||||
iframeListener.dispatchVariableToOtherIframes(key, event.value, source);
|
iframeListener.dispatchVariableToOtherIframes(key, event.value, source);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static findVariablesInMap(gameMap: GameMap): Map<string, Variable> {
|
private static findVariablesInMap(gameMap: GameMap): Map<string, Variable> {
|
||||||
@ -164,10 +165,6 @@ export class SharedVariablesManager {
|
|||||||
return variable;
|
return variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public close(): void {
|
|
||||||
iframeListener.unregisterAnswerer("setVariable");
|
|
||||||
}
|
|
||||||
|
|
||||||
get variables(): Map<string, unknown> {
|
get variables(): Map<string, unknown> {
|
||||||
return this._variables;
|
return this._variables;
|
||||||
}
|
}
|
||||||
|
@ -14,25 +14,28 @@ import controls from "./Api/iframe/controls";
|
|||||||
import ui from "./Api/iframe/ui";
|
import ui from "./Api/iframe/ui";
|
||||||
import sound from "./Api/iframe/sound";
|
import sound from "./Api/iframe/sound";
|
||||||
import room, { setMapURL, setRoomId } from "./Api/iframe/room";
|
import room, { setMapURL, setRoomId } from "./Api/iframe/room";
|
||||||
import state, { initVariables } from "./Api/iframe/state";
|
import { createState } from "./Api/iframe/state";
|
||||||
import player, { setPlayerName, setTags, setUserRoomToken, setUuid } from "./Api/iframe/player";
|
import player, { setPlayerName, setTags, setUserRoomToken, setUuid } from "./Api/iframe/player";
|
||||||
import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
|
import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
|
||||||
import type { Popup } from "./Api/iframe/Ui/Popup";
|
import type { Popup } from "./Api/iframe/Ui/Popup";
|
||||||
import type { Sound } from "./Api/iframe/Sound/Sound";
|
import type { Sound } from "./Api/iframe/Sound/Sound";
|
||||||
import { answerPromises, queryWorkadventure } from "./Api/iframe/IframeApiContribution";
|
import { answerPromises, queryWorkadventure } from "./Api/iframe/IframeApiContribution";
|
||||||
|
|
||||||
|
const globalState = createState("global");
|
||||||
|
|
||||||
// Notify WorkAdventure that we are ready to receive data
|
// Notify WorkAdventure that we are ready to receive data
|
||||||
const initPromise = queryWorkadventure({
|
const initPromise = queryWorkadventure({
|
||||||
type: "getState",
|
type: "getState",
|
||||||
data: undefined,
|
data: undefined,
|
||||||
}).then((state) => {
|
}).then((gameState) => {
|
||||||
setPlayerName(state.nickname);
|
setPlayerName(gameState.nickname);
|
||||||
setRoomId(state.roomId);
|
setRoomId(gameState.roomId);
|
||||||
setMapURL(state.mapUrl);
|
setMapURL(gameState.mapUrl);
|
||||||
setTags(state.tags);
|
setTags(gameState.tags);
|
||||||
setUuid(state.uuid);
|
setUuid(gameState.uuid);
|
||||||
initVariables(state.variables as Map<string, unknown>);
|
globalState.initVariables(gameState.variables as Map<string, unknown>);
|
||||||
setUserRoomToken(state.userRoomToken);
|
player.state.initVariables(gameState.playerVariables as Map<string, unknown>);
|
||||||
|
setUserRoomToken(gameState.userRoomToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
const wa = {
|
const wa = {
|
||||||
@ -43,7 +46,7 @@ const wa = {
|
|||||||
sound,
|
sound,
|
||||||
room,
|
room,
|
||||||
player,
|
player,
|
||||||
state,
|
state: globalState,
|
||||||
|
|
||||||
onInit(): Promise<void> {
|
onInit(): Promise<void> {
|
||||||
return initPromise;
|
return initPromise;
|
||||||
@ -225,7 +228,5 @@ window.addEventListener(
|
|||||||
callback?.callback(payloadData);
|
callback?.callback(payloadData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user