Zod EVERYWHERE (#2027)

* Zod EVERYWHERE

* Add no-unused-vars rule to eslint in front

* Add no-unused-vars rule to eslint in pusher

* Add no-unused-vars rule to eslint in back

* Remove unused PlayerTexture guards

* Fix data providing on room connection

Co-authored-by: Alexis Faizeau <a.faizeau@workadventu.re>
This commit is contained in:
Alexis Faizeau
2022-04-12 14:21:19 +02:00
committed by GitHub
parent 41e62051d4
commit d1e8243c47
161 changed files with 1131 additions and 1248 deletions
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isActionsMenuActionClickedEvent = new tg.IsInterface()
.withProperties({
id: tg.isNumber,
actionName: tg.isString,
})
.get();
export const isActionsMenuActionClickedEvent = z.object({
id: z.number(),
actionName: z.string(),
});
export type ActionsMenuActionClickedEvent = tg.GuardedType<typeof isActionsMenuActionClickedEvent>;
export type ActionsMenuActionClickedEvent = z.infer<typeof isActionsMenuActionClickedEvent>;
export type ActionsMenuActionClickedEventCallback = (event: ActionsMenuActionClickedEvent) => void;
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isAddActionsMenuKeyToRemotePlayerEvent = new tg.IsInterface()
.withProperties({
id: tg.isNumber,
actionKey: tg.isString,
})
.get();
export const isAddActionsMenuKeyToRemotePlayerEvent = z.object({
id: z.number(),
actionKey: z.string(),
});
export type AddActionsMenuKeyToRemotePlayerEvent = tg.GuardedType<typeof isAddActionsMenuKeyToRemotePlayerEvent>;
export type AddActionsMenuKeyToRemotePlayerEvent = z.infer<typeof isAddActionsMenuKeyToRemotePlayerEvent>;
export type AddActionsMenuKeyToRemotePlayerEventCallback = (event: AddActionsMenuKeyToRemotePlayerEvent) => void;
+7 -8
View File
@@ -1,12 +1,11 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isButtonClickedEvent = z.object({
popupId: z.number(),
buttonId: z.number(),
});
export const isButtonClickedEvent = new tg.IsInterface()
.withProperties({
popupId: tg.isNumber,
buttonId: tg.isNumber,
})
.get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property.
*/
export type ButtonClickedEvent = tg.GuardedType<typeof isButtonClickedEvent>;
export type ButtonClickedEvent = z.infer<typeof isButtonClickedEvent>;
@@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isCameraFollowPlayerEvent = z.object({
smooth: z.boolean(),
});
export const isCameraFollowPlayerEvent = new tg.IsInterface()
.withProperties({
smooth: tg.isBoolean,
})
.get();
/**
* A message sent from the iFrame to the game to make the camera follow player.
*/
export type CameraFollowPlayerEvent = tg.GuardedType<typeof isCameraFollowPlayerEvent>;
export type CameraFollowPlayerEvent = z.infer<typeof isCameraFollowPlayerEvent>;
+11 -12
View File
@@ -1,16 +1,15 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isCameraSetEvent = z.object({
x: z.number(),
y: z.number(),
width: z.optional(z.number()),
height: z.optional(z.number()),
lock: z.boolean(),
smooth: z.boolean(),
});
export const isCameraSetEvent = new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
width: tg.isOptional(tg.isNumber),
height: tg.isOptional(tg.isNumber),
lock: tg.isBoolean,
smooth: tg.isBoolean,
})
.get();
/**
* A message sent from the iFrame to the game to change the camera position.
*/
export type CameraSetEvent = tg.GuardedType<typeof isCameraSetEvent>;
export type CameraSetEvent = z.infer<typeof isCameraSetEvent>;
+6 -7
View File
@@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isChangeLayerEvent = z.object({
name: z.string(),
});
export const isChangeLayerEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
})
.get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a layer.
*/
export type ChangeLayerEvent = tg.GuardedType<typeof isChangeLayerEvent>;
export type ChangeLayerEvent = z.infer<typeof isChangeLayerEvent>;
+6 -7
View File
@@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isChangeZoneEvent = z.object({
name: z.string(),
});
export const isChangeZoneEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
})
.get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a zone.
*/
export type ChangeZoneEvent = tg.GuardedType<typeof isChangeZoneEvent>;
export type ChangeZoneEvent = z.infer<typeof isChangeZoneEvent>;
+7 -8
View File
@@ -1,12 +1,11 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isChatEvent = z.object({
message: z.string(),
author: z.string(),
});
export const isChatEvent = new tg.IsInterface()
.withProperties({
message: tg.isString,
author: tg.isString,
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type ChatEvent = tg.GuardedType<typeof isChatEvent>;
export type ChatEvent = z.infer<typeof isChatEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isCloseCoWebsite = new tg.IsInterface()
.withProperties({
id: tg.isOptional(tg.isString),
})
.get();
export const isCloseCoWebsite = z.object({
id: z.optional(z.string()),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type CloseCoWebsiteEvent = tg.GuardedType<typeof isCloseCoWebsite>;
export type CloseCoWebsiteEvent = z.infer<typeof isCloseCoWebsite>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isClosePopupEvent = new tg.IsInterface()
.withProperties({
popupId: tg.isNumber,
})
.get();
export const isClosePopupEvent = z.object({
popupId: z.number(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type ClosePopupEvent = tg.GuardedType<typeof isClosePopupEvent>;
export type ClosePopupEvent = z.infer<typeof isClosePopupEvent>;
+8 -9
View File
@@ -1,13 +1,12 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isColorEvent = z.object({
red: z.number(),
green: z.number(),
blue: z.number(),
});
export const isColorEvent = new tg.IsInterface()
.withProperties({
red: tg.isNumber,
green: tg.isNumber,
blue: tg.isNumber,
})
.get();
/**
* A message sent from the iFrame to the game to dynamically set the outline of the player.
*/
export type ColorEvent = tg.GuardedType<typeof isColorEvent>;
export type ColorEvent = z.infer<typeof isColorEvent>;
+35 -44
View File
@@ -1,52 +1,43 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isRectangle = new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
width: tg.isNumber,
height: tg.isNumber,
})
.get();
export const isRectangle = z.object({
x: z.number(),
y: z.number(),
width: z.number(),
height: z.number(),
});
export const isEmbeddedWebsiteEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
})
.withOptionalProperties({
url: tg.isString,
visible: tg.isBoolean,
allowApi: tg.isBoolean,
allow: tg.isString,
x: tg.isNumber,
y: tg.isNumber,
width: tg.isNumber,
height: tg.isNumber,
origin: tg.isSingletonStringUnion("player", "map"),
scale: tg.isNumber,
})
.get();
// TODO: make a variation that is all optional (except for the name)
export type Rectangle = z.infer<typeof isRectangle>;
export const isCreateEmbeddedWebsiteEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
url: tg.isString,
position: isRectangle,
})
.withOptionalProperties({
visible: tg.isBoolean,
allowApi: tg.isBoolean,
allow: tg.isString,
origin: tg.isSingletonStringUnion("player", "map"),
scale: tg.isNumber,
})
.get();
export const isEmbeddedWebsiteEvent = z.object({
name: z.string(),
url: z.optional(z.string()),
visible: z.optional(z.boolean()),
allowApi: z.optional(z.boolean()),
allow: z.optional(z.string()),
x: z.optional(z.number()),
y: z.optional(z.number()),
width: z.optional(z.number()),
height: z.optional(z.number()),
origin: z.optional(z.enum(["player", "map"])),
scale: z.optional(z.number()),
});
/**
* A message sent from the iFrame to the game to modify an embedded website
*/
export type ModifyEmbeddedWebsiteEvent = tg.GuardedType<typeof isEmbeddedWebsiteEvent>;
export type ModifyEmbeddedWebsiteEvent = z.infer<typeof isEmbeddedWebsiteEvent>;
export type CreateEmbeddedWebsiteEvent = tg.GuardedType<typeof isCreateEmbeddedWebsiteEvent>;
// TODO: make a variation that is all optional (except for the name)
export type Rectangle = tg.GuardedType<typeof isRectangle>;
export const isCreateEmbeddedWebsiteEvent = z.object({
name: z.string(),
url: z.string(),
position: isRectangle,
visible: z.optional(z.boolean()),
allowApi: z.optional(z.boolean()),
allow: z.optional(z.string()),
origin: z.optional(z.enum(["player", "map"])),
scale: z.optional(z.number()),
});
export type CreateEmbeddedWebsiteEvent = z.infer<typeof isCreateEmbeddedWebsiteEvent>;
+6 -7
View File
@@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isEnterLeaveEvent = z.object({
name: z.string(),
});
export const isEnterLeaveEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
})
.get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property.
*/
export type EnterLeaveEvent = tg.GuardedType<typeof isEnterLeaveEvent>;
export type EnterLeaveEvent = z.infer<typeof isEnterLeaveEvent>;
+15 -16
View File
@@ -1,20 +1,19 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isGameStateEvent = z.object({
roomId: z.string(),
mapUrl: z.string(),
nickname: z.string(),
language: z.optional(z.string()),
uuid: z.optional(z.string()),
startLayerName: z.optional(z.string()),
tags: z.array(z.string()),
variables: z.unknown(), // Todo : Typing
playerVariables: z.unknown(), // Todo : Typing
userRoomToken: z.optional(z.string()),
});
export const isGameStateEvent = new tg.IsInterface()
.withProperties({
roomId: tg.isString,
mapUrl: tg.isString,
nickname: tg.isString,
language: tg.isUnion(tg.isString, tg.isUndefined),
uuid: tg.isUnion(tg.isString, tg.isUndefined),
startLayerName: tg.isUnion(tg.isString, tg.isNull),
tags: tg.isArray(tg.isString),
variables: tg.isObject,
playerVariables: tg.isObject,
userRoomToken: tg.isUnion(tg.isString, tg.isUndefined),
})
.get();
/**
* A message sent from the game to the iFrame when the gameState is received by the script
*/
export type GameStateEvent = tg.GuardedType<typeof isGameStateEvent>;
export type GameStateEvent = z.infer<typeof isGameStateEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isGoToPageEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
})
.get();
export const isGoToPageEvent = z.object({
url: z.string(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type GoToPageEvent = tg.GuardedType<typeof isGoToPageEvent>;
export type GoToPageEvent = z.infer<typeof isGoToPageEvent>;
+10 -12
View File
@@ -1,19 +1,17 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isHasPlayerMovedEvent = new tg.IsInterface()
.withProperties({
direction: tg.isElementOf("right", "left", "up", "down"),
moving: tg.isBoolean,
x: tg.isNumber,
y: tg.isNumber,
oldX: tg.isOptional(tg.isNumber),
oldY: tg.isOptional(tg.isNumber),
})
.get();
export const isHasPlayerMovedEvent = z.object({
direction: z.enum(["right", "left", "up", "down"]),
moving: z.boolean(),
x: z.number(),
y: z.number(),
oldX: z.optional(z.number()),
oldY: z.optional(z.number()),
});
/**
* A message sent from the game to the iFrame to notify a movement from the current player.
*/
export type HasPlayerMovedEvent = tg.GuardedType<typeof isHasPlayerMovedEvent>;
export type HasPlayerMovedEvent = z.infer<typeof isHasPlayerMovedEvent>;
export type HasPlayerMovedEventCallback = (event: HasPlayerMovedEvent) => void;
+163 -85
View File
@@ -1,46 +1,44 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
import type { ButtonClickedEvent } from "./ButtonClickedEvent";
import type { ChatEvent } from "./ChatEvent";
import type { ClosePopupEvent } from "./ClosePopupEvent";
import { isChatEvent } from "./ChatEvent";
import { isClosePopupEvent } from "./ClosePopupEvent";
import type { EnterLeaveEvent } from "./EnterLeaveEvent";
import type { GoToPageEvent } from "./GoToPageEvent";
import type { LoadPageEvent } from "./LoadPageEvent";
import { isGoToPageEvent } from "./GoToPageEvent";
import { isLoadPageEvent } from "./LoadPageEvent";
import { isCoWebsite, isOpenCoWebsiteEvent } from "./OpenCoWebsiteEvent";
import type { OpenPopupEvent } from "./OpenPopupEvent";
import type { OpenTabEvent } from "./OpenTabEvent";
import { isOpenPopupEvent } from "./OpenPopupEvent";
import { isOpenTabEvent } from "./OpenTabEvent";
import type { UserInputChatEvent } from "./UserInputChatEvent";
import type { LayerEvent } from "./LayerEvent";
import type { SetPropertyEvent } from "./setPropertyEvent";
import type { LoadSoundEvent } from "./LoadSoundEvent";
import type { PlaySoundEvent } from "./PlaySoundEvent";
import type { StopSoundEvent } from "./StopSoundEvent";
import { isLayerEvent } from "./LayerEvent";
import { isSetPropertyEvent } from "./setPropertyEvent";
import { isLoadSoundEvent } from "./LoadSoundEvent";
import { isPlaySoundEvent } from "./PlaySoundEvent";
import { isStopSoundEvent } from "./StopSoundEvent";
import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent";
import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent";
import type { SetTilesEvent } from "./SetTilesEvent";
import { isSetTilesEvent } from "./SetTilesEvent";
import type { SetVariableEvent } from "./SetVariableEvent";
import { isGameStateEvent } from "./GameStateEvent";
import { isMapDataEvent } from "./MapDataEvent";
import { isSetVariableEvent } from "./SetVariableEvent";
import type { EmbeddedWebsite } from "../iframe/Room/EmbeddedWebsite";
import { isCreateEmbeddedWebsiteEvent } from "./EmbeddedWebsiteEvent";
import type { LoadTilesetEvent } from "./LoadTilesetEvent";
import { isCreateEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./EmbeddedWebsiteEvent";
import { isLoadTilesetEvent } from "./LoadTilesetEvent";
import type { MessageReferenceEvent } from "./ui/TriggerActionMessageEvent";
import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent";
import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent";
import { isMenuRegisterEvent, isUnregisterMenuEvent } from "./ui/MenuRegisterEvent";
import type { ChangeLayerEvent } from "./ChangeLayerEvent";
import { isPlayerPosition } from "./PlayerPosition";
import type { WasCameraUpdatedEvent } from "./WasCameraUpdatedEvent";
import type { ChangeZoneEvent } from "./ChangeZoneEvent";
import type { CameraSetEvent } from "./CameraSetEvent";
import type { CameraFollowPlayerEvent } from "./CameraFollowPlayerEvent";
import { isCameraSetEvent } from "./CameraSetEvent";
import { isCameraFollowPlayerEvent } from "./CameraFollowPlayerEvent";
import { isColorEvent } from "./ColorEvent";
import { isMovePlayerToEventConfig } from "./MovePlayerToEvent";
import { isMovePlayerToEventAnswer } from "./MovePlayerToEventAnswer";
import type { RemotePlayerClickedEvent } from "./RemotePlayerClickedEvent";
import type { AddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRemotePlayerEvent";
import { isAddActionsMenuKeyToRemotePlayerEvent } from "./AddActionsMenuKeyToRemotePlayerEvent";
import type { ActionsMenuActionClickedEvent } from "./ActionsMenuActionClickedEvent";
import type { RemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent";
import { isRemoveActionsMenuKeyFromRemotePlayerEvent } from "./RemoveActionsMenuKeyFromRemotePlayerEvent";
export interface TypedMessageEvent<T> extends MessageEvent {
data: T;
@@ -49,45 +47,114 @@ export interface TypedMessageEvent<T> extends MessageEvent {
/**
* List event types sent from an iFrame to WorkAdventure
*/
export type IframeEventMap = {
addActionsMenuKeyToRemotePlayer: AddActionsMenuKeyToRemotePlayerEvent;
removeActionsMenuKeyFromRemotePlayer: RemoveActionsMenuKeyFromRemotePlayerEvent;
loadPage: LoadPageEvent;
chat: ChatEvent;
cameraFollowPlayer: CameraFollowPlayerEvent;
cameraSet: CameraSetEvent;
openPopup: OpenPopupEvent;
closePopup: ClosePopupEvent;
openTab: OpenTabEvent;
goToPage: GoToPageEvent;
disablePlayerControls: null;
restorePlayerControls: null;
displayBubble: null;
removeBubble: null;
onPlayerMove: undefined;
onOpenActionMenu: undefined;
onCameraUpdate: undefined;
showLayer: LayerEvent;
hideLayer: LayerEvent;
setProperty: SetPropertyEvent;
loadSound: LoadSoundEvent;
playSound: PlaySoundEvent;
stopSound: StopSoundEvent;
getState: undefined;
loadTileset: LoadTilesetEvent;
registerMenu: MenuRegisterEvent;
unregisterMenu: UnregisterMenuEvent;
setTiles: SetTilesEvent;
modifyEmbeddedWebsite: Partial<EmbeddedWebsite>; // Note: name should be compulsory in fact
};
export interface IframeEvent<T extends keyof IframeEventMap> {
type: T;
data: IframeEventMap[T];
}
export const isIframeEventWrapper = z.union([
z.object({
type: z.literal("addActionsMenuKeyToRemotePlayer"),
data: isAddActionsMenuKeyToRemotePlayerEvent,
}),
z.object({
type: z.literal("removeActionsMenuKeyFromRemotePlayer"),
data: isRemoveActionsMenuKeyFromRemotePlayerEvent,
}),
z.object({
type: z.literal("loadPage"),
data: isLoadPageEvent,
}),
z.object({
type: z.literal("chat"),
data: isChatEvent,
}),
z.object({
type: z.literal("cameraFollowPlayer"),
data: isCameraFollowPlayerEvent,
}),
z.object({
type: z.literal("cameraSet"),
data: isCameraSetEvent,
}),
z.object({
type: z.literal("openPopup"),
data: isOpenPopupEvent,
}),
z.object({
type: z.literal("closePopup"),
data: isClosePopupEvent,
}),
z.object({
type: z.literal("openTab"),
data: isOpenTabEvent,
}),
z.object({
type: z.literal("goToPage"),
data: isGoToPageEvent,
}),
z.object({
type: z.literal("disablePlayerControls"),
data: z.undefined(),
}),
z.object({
type: z.literal("restorePlayerControls"),
data: z.undefined(),
}),
z.object({
type: z.literal("displayBubble"),
data: z.undefined(),
}),
z.object({
type: z.literal("removeBubble"),
data: z.undefined(),
}),
z.object({
type: z.literal("onPlayerMove"),
data: z.undefined(),
}),
z.object({
type: z.literal("onCameraUpdate"),
data: z.undefined(),
}),
z.object({
type: z.literal("showLayer"),
data: isLayerEvent,
}),
z.object({
type: z.literal("hideLayer"),
data: isLayerEvent,
}),
z.object({
type: z.literal("setProperty"),
data: isSetPropertyEvent,
}),
z.object({
type: z.literal("loadSound"),
data: isLoadSoundEvent,
}),
z.object({
type: z.literal("playSound"),
data: isPlaySoundEvent,
}),
z.object({
type: z.literal("stopSound"),
data: isStopSoundEvent,
}),
z.object({
type: z.literal("registerMenu"),
data: isMenuRegisterEvent,
}),
z.object({
type: z.literal("unregisterMenu"),
data: isUnregisterMenuEvent,
}),
z.object({
type: z.literal("setTiles"),
data: isSetTilesEvent,
}),
z.object({
type: z.literal("modifyEmbeddedWebsite"),
data: isEmbeddedWebsiteEvent,
}),
]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeEventWrapper = (event: any): event is IframeEvent<keyof IframeEventMap> =>
typeof event.type === "string";
export type IframeEvent = z.infer<typeof isIframeEventWrapper>;
export interface IframeResponseEventMap {
userInputChat: UserInputChatEvent;
@@ -116,73 +183,78 @@ export const isIframeResponseEventWrapper = (event: {
type?: string;
}): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === "string";
export const isLookingLikeIframeEventWrapper = z.object({
type: z.string(),
data: z.unknown().optional(),
});
/**
* List event types sent from an iFrame to WorkAdventure that expect a unique answer from WorkAdventure along the type for the answer from WorkAdventure to the iFrame.
* Types are defined using Type guards that will actually bused to enforce and check types.
*/
export const iframeQueryMapTypeGuards = {
getState: {
query: tg.isUndefined,
query: z.undefined(),
answer: isGameStateEvent,
},
getMapData: {
query: tg.isUndefined,
query: z.undefined(),
answer: isMapDataEvent,
},
setVariable: {
query: isSetVariableEvent,
answer: tg.isUndefined,
answer: z.undefined(),
},
loadTileset: {
query: isLoadTilesetEvent,
answer: tg.isNumber,
answer: z.number(),
},
openCoWebsite: {
query: isOpenCoWebsiteEvent,
answer: isCoWebsite,
},
getCoWebsites: {
query: tg.isUndefined,
answer: tg.isArray(isCoWebsite),
query: z.undefined(),
answer: z.array(isCoWebsite),
},
closeCoWebsite: {
query: tg.isString,
answer: tg.isUndefined,
query: z.string(),
answer: z.undefined(),
},
closeCoWebsites: {
query: tg.isUndefined,
answer: tg.isUndefined,
query: z.undefined(),
answer: z.undefined(),
},
triggerActionMessage: {
query: isTriggerActionMessageEvent,
answer: tg.isUndefined,
answer: z.undefined(),
},
removeActionMessage: {
query: isMessageReferenceEvent,
answer: tg.isUndefined,
answer: z.undefined(),
},
getEmbeddedWebsite: {
query: tg.isString,
query: z.string(),
answer: isCreateEmbeddedWebsiteEvent,
},
deleteEmbeddedWebsite: {
query: tg.isString,
answer: tg.isUndefined,
query: z.string(),
answer: z.undefined(),
},
createEmbeddedWebsite: {
query: isCreateEmbeddedWebsiteEvent,
answer: tg.isUndefined,
answer: z.undefined(),
},
setPlayerOutline: {
query: isColorEvent,
answer: tg.isUndefined,
answer: z.undefined(),
},
removePlayerOutline: {
query: tg.isUndefined,
answer: tg.isUndefined,
query: z.undefined(),
answer: z.undefined(),
},
getPlayerPosition: {
query: tg.isUndefined,
query: z.undefined(),
answer: isPlayerPosition,
},
movePlayerTo: {
@@ -191,14 +263,13 @@ export const iframeQueryMapTypeGuards = {
},
};
type GuardedType<T> = T extends (x: unknown) => x is infer T ? T : never;
type IframeQueryMapTypeGuardsType = typeof iframeQueryMapTypeGuards;
type UnknownToVoid<T> = undefined extends T ? void : T;
export type IframeQueryMap = {
[key in keyof IframeQueryMapTypeGuardsType]: {
query: GuardedType<IframeQueryMapTypeGuardsType[key]["query"]>;
answer: UnknownToVoid<GuardedType<IframeQueryMapTypeGuardsType[key]["answer"]>>;
query: z.infer<typeof iframeQueryMapTypeGuards[key]["query"]>;
answer: UnknownToVoid<z.infer<typeof iframeQueryMapTypeGuards[key]["answer"]>>;
};
};
@@ -226,11 +297,18 @@ export const isIframeQuery = (event: any): event is IframeQuery<keyof IframeQuer
return false;
}
const result = iframeQueryMapTypeGuards[type].query(event.data);
if (!result) {
try {
iframeQueryMapTypeGuards[type].query.parse(event.data);
} catch (err) {
if (err instanceof z.ZodError) {
console.error(err.issues);
}
console.warn('Received a query with type "' + type + '" but the payload is invalid.');
return false;
}
return result;
return true;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
+6 -7
View File
@@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isLayerEvent = z.object({
name: z.string(),
});
export const isLayerEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
})
.get();
/**
* A message sent from the iFrame to the game to show/hide a layer.
*/
export type LayerEvent = tg.GuardedType<typeof isLayerEvent>;
export type LayerEvent = z.infer<typeof isLayerEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isLoadPageEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
})
.get();
export const isLoadPageEvent = z.object({
url: z.string(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type LoadPageEvent = tg.GuardedType<typeof isLoadPageEvent>;
export type LoadPageEvent = z.infer<typeof isLoadPageEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isLoadSoundEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
})
.get();
export const isLoadSoundEvent = z.object({
url: z.string(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type LoadSoundEvent = tg.GuardedType<typeof isLoadSoundEvent>;
export type LoadSoundEvent = z.infer<typeof isLoadSoundEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isLoadTilesetEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
})
.get();
export const isLoadTilesetEvent = z.object({
url: z.string(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type LoadTilesetEvent = tg.GuardedType<typeof isLoadTilesetEvent>;
export type LoadTilesetEvent = z.infer<typeof isLoadTilesetEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isMapDataEvent = new tg.IsInterface()
.withProperties({
data: tg.isObject,
})
.get();
export const isMapDataEvent = z.object({
data: z.unknown(), // Todo : Typing
});
/**
* 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 MapDataEvent = tg.GuardedType<typeof isMapDataEvent>;
export type MapDataEvent = z.infer<typeof isMapDataEvent>;
+7 -9
View File
@@ -1,11 +1,9 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isMovePlayerToEventConfig = new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
speed: tg.isOptional(tg.isNumber),
})
.get();
export const isMovePlayerToEventConfig = z.object({
x: z.number(),
y: z.number(),
speed: z.optional(z.number()),
});
export type MovePlayerToEvent = tg.GuardedType<typeof isMovePlayerToEventConfig>;
export type MovePlayerToEvent = z.infer<typeof isMovePlayerToEventConfig>;
@@ -1,11 +1,9 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isMovePlayerToEventAnswer = new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
cancelled: tg.isBoolean,
})
.get();
export const isMovePlayerToEventAnswer = z.object({
x: z.number(),
y: z.number(),
cancelled: z.boolean(),
});
export type MovePlayerToEventAnswer = tg.GuardedType<typeof isMovePlayerToEventAnswer>;
export type ActionsMenuActionClickedEvent = z.infer<typeof isMovePlayerToEventAnswer>;
+14 -18
View File
@@ -1,24 +1,20 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isOpenCoWebsiteEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
allowApi: tg.isOptional(tg.isBoolean),
allowPolicy: tg.isOptional(tg.isString),
widthPercent: tg.isOptional(tg.isNumber),
position: tg.isOptional(tg.isNumber),
closable: tg.isOptional(tg.isBoolean),
lazy: tg.isOptional(tg.isBoolean),
})
.get();
export const isOpenCoWebsiteEvent = z.object({
url: z.string(),
allowApi: z.optional(z.boolean()),
allowPolicy: z.optional(z.string()),
widthPercent: z.optional(z.number()),
position: z.optional(z.number()),
closable: z.optional(z.boolean()),
lazy: z.optional(z.boolean()),
});
export const isCoWebsite = new tg.IsInterface()
.withProperties({
id: tg.isString,
})
.get();
export const isCoWebsite = z.object({
id: z.string(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenCoWebsiteEvent = tg.GuardedType<typeof isOpenCoWebsiteEvent>;
export type OpenCoWebsiteEvent = z.infer<typeof isOpenCoWebsiteEvent>;
+12 -16
View File
@@ -1,22 +1,18 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
const isButtonDescriptor = new tg.IsInterface()
.withProperties({
label: tg.isString,
className: tg.isOptional(tg.isString),
})
.get();
export const isButtonDescriptor = z.object({
label: z.string(),
className: z.optional(z.string()),
});
export const isOpenPopupEvent = new tg.IsInterface()
.withProperties({
popupId: tg.isNumber,
targetObject: tg.isString,
message: tg.isString,
buttons: tg.isArray(isButtonDescriptor),
})
.get();
export const isOpenPopupEvent = z.object({
popupId: z.number(),
targetObject: z.string(),
message: z.string(),
buttons: z.array(isButtonDescriptor),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenPopupEvent = tg.GuardedType<typeof isOpenPopupEvent>;
export type OpenPopupEvent = z.infer<typeof isOpenPopupEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isOpenTabEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
})
.get();
export const isOpenTabEvent = z.object({
url: z.string(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenTabEvent = tg.GuardedType<typeof isOpenTabEvent>;
export type OpenTabEvent = z.infer<typeof isOpenTabEvent>;
+15 -19
View File
@@ -1,25 +1,21 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
const isSoundConfig = new tg.IsInterface()
.withProperties({
volume: tg.isOptional(tg.isNumber),
loop: tg.isOptional(tg.isBoolean),
mute: tg.isOptional(tg.isBoolean),
rate: tg.isOptional(tg.isNumber),
detune: tg.isOptional(tg.isNumber),
seek: tg.isOptional(tg.isNumber),
delay: tg.isOptional(tg.isNumber),
})
.get();
export const isSoundConfig = z.object({
volume: z.optional(z.number()),
loop: z.optional(z.boolean()),
mute: z.optional(z.boolean()),
rate: z.optional(z.number()),
detune: z.optional(z.number()),
seek: z.optional(z.number()),
delay: z.optional(z.number()),
});
export const isPlaySoundEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
config: tg.isOptional(isSoundConfig),
})
.get();
export const isPlaySoundEvent = z.object({
url: z.string(),
config: z.optional(isSoundConfig),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type PlaySoundEvent = tg.GuardedType<typeof isPlaySoundEvent>;
export type PlaySoundEvent = z.infer<typeof isPlaySoundEvent>;
+6 -8
View File
@@ -1,10 +1,8 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isPlayerPosition = new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
})
.get();
export const isPlayerPosition = z.object({
x: z.number(),
y: z.number(),
});
export type PlayerPosition = tg.GuardedType<typeof isPlayerPosition>;
export type PlayerPosition = z.infer<typeof isPlayerPosition>;
@@ -1,15 +1,13 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
// TODO: Change for player Clicked, add all neccessary data
export const isRemotePlayerClickedEvent = new tg.IsInterface()
.withProperties({
id: tg.isNumber,
})
.get();
export const isRemotePlayerClickedEvent = z.object({
id: z.number(),
});
/**
* A message sent from the game to the iFrame when RemotePlayer is clicked.
*/
export type RemotePlayerClickedEvent = tg.GuardedType<typeof isRemotePlayerClickedEvent>;
export type RemotePlayerClickedEvent = z.infer<typeof isRemotePlayerClickedEvent>;
export type RemotePlayerClickedEventCallback = (event: RemotePlayerClickedEvent) => void;
@@ -1,15 +1,11 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isRemoveActionsMenuKeyFromRemotePlayerEvent = new tg.IsInterface()
.withProperties({
id: tg.isNumber,
actionKey: tg.isString,
})
.get();
export const isRemoveActionsMenuKeyFromRemotePlayerEvent = z.object({
id: z.number(),
actionKey: z.string(),
});
export type RemoveActionsMenuKeyFromRemotePlayerEvent = tg.GuardedType<
typeof isRemoveActionsMenuKeyFromRemotePlayerEvent
>;
export type RemoveActionsMenuKeyFromRemotePlayerEvent = z.infer<typeof isRemoveActionsMenuKeyFromRemotePlayerEvent>;
export type RemoveActionsMenuKeyFromRemotePlayerEventCallback = (
event: RemoveActionsMenuKeyFromRemotePlayerEvent
+10 -11
View File
@@ -1,16 +1,15 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isSetTilesEvent = tg.isArray(
new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
tile: tg.isUnion(tg.isUnion(tg.isNumber, tg.isString), tg.isNull),
layer: tg.isString,
})
.get()
export const isSetTilesEvent = z.array(
z.object({
x: z.number(),
y: z.number(),
tile: z.union([z.number(), z.string(), z.null()]),
layer: z.string(),
})
);
/**
* A message sent from the iFrame to the game to set one or many tiles.
*/
export type SetTilesEvent = tg.GuardedType<typeof isSetTilesEvent>;
export type SetTilesEvent = z.infer<typeof isSetTilesEvent>;
+13 -16
View File
@@ -1,20 +1,17 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isSetVariableEvent = z.object({
key: z.string(),
value: z.unknown(),
target: z.enum(["global", "player"]),
});
export const isSetVariableIframeEvent = z.object({
type: z.enum(["setVariable"]),
data: isSetVariableEvent,
});
export const isSetVariableEvent = new tg.IsInterface()
.withProperties({
key: tg.isString,
value: tg.isUnknown,
target: tg.isSingletonStringUnion("global", "player"),
})
.get();
/**
* A message sent from the iFrame to the game to change the value of the property of the layer
*/
export type SetVariableEvent = tg.GuardedType<typeof isSetVariableEvent>;
export const isSetVariableIframeEvent = new tg.IsInterface()
.withProperties({
type: tg.isSingletonString("setVariable"),
data: isSetVariableEvent,
})
.get();
export type SetVariableEvent = z.infer<typeof isSetVariableEvent>;
+5 -7
View File
@@ -1,12 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isStopSoundEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
})
.get();
export const isStopSoundEvent = z.object({
url: z.string(),
});
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type StopSoundEvent = tg.GuardedType<typeof isStopSoundEvent>;
export type StopSoundEvent = z.infer<typeof isStopSoundEvent>;
+6 -7
View File
@@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isUserInputChatEvent = z.object({
message: z.string(),
});
export const isUserInputChatEvent = new tg.IsInterface()
.withProperties({
message: tg.isString,
})
.get();
/**
* A message sent from the game to the iFrame when a user types a message in the chat.
*/
export type UserInputChatEvent = tg.GuardedType<typeof isUserInputChatEvent>;
export type UserInputChatEvent = z.infer<typeof isUserInputChatEvent>;
+9 -12
View File
@@ -1,19 +1,16 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isWasCameraUpdatedEvent = new tg.IsInterface()
.withProperties({
x: tg.isNumber,
y: tg.isNumber,
width: tg.isNumber,
height: tg.isNumber,
zoom: tg.isNumber,
})
.get();
export const isWasCameraUpdatedEvent = z.object({
x: z.number(),
y: z.number(),
width: z.number(),
height: z.number(),
zoom: z.number(),
});
/**
* A message sent from the game to the iFrame to notify a movement from the camera.
*/
export type WasCameraUpdatedEvent = tg.GuardedType<typeof isWasCameraUpdatedEvent>;
export type WasCameraUpdatedEvent = z.infer<typeof isWasCameraUpdatedEvent>;
export type WasCameraUpdatedEventCallback = (event: WasCameraUpdatedEvent) => void;
+8 -9
View File
@@ -1,13 +1,12 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isSetPropertyEvent = z.object({
layerName: z.string(),
propertyName: z.string(),
propertyValue: z.optional(z.union([z.string(), z.number(), z.boolean()])),
});
export const isSetPropertyEvent = new tg.IsInterface()
.withProperties({
layerName: tg.isString,
propertyName: tg.isString,
propertyValue: tg.isUnion(tg.isString, tg.isUnion(tg.isNumber, tg.isUnion(tg.isBoolean, tg.isUndefined))),
})
.get();
/**
* A message sent from the iFrame to the game to change the value of the property of the layer
*/
export type SetPropertyEvent = tg.GuardedType<typeof isSetPropertyEvent>;
export type SetPropertyEvent = z.infer<typeof isSetPropertyEvent>;
@@ -1,11 +1,10 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const isMenuItemClickedEvent = z.object({
menuItem: z.string(),
});
export const isMenuItemClickedEvent = new tg.IsInterface()
.withProperties({
menuItem: tg.isString,
})
.get();
/**
* A message sent from the game to the iFrame when a menu item is clicked.
*/
export type MenuItemClickedEvent = tg.GuardedType<typeof isMenuItemClickedEvent>;
export type MenuItemClickedEvent = z.infer<typeof isMenuItemClickedEvent>;
+14 -20
View File
@@ -1,31 +1,25 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
/**
* A message sent from a script to the game to remove a custom menu from the menu
*/
export const isUnregisterMenuEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
})
.get();
export const isUnregisterMenuEvent = z.object({
name: z.string(),
});
export type UnregisterMenuEvent = tg.GuardedType<typeof isUnregisterMenuEvent>;
export type UnregisterMenuEvent = z.infer<typeof isUnregisterMenuEvent>;
export const isMenuRegisterOptions = new tg.IsInterface()
.withProperties({
allowApi: tg.isBoolean,
})
.get();
export const isMenuRegisterOptions = z.object({
allowApi: z.boolean(),
});
/**
* A message sent from a script to the game to add a custom menu from the menu
*/
export const isMenuRegisterEvent = new tg.IsInterface()
.withProperties({
name: tg.isString,
iframe: tg.isUnion(tg.isString, tg.isUndefined),
options: isMenuRegisterOptions,
})
.get();
export const isMenuRegisterEvent = z.object({
name: z.string(),
iframe: z.optional(z.string()),
options: isMenuRegisterOptions,
});
export type MenuRegisterEvent = tg.GuardedType<typeof isMenuRegisterEvent>;
export type MenuRegisterEvent = z.infer<typeof isMenuRegisterEvent>;
@@ -1,26 +1,22 @@
import * as tg from "generic-type-guard";
import { z } from "zod";
export const triggerActionMessage = "triggerActionMessage";
export const removeActionMessage = "removeActionMessage";
export const isActionMessageType = tg.isSingletonStringUnion("message", "warning");
export const isActionMessageType = z.enum(["message", "warning"]);
export type ActionMessageType = tg.GuardedType<typeof isActionMessageType>;
export type ActionMessageType = z.infer<typeof isActionMessageType>;
export const isTriggerActionMessageEvent = new tg.IsInterface()
.withProperties({
message: tg.isString,
uuid: tg.isString,
type: isActionMessageType,
})
.get();
export const isTriggerActionMessageEvent = z.object({
message: z.string(),
uuid: z.string(),
type: isActionMessageType,
});
export type TriggerActionMessageEvent = tg.GuardedType<typeof isTriggerActionMessageEvent>;
export type TriggerActionMessageEvent = z.infer<typeof isTriggerActionMessageEvent>;
export const isMessageReferenceEvent = new tg.IsInterface()
.withProperties({
uuid: tg.isString,
})
.get();
export const isMessageReferenceEvent = z.object({
uuid: z.string(),
});
export type MessageReferenceEvent = tg.GuardedType<typeof isMessageReferenceEvent>;
export type MessageReferenceEvent = z.infer<typeof isMessageReferenceEvent>;
@@ -5,20 +5,16 @@ import {
triggerActionMessage,
} from "./TriggerActionMessageEvent";
import * as tg from "generic-type-guard";
import { z } from "zod";
const isTriggerMessageEventObject = new tg.IsInterface()
.withProperties({
type: tg.isSingletonString(triggerActionMessage),
data: isTriggerActionMessageEvent,
})
.get();
const isTriggerMessageEventObject = z.object({
type: z.enum([triggerActionMessage]),
data: isTriggerActionMessageEvent,
});
const isTriggerMessageRemoveEventObject = new tg.IsInterface()
.withProperties({
type: tg.isSingletonString(removeActionMessage),
data: isMessageReferenceEvent,
})
.get();
const isTriggerMessageRemoveEventObject = z.object({
type: z.enum([removeActionMessage]),
data: isMessageReferenceEvent,
});
export const isTriggerMessageHandlerEvent = tg.isUnion(isTriggerMessageEventObject, isTriggerMessageRemoveEventObject);
export const isTriggerMessageHandlerEvent = z.union([isTriggerMessageEventObject, isTriggerMessageRemoveEventObject]);
+86 -82
View File
@@ -1,13 +1,11 @@
import { Subject } from "rxjs";
import { 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 { OpenPopupEvent } from "./Events/OpenPopupEvent";
import { OpenTabEvent } from "./Events/OpenTabEvent";
import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
import { ClosePopupEvent } from "./Events/ClosePopupEvent";
import { scriptUtils } from "./ScriptUtils";
import { isGoToPageEvent } from "./Events/GoToPageEvent";
import {
IframeErrorAnswerEvent,
IframeQueryMap,
@@ -15,35 +13,28 @@ import {
IframeResponseEventMap,
isIframeEventWrapper,
isIframeQueryWrapper,
isLookingLikeIframeEventWrapper,
} 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";
import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent";
import { isLayerEvent, LayerEvent } from "./Events/LayerEvent";
import { PlaySoundEvent } from "./Events/PlaySoundEvent";
import { StopSoundEvent } from "./Events/StopSoundEvent";
import { LoadSoundEvent } from "./Events/LoadSoundEvent";
import { SetPropertyEvent } from "./Events/setPropertyEvent";
import { LayerEvent } from "./Events/LayerEvent";
import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent";
import { isLoadPageEvent } from "./Events/LoadPageEvent";
import { isMenuRegisterEvent, isUnregisterMenuEvent } from "./Events/ui/MenuRegisterEvent";
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
import { SetTilesEvent } from "./Events/SetTilesEvent";
import type { SetVariableEvent } from "./Events/SetVariableEvent";
import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
import { ModifyEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent";
import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore";
import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent";
import type { WasCameraUpdatedEvent } from "./Events/WasCameraUpdatedEvent";
import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent";
import { CameraSetEvent, isCameraSetEvent } from "./Events/CameraSetEvent";
import { CameraFollowPlayerEvent, isCameraFollowPlayerEvent } from "./Events/CameraFollowPlayerEvent";
import { CameraSetEvent } from "./Events/CameraSetEvent";
import { CameraFollowPlayerEvent } from "./Events/CameraFollowPlayerEvent";
import type { RemotePlayerClickedEvent } from "./Events/RemotePlayerClickedEvent";
import {
AddActionsMenuKeyToRemotePlayerEvent,
isAddActionsMenuKeyToRemotePlayerEvent,
} from "./Events/AddActionsMenuKeyToRemotePlayerEvent";
import { AddActionsMenuKeyToRemotePlayerEvent } from "./Events/AddActionsMenuKeyToRemotePlayerEvent";
import type { ActionsMenuActionClickedEvent } from "./Events/ActionsMenuActionClickedEvent";
import {
isRemoveActionsMenuKeyFromRemotePlayerEvent,
RemoveActionsMenuKeyFromRemotePlayerEvent,
} from "./Events/RemoveActionsMenuKeyFromRemotePlayerEvent";
import { RemoveActionsMenuKeyFromRemotePlayerEvent } from "./Events/RemoveActionsMenuKeyFromRemotePlayerEvent";
type AnswererCallback<T extends keyof IframeQueryMap> = (
query: IframeQueryMap[T]["query"],
@@ -150,8 +141,10 @@ class IframeListener {
const payload = message.data;
const lookingLikeEvent = isLookingLikeIframeEventWrapper.safeParse(payload);
if (foundSrc === undefined || iframe === undefined) {
if (isIframeEventWrapper(payload)) {
if (lookingLikeEvent.success) {
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 " +
@@ -221,63 +214,70 @@ class IframeListener {
} catch (reason) {
errorHandler(reason);
}
} else if (isIframeEventWrapper(payload)) {
if (payload.type === "showLayer" && isLayerEvent(payload.data)) {
this._showLayerStream.next(payload.data);
} else if (payload.type === "hideLayer" && isLayerEvent(payload.data)) {
this._hideLayerStream.next(payload.data);
} else if (payload.type === "setProperty" && isSetPropertyEvent(payload.data)) {
this._setPropertyStream.next(payload.data);
} else if (payload.type === "cameraSet" && isCameraSetEvent(payload.data)) {
this._cameraSetStream.next(payload.data);
} else if (payload.type === "cameraFollowPlayer" && isCameraFollowPlayerEvent(payload.data)) {
this._cameraFollowPlayerStream.next(payload.data);
} else if (payload.type === "chat" && isChatEvent(payload.data)) {
scriptUtils.sendAnonymousChat(payload.data);
} else if (payload.type === "openPopup" && isOpenPopupEvent(payload.data)) {
this._openPopupStream.next(payload.data);
} else if (payload.type === "closePopup" && isClosePopupEvent(payload.data)) {
this._closePopupStream.next(payload.data);
} else if (payload.type === "openTab" && isOpenTabEvent(payload.data)) {
scriptUtils.openTab(payload.data.url);
} else if (payload.type === "goToPage" && isGoToPageEvent(payload.data)) {
scriptUtils.goToPage(payload.data.url);
} else if (payload.type === "loadPage" && isLoadPageEvent(payload.data)) {
this._loadPageStream.next(payload.data.url);
} else if (payload.type === "playSound" && isPlaySoundEvent(payload.data)) {
this._playSoundStream.next(payload.data);
} else if (payload.type === "stopSound" && isStopSoundEvent(payload.data)) {
this._stopSoundStream.next(payload.data);
} else if (payload.type === "loadSound" && isLoadSoundEvent(payload.data)) {
this._loadSoundStream.next(payload.data);
} else if (payload.type === "disablePlayerControls") {
} else if (lookingLikeEvent.success) {
const iframeEventGuarded = isIframeEventWrapper.safeParse(lookingLikeEvent.data);
if (!iframeEventGuarded.success) {
console.error(
`Invalid event "${lookingLikeEvent.data.type}" received from Iframe: `,
lookingLikeEvent.data,
iframeEventGuarded.error.issues
);
return;
}
const iframeEvent = iframeEventGuarded.data;
if (iframeEvent.type === "showLayer") {
this._showLayerStream.next(iframeEvent.data);
} else if (iframeEvent.type === "hideLayer") {
this._hideLayerStream.next(iframeEvent.data);
} else if (iframeEvent.type === "setProperty") {
this._setPropertyStream.next(iframeEvent.data);
} else if (iframeEvent.type === "cameraSet") {
this._cameraSetStream.next(iframeEvent.data);
} else if (iframeEvent.type === "cameraFollowPlayer") {
this._cameraFollowPlayerStream.next(iframeEvent.data);
} else if (iframeEvent.type === "chat") {
scriptUtils.sendAnonymousChat(iframeEvent.data);
} else if (iframeEvent.type === "openPopup") {
this._openPopupStream.next(iframeEvent.data);
} else if (iframeEvent.type === "closePopup") {
this._closePopupStream.next(iframeEvent.data);
} else if (iframeEvent.type === "openTab") {
scriptUtils.openTab(iframeEvent.data.url);
} else if (iframeEvent.type === "goToPage") {
scriptUtils.goToPage(iframeEvent.data.url);
} else if (iframeEvent.type === "loadPage") {
this._loadPageStream.next(iframeEvent.data.url);
} else if (iframeEvent.type === "playSound") {
this._playSoundStream.next(iframeEvent.data);
} else if (iframeEvent.type === "stopSound") {
this._stopSoundStream.next(iframeEvent.data);
} else if (iframeEvent.type === "loadSound") {
this._loadSoundStream.next(iframeEvent.data);
} else if (iframeEvent.type === "disablePlayerControls") {
this._disablePlayerControlStream.next();
} else if (payload.type === "restorePlayerControls") {
} else if (iframeEvent.type === "restorePlayerControls") {
this._enablePlayerControlStream.next();
} else if (payload.type === "displayBubble") {
} else if (iframeEvent.type === "displayBubble") {
this._displayBubbleStream.next();
} else if (payload.type === "removeBubble") {
} else if (iframeEvent.type === "removeBubble") {
this._removeBubbleStream.next();
} else if (payload.type == "onPlayerMove") {
} else if (iframeEvent.type == "onPlayerMove") {
this.sendPlayerMove = true;
} else if (
payload.type == "addActionsMenuKeyToRemotePlayer" &&
isAddActionsMenuKeyToRemotePlayerEvent(payload.data)
) {
this._addActionsMenuKeyToRemotePlayerStream.next(payload.data);
} else if (
payload.type == "removeActionsMenuKeyFromRemotePlayer" &&
isRemoveActionsMenuKeyFromRemotePlayerEvent(payload.data)
) {
this._removeActionsMenuKeyFromRemotePlayerEvent.next(payload.data);
} else if (payload.type == "onCameraUpdate") {
} else if (iframeEvent.type == "addActionsMenuKeyToRemotePlayer") {
this._addActionsMenuKeyToRemotePlayerStream.next(iframeEvent.data);
} else if (iframeEvent.type == "removeActionsMenuKeyFromRemotePlayer") {
this._removeActionsMenuKeyFromRemotePlayerEvent.next(iframeEvent.data);
} else if (iframeEvent.type == "onCameraUpdate") {
this._trackCameraUpdateStream.next();
} else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) {
this._setTilesStream.next(payload.data);
} else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) {
this._modifyEmbeddedWebsiteStream.next(payload.data);
} else if (payload.type == "registerMenu" && isMenuRegisterEvent(payload.data)) {
const dataName = payload.data.name;
} else if (iframeEvent.type == "setTiles") {
this._setTilesStream.next(iframeEvent.data);
} else if (iframeEvent.type == "modifyEmbeddedWebsite") {
this._modifyEmbeddedWebsiteStream.next(iframeEvent.data);
} else if (iframeEvent.type == "registerMenu") {
const dataName = iframeEvent.data.name;
this.iframeCloseCallbacks.get(iframe)?.push(() => {
handleMenuUnregisterEvent(dataName);
});
@@ -285,13 +285,17 @@ class IframeListener {
foundSrc = this.getBaseUrl(foundSrc, message.source);
handleMenuRegistrationEvent(
payload.data.name,
payload.data.iframe,
iframeEvent.data.name,
iframeEvent.data.iframe,
foundSrc,
payload.data.options
iframeEvent.data.options
);
} else if (payload.type == "unregisterMenu" && isUnregisterMenuEvent(payload.data)) {
handleMenuUnregisterEvent(payload.data.name);
} else if (iframeEvent.type == "unregisterMenu") {
handleMenuUnregisterEvent(iframeEvent.data.name);
} else {
// Keep the line below. It will throw an error if we forget to handle one of the possible values.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustiveCheck: never = iframeEvent;
}
}
},
@@ -315,7 +319,7 @@ class IframeListener {
}
registerScript(scriptUrl: string, enableModuleMode: boolean = true): Promise<void> {
return new Promise<void>((resolve, reject) => {
return new Promise<void>((resolve) => {
console.info("Loading map related script at ", scriptUrl);
const iframe = document.createElement("iframe");
+4 -12
View File
@@ -1,14 +1,8 @@
import type * as tg from "generic-type-guard";
import type {
IframeEvent,
IframeEventMap,
IframeQuery,
IframeQueryMap,
IframeResponseEventMap,
} from "../Events/IframeEvent";
import { z } from "zod";
import type { IframeEvent, IframeQuery, IframeQueryMap, IframeResponseEventMap } from "../Events/IframeEvent";
import type { IframeQueryWrapper } from "../Events/IframeEvent";
export function sendToWorkadventure(content: IframeEvent<keyof IframeEventMap>) {
export function sendToWorkadventure(content: IframeEvent) {
window.parent.postMessage(content, "*");
}
@@ -48,12 +42,10 @@ export function queryWorkadventure<T extends keyof IframeQueryMap>(
});
}
type GuardedType<Guard extends tg.TypeGuard<unknown>> = Guard extends tg.TypeGuard<infer T> ? T : never;
export interface IframeCallback<
Key extends keyof IframeResponseEventMap,
T = IframeResponseEventMap[Key],
Guard = tg.TypeGuard<T>
Guard = z.ZodType<T>
> {
typeChecker: Guard;
callback: (payloadData: T) => void;
+1 -5
View File
@@ -1,9 +1,5 @@
import { sendToWorkadventure } from "../IframeApiContribution";
import type {
CreateEmbeddedWebsiteEvent,
ModifyEmbeddedWebsiteEvent,
Rectangle,
} from "../../Events/EmbeddedWebsiteEvent";
import type { CreateEmbeddedWebsiteEvent, Rectangle } from "../../Events/EmbeddedWebsiteEvent";
export class EmbeddedWebsite {
public readonly name: string;
+1 -1
View File
@@ -41,7 +41,7 @@ export class WorkAdventureCameraCommands extends IframeApiContribution<WorkAdven
onCameraUpdate(): Subject<WasCameraUpdatedEvent> {
sendToWorkadventure({
type: "onCameraUpdate",
data: null,
data: undefined,
});
return moveStream;
}
+2 -2
View File
@@ -4,11 +4,11 @@ export class WorkadventureControlsCommands extends IframeApiContribution<Workadv
callbacks = [];
disablePlayerControls(): void {
sendToWorkadventure({ type: "disablePlayerControls", data: null });
sendToWorkadventure({ type: "disablePlayerControls", data: undefined });
}
restorePlayerControls(): void {
sendToWorkadventure({ type: "restorePlayerControls", data: null });
sendToWorkadventure({ type: "restorePlayerControls", data: undefined });
}
}
+1 -1
View File
@@ -54,7 +54,7 @@ export class WorkadventurePlayerCommands extends IframeApiContribution<Workadven
moveStream.subscribe(callback);
sendToWorkadventure({
type: "onPlayerMove",
data: null,
data: undefined,
});
}
+1 -4
View File
@@ -1,7 +1,4 @@
import type { LoadSoundEvent } from "../Events/LoadSoundEvent";
import type { PlaySoundEvent } from "../Events/PlaySoundEvent";
import type { StopSoundEvent } from "../Events/StopSoundEvent";
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
import { IframeApiContribution } from "./IframeApiContribution";
import { Sound } from "./Sound/Sound";
export class WorkadventureSoundCommands extends IframeApiContribution<WorkadventureSoundCommands> {
+3 -6
View File
@@ -1,13 +1,9 @@
import { Observable, Subject } from "rxjs";
import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent";
import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "./IframeApiContribution";
import { IframeApiContribution, queryWorkadventure } from "./IframeApiContribution";
import { apiCallback } from "./registeredCallbacks";
import { isSetVariableEvent, SetVariableEvent } from "../Events/SetVariableEvent";
import type { ITiledMap } from "../../Phaser/Map/ITiledMap";
export class WorkadventureStateCommands extends IframeApiContribution<WorkadventureStateCommands> {
private setVariableResolvers = new Subject<SetVariableEvent>();
private variables = new Map<string, unknown>();
@@ -17,7 +13,7 @@ export class WorkadventureStateCommands extends IframeApiContribution<Workadvent
super();
this.setVariableResolvers.subscribe((event) => {
const oldValue = this.variables.get(event.key);
// const oldValue = this.variables.get(event.key);
// 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
/*if (JSON.stringify(oldValue) === JSON.stringify(event.value)) {
@@ -92,6 +88,7 @@ export function createState(target: "global" | "player"): WorkadventureStateComm
}
return target.loadVariable(p.toString());
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean {
// Note: when using "set", there is no way to wait, so we ignore the return of the promise.
// User must use WA.state.saveVariable to have error message.
+2 -10
View File
@@ -34,14 +34,6 @@ interface MenuDescriptor {
export type MenuOptions = RequireOnlyOne<MenuDescriptor, "callback" | "iframe">;
interface ZonedPopupOptions {
zone: string;
objectLayerName?: string;
popupText: string;
delay?: number;
popupOptions: Array<ButtonDescriptor>;
}
export interface ActionMessageOptions {
message: string;
type?: "message" | "warning";
@@ -277,11 +269,11 @@ export class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventure
}
public displayBubble(): void {
sendToWorkadventure({ type: "displayBubble", data: null });
sendToWorkadventure({ type: "displayBubble", data: undefined });
}
public removeBubble(): void {
sendToWorkadventure({ type: "removeBubble", data: null });
sendToWorkadventure({ type: "removeBubble", data: undefined });
}
public displayActionMessage(actionMessageOptions: ActionMessageOptions): ActionMessage {
+1 -1
View File
@@ -1,4 +1,4 @@
import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "./IframeApiContribution";
import { IframeApiContribution, queryWorkadventure } from "./IframeApiContribution";
import { EmbeddedWebsite } from "./Room/EmbeddedWebsite";
import type { CreateEmbeddedWebsiteEvent } from "../Events/EmbeddedWebsiteEvent";