added callback on playermove - gets quite delayed after walking for a few seconds

This commit is contained in:
jonny 2021-05-10 21:27:17 +02:00
parent ce0c7ea3eb
commit 8e136cebe8
8 changed files with 104 additions and 44 deletions

View File

@ -0,0 +1,19 @@
import * as tg from "generic-type-guard";
export const isHasMovedEvent =
new tg.IsInterface().withProperties({
direction: tg.isString,
moving: tg.isBoolean,
x: tg.isNumber,
y: tg.isNumber
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type HasMovedEvent = tg.GuardedType<typeof isHasMovedEvent>;
export type HasMovedEventCallback = (event: HasMovedEvent) => void

View File

@ -1,11 +1,11 @@
import { GameStateEvent } from './ApiGameStateEvent'; import { GameStateEvent } from './ApiGameStateEvent';
import { ButtonClickedEvent } from './ButtonClickedEvent'; import { ButtonClickedEvent } from './ButtonClickedEvent';
import { ChatEvent } from './ChatEvent'; import { ChatEvent } from './ChatEvent';
import { ClosePopupEvent } from './ClosePopupEvent'; import { ClosePopupEvent } from './ClosePopupEvent';
import { EnterLeaveEvent } from './EnterLeaveEvent'; import { EnterLeaveEvent } from './EnterLeaveEvent';
import { GoToPageEvent } from './GoToPageEvent'; import { GoToPageEvent } from './GoToPageEvent';
import { HasMovedEvent } from './HasMovedEvent';
import { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent'; import { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
import { OpenPopupEvent } from './OpenPopupEvent'; import { OpenPopupEvent } from './OpenPopupEvent';
import { OpenTabEvent } from './OpenTabEvent'; import { OpenTabEvent } from './OpenTabEvent';
@ -30,6 +30,7 @@ export type IframeEventMap = {
restorePlayerControl: null restorePlayerControl: null
displayBubble: null displayBubble: null
removeBubble: null removeBubble: null
enableMoveEvents: undefined
} }
export interface IframeEvent<T extends keyof IframeEventMap> { export interface IframeEvent<T extends keyof IframeEventMap> {
type: T; type: T;
@ -46,6 +47,7 @@ export interface IframeResponseEventMap {
leaveEvent: EnterLeaveEvent leaveEvent: EnterLeaveEvent
buttonClickedEvent: ButtonClickedEvent buttonClickedEvent: ButtonClickedEvent
gameState: GameStateEvent gameState: GameStateEvent
hasMovedEvent: HasMovedEvent
} }
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> { export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
type: T; type: T;

View File

@ -14,6 +14,7 @@ import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMa
import { UserInputChatEvent } from "./Events/UserInputChatEvent"; import { UserInputChatEvent } from "./Events/UserInputChatEvent";
import { GameStateEvent } from './Events/ApiGameStateEvent'; import { GameStateEvent } from './Events/ApiGameStateEvent';
import { deepFreezeClone as deepFreezeClone } from '../utility'; import { deepFreezeClone as deepFreezeClone } from '../utility';
import { HasMovedEvent } from './Events/HasMovedEvent';
/** /**
@ -21,6 +22,7 @@ import { deepFreezeClone as deepFreezeClone } from '../utility';
* Also allows to send messages to those iframes. * Also allows to send messages to those iframes.
*/ */
class IframeListener { class IframeListener {
private readonly _chatStream: Subject<ChatEvent> = new Subject(); private readonly _chatStream: Subject<ChatEvent> = new Subject();
public readonly chatStream = this._chatStream.asObservable(); public readonly chatStream = this._chatStream.asObservable();
@ -60,6 +62,7 @@ class IframeListener {
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 sendMoveEvents: boolean = false;
init() { init() {
window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => { window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => {
@ -101,20 +104,18 @@ class IframeListener {
} }
else if (payload.type === 'closeCoWebSite') { else if (payload.type === 'closeCoWebSite') {
scriptUtils.closeCoWebSite(); scriptUtils.closeCoWebSite();
} } else if (payload.type === 'disablePlayerControl') {
else if (payload.type === 'disablePlayerControl') {
this._disablePlayerControlStream.next(); this._disablePlayerControlStream.next();
} } else if (payload.type === 'restorePlayerControl') {
else if (payload.type === 'restorePlayerControl') {
this._enablePlayerControlStream.next(); this._enablePlayerControlStream.next();
} } else if (payload.type === 'displayBubble') {
else if (payload.type === 'displayBubble') {
this._displayBubbleStream.next(); this._displayBubbleStream.next();
} } else if (payload.type === 'removeBubble') {
else if (payload.type === 'removeBubble') {
this._removeBubbleStream.next(); this._removeBubbleStream.next();
}else if(payload.type=="getState"){ } else if (payload.type == "getState") {
this._gameStateStream.next(); this._gameStateStream.next();
} else if (payload.type == "enableMoveEvents") {
this.sendMoveEvents = true
} }
} }
@ -234,6 +235,15 @@ class IframeListener {
}); });
} }
hasMovedEvent(event: HasMovedEvent) {
if (this.sendMoveEvents) {
this.postMessage({
'type': 'hasMovedEvent',
'data': event
});
}
}
sendButtonClickedEvent(popupId: number, buttonId: number): void { sendButtonClickedEvent(popupId: number, buttonId: number): void {
this.postMessage({ this.postMessage({
'type': 'buttonClickedEvent', 'type': 'buttonClickedEvent',

View File

@ -8,12 +8,7 @@ import {SelectCharacterSceneName} from "../Login/SelectCharacterScene";
import {EnableCameraSceneName} from "../Login/EnableCameraScene"; import {EnableCameraSceneName} from "../Login/EnableCameraScene";
import {localUserStore} from "../../Connexion/LocalUserStore"; import {localUserStore} from "../../Connexion/LocalUserStore";
export interface HasMovedEvent {
direction: string;
moving: boolean;
x: number;
y: number;
}
/** /**
* This class should be responsible for any scene starting/stopping * This class should be responsible for any scene starting/stopping

View File

@ -1,4 +1,4 @@
import {gameManager, HasMovedEvent} from "./GameManager"; import { gameManager } from "./GameManager";
import { import {
GroupCreatedUpdatedMessageInterface, GroupCreatedUpdatedMessageInterface,
MessageUserJoined, MessageUserJoined,
@ -91,7 +91,8 @@ 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 { PlayerStateObject } from '../../Api/Events/ApiGameStateEvent'; import { PlayerStateObject } from '../../Api/Events/ApiGameStateEvent';
import {waScaleManager} from "../Services/WaScaleManager"; import { waScaleManager } from "../Services/WaScaleManager";
import { HasMovedEvent } from '../../Api/Events/HasMovedEvent';
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface|null, initPosition: PointInterface|null,
@ -631,6 +632,9 @@ export class GameScene extends DirtyScene implements CenterListener {
//listen event to share position of user //listen event to share position of user
this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))
this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => {
iframeListener.hasMovedEvent(event)
})
this.CurrentPlayer.on(hasMovedEventName, this.outlineItem.bind(this)) this.CurrentPlayer.on(hasMovedEventName, this.outlineItem.bind(this))
this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => { this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => {
this.gameMap.setPosition(event.x, event.y); this.gameMap.setPosition(event.x, event.y);

View File

@ -1,6 +1,7 @@
import {HasMovedEvent} from "./GameManager";
import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable"; import { MAX_EXTRAPOLATION_TIME } from "../../Enum/EnvironmentVariable";
import {PositionInterface} from "../../Connexion/ConnexionModels"; import { PositionInterface } from "../../Connexion/ConnexionModels";
import { HasMovedEvent } from '../../Api/Events/HasMovedEvent';
export class PlayerMovement { export class PlayerMovement {
public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) { public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) {

View File

@ -2,13 +2,13 @@
* This class is in charge of computing the position of all players. * This class is in charge of computing the position of all players.
* Player movement is delayed by 200ms so position depends on ticks. * Player movement is delayed by 200ms so position depends on ticks.
*/ */
import {PlayerMovement} from "./PlayerMovement"; import { HasMovedEvent } from '../../Api/Events/HasMovedEvent';
import {HasMovedEvent} from "./GameManager"; import { PlayerMovement } from "./PlayerMovement";
export class PlayersPositionInterpolator { export class PlayersPositionInterpolator {
playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>(); playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>();
updatePlayerPosition(userId: number, playerMovement: PlayerMovement) : void { updatePlayerPosition(userId: number, playerMovement: PlayerMovement): void {
this.playerMovements.set(userId, playerMovement); this.playerMovements.set(userId, playerMovement);
} }
@ -16,7 +16,7 @@ export class PlayersPositionInterpolator {
this.playerMovements.delete(userId); this.playerMovements.delete(userId);
} }
getUpdatedPositions(tick: number) : Map<number, HasMovedEvent> { getUpdatedPositions(tick: number): Map<number, HasMovedEvent> {
const positions = new Map<number, HasMovedEvent>(); const positions = new Map<number, HasMovedEvent>();
this.playerMovements.forEach((playerMovement: PlayerMovement, userId: number) => { this.playerMovements.forEach((playerMovement: PlayerMovement, userId: number) => {
if (playerMovement.isOutdated(tick)) { if (playerMovement.isOutdated(tick)) {

View File

@ -1,5 +1,5 @@
import { ChatEvent } from "./Api/Events/ChatEvent"; import { ChatEvent } from "./Api/Events/ChatEvent";
import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent"; import { IframeEvent, IframeEventMap, isIframeResponseEventWrapper } from "./Api/Events/IframeEvent";
import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent"; import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent";
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent"; import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent";
@ -10,6 +10,7 @@ import { OpenTabEvent } from "./Api/Events/OpenTabEvent";
import { GoToPageEvent } from "./Api/Events/GoToPageEvent"; import { GoToPageEvent } from "./Api/Events/GoToPageEvent";
import { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent"; import { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent";
import { GameStateEvent, isGameStateEvent } from './Api/Events/ApiGameStateEvent'; import { GameStateEvent, isGameStateEvent } from './Api/Events/ApiGameStateEvent';
import { HasMovedEvent, HasMovedEventCallback, isHasMovedEvent } from './Api/Events/HasMovedEvent';
interface WorkAdventureApi { interface WorkAdventureApi {
sendChatMessage(message: string, author: string): void; sendChatMessage(message: string, author: string): void;
@ -17,15 +18,17 @@ interface WorkAdventureApi {
onEnterZone(name: string, callback: () => void): void; onEnterZone(name: string, callback: () => void): void;
onLeaveZone(name: string, callback: () => void): void; onLeaveZone(name: string, callback: () => void): void;
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup; openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup;
openTab(url : string): void; openTab(url: string): void;
goToPage(url : string): void; goToPage(url: string): void;
openCoWebSite(url : string): void; openCoWebSite(url: string): void;
closeCoWebSite(): void; closeCoWebSite(): void;
disablePlayerControl(): void; disablePlayerControl(): void;
restorePlayerControl(): void; restorePlayerControl(): void;
displayBubble(): void; displayBubble(): void;
removeBubble(): void; removeBubble(): void;
getGameState():Promise<unknown> getGameState(): Promise<GameStateEvent>
onMoveEvent(callback: (moveEvent: HasMovedEvent) => void): void
} }
declare global { declare global {
@ -75,20 +78,44 @@ class Popup {
}, '*'); }, '*');
} }
} }
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
const stateResolvers: Array<(event: GameStateEvent) => void> = []
const callbacks: { [type: string]: HasMovedEventCallback | ((arg?: HasMovedEvent | never) => void) } = {}
const stateResolvers:Array<(event:GameStateEvent)=>void> =[] function postToParent(content: IframeEvent<keyof IframeEventMap>) {
window.parent.postMessage(content, "*")
}
let moveEventUuid: string | undefined;
window.WA = { window.WA = {
onMoveEvent(callback: HasMovedEventCallback): void {
moveEventUuid = uuidv4();
callbacks[moveEventUuid] = callback;
postToParent({
type: "enableMoveEvents",
data: undefined
})
window.parent.postMessage({
type: "enable"
}, "*")
},
getGameState(){ getGameState() {
return new Promise<GameStateEvent>((resolver,thrower)=>{ return new Promise<GameStateEvent>((resolver, thrower) => {
stateResolvers.push(resolver); stateResolvers.push(resolver);
window.parent.postMessage({ window.parent.postMessage({
type:"getState" type: "getState"
},"*") }, "*")
}) })
}, },
@ -140,10 +167,10 @@ window.WA = {
}, '*'); }, '*');
}, },
openCoWebSite(url : string) : void{ openCoWebSite(url: string): void {
window.parent.postMessage({ window.parent.postMessage({
"type" : 'openCoWebSite', "type": 'openCoWebSite',
"data" : { "data": {
url url
} as OpenCoWebSiteEvent } as OpenCoWebSiteEvent
}, '*'); }, '*');
@ -242,10 +269,12 @@ window.addEventListener('message', message => {
if (callback) { if (callback) {
callback(popup); callback(popup);
} }
}else if(payload.type=="gameState" && isGameStateEvent(payloadData)){ } else if (payload.type == "gameState" && isGameStateEvent(payloadData)) {
stateResolvers.forEach(resolver=>{ stateResolvers.forEach(resolver => {
resolver(payloadData); resolver(payloadData);
}) })
} else if (payload.type == "hasMovedEvent" && isHasMovedEvent(payloadData) && moveEventUuid) {
callbacks[moveEventUuid](payloadData)
} }
} }