Merge branch 'develop' of github.com:thecodingmachine/workadventure into menu-command-api

# Conflicts:
#	front/src/Api/Events/IframeEvent.ts
#	front/src/Api/IframeListener.ts
#	front/src/iframe_api.ts
This commit is contained in:
David Négrier 2021-05-18 10:16:44 +02:00
commit 127a4759ac
77 changed files with 2086 additions and 1569 deletions

View file

@ -1,5 +1,5 @@
import {PointInterface} from "../../Connexion/ConnexionModels";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import type {PointInterface} from "../../Connexion/ConnexionModels";
import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
export interface AddPlayerInterface {
userId: number;

View file

@ -2,6 +2,8 @@ import {ResizableScene} from "../Login/ResizableScene";
import GameObject = Phaser.GameObjects.GameObject;
import Events = Phaser.Scenes.Events;
import AnimationEvents = Phaser.Animations.Events;
import StructEvents = Phaser.Structs.Events;
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
/**
* A scene that can track its dirty/pristine state.
@ -18,17 +20,16 @@ export abstract class DirtyScene extends ResizableScene {
* Note: this does not work with animations from sprites inside containers.
*/
protected trackDirtyAnims(): void {
if (this.isAlreadyTracking) {
if (this.isAlreadyTracking || SKIP_RENDER_OPTIMIZATIONS) {
return;
}
this.isAlreadyTracking = true;
const trackAnimationFunction = this.trackAnimation.bind(this);
this.events.on(Events.ADDED_TO_SCENE, (gameObject: GameObject) => {
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_ADD, (gameObject: GameObject) => {
this.objectListChanged = true;
gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
});
this.events.on(Events.REMOVED_FROM_SCENE, (gameObject: GameObject) => {
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_REMOVE, (gameObject: GameObject) => {
this.objectListChanged = true;
gameObject.removeListener(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
});

View file

@ -1,3 +1,8 @@
import {SKIP_RENDER_OPTIMIZATIONS} from "../../Enum/EnvironmentVariable";
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import {waScaleManager} from "../Services/WaScaleManager";
import {ResizableScene} from "../Login/ResizableScene";
const Events = Phaser.Core.Events;
/**
@ -5,8 +10,27 @@ const Events = Phaser.Core.Events;
* It comes with an optimization to skip rendering.
*
* Beware, the "step" function might vary in future versions of Phaser.
*
* It also automatically calls "onResize" on any scenes extending ResizableScene.
*/
export class Game extends Phaser.Game {
private _isDirty = false;
constructor(GameConfig: Phaser.Types.Core.GameConfig) {
super(GameConfig);
window.addEventListener('resize', (event) => {
// Let's trigger the onResize method of any active scene that is a ResizableScene
for (const scene of this.scene.getScenes(true)) {
if (scene instanceof ResizableScene) {
scene.onResize(event);
}
}
});
}
public step(time: number, delta: number)
{
// @ts-ignore
@ -35,7 +59,7 @@ export class Game extends Phaser.Game {
eventEmitter.emit(Events.POST_STEP, time, delta);
// This "if" is the changed introduced by the new "Game" class to avoid rendering unnecessarily.
if (this.isDirty()) {
if (SKIP_RENDER_OPTIMIZATIONS || this.isDirty()) {
const renderer = this.renderer;
// Run the Pre-render (clearing the canvas, setting background colors, etc)
@ -62,6 +86,11 @@ export class Game extends Phaser.Game {
}
private isDirty(): boolean {
if (this._isDirty) {
this._isDirty = false;
return true;
}
// Loop through the scenes in forward order
for (let i = 0; i < this.scene.scenes.length; i++)
{
@ -85,4 +114,11 @@ export class Game extends Phaser.Game {
return false;
}
/**
* Marks the game as needing to be redrawn.
*/
public markDirty(): void {
this._isDirty = true;
}
}

View file

@ -1,6 +1,6 @@
import {GameScene} from "./GameScene";
import {connectionManager} from "../../Connexion/ConnectionManager";
import {Room} from "../../Connexion/Room";
import type {Room} from "../../Connexion/Room";
import {MenuScene, MenuSceneName} from "../Menu/MenuScene";
import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene";
import {LoginSceneName} from "../Login/LoginScene";
@ -24,7 +24,7 @@ export class GameManager {
private companion: string|null;
private startRoom!:Room;
currentGameSceneName: string|null = null;
constructor() {
this.playerName = localUserStore.getName();
this.characterLayers = localUserStore.getCharacterLayers();
@ -65,7 +65,7 @@ export class GameManager {
return this.characterLayers;
}
setCompanion(companion: string|null): void {
this.companion = companion;
}
@ -91,7 +91,7 @@ export class GameManager {
scenePlugin.launch(MenuSceneName);
scenePlugin.launch(HelpCameraSettingsSceneName);//700
}
public gameSceneIsCreated(scene: GameScene) {
this.currentGameSceneName = scene.scene.key;
const menuScene: MenuScene = scene.scene.get(MenuSceneName) as MenuScene;
@ -125,7 +125,7 @@ export class GameManager {
scene.scene.run(fallbackSceneName)
}
}
public getCurrentGameScene(scene: Phaser.Scene): GameScene {
if (this.currentGameSceneName === null) throw 'No current scene id set!';
return scene.scene.get(this.currentGameSceneName) as GameScene

View file

@ -1,4 +1,4 @@
import {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap";
import type {ITiledMap, ITiledMapLayer} from "../Map/ITiledMap";
import {LayersIterator} from "../Map/LayersIterator";
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;

View file

@ -1,5 +1,5 @@
import {gameManager, HasMovedEvent} from "./GameManager";
import {
import type {
GroupCreatedUpdatedMessageInterface,
MessageUserJoined,
MessageUserMovedInterface,
@ -16,7 +16,7 @@ import {
MAX_PER_GROUP,
POSITION_DELAY,
} from "../../Enum/EnvironmentVariable";
import {
import type {
ITiledMap,
ITiledMapLayer,
ITiledMapLayerProperty,
@ -25,7 +25,7 @@ import {
ITiledMapTileLayer,
ITiledTileSet
} from "../Map/ITiledMap";
import {AddPlayerInterface} from "./AddPlayerInterface";
import type {AddPlayerInterface} from "./AddPlayerInterface";
import {PlayerAnimationDirections} from "../Player/Animation";
import {PlayerMovement} from "./PlayerMovement";
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
@ -49,13 +49,13 @@ import {
import {GameMap} from "./GameMap";
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import {mediaManager} from "../../WebRtc/MediaManager";
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
import {ActionableItem} from "../Items/ActionableItem";
import type {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
import type {ActionableItem} from "../Items/ActionableItem";
import {UserInputManager} from "../UserInput/UserInputManager";
import {UserMovedMessage} from "../../Messages/generated/messages_pb";
import type {UserMovedMessage} from "../../Messages/generated/messages_pb";
import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils";
import {connectionManager} from "../../Connexion/ConnectionManager";
import {RoomConnection} from "../../Connexion/RoomConnection";
import type {RoomConnection} from "../../Connexion/RoomConnection";
import {GlobalMessageManager} from "../../Administration/GlobalMessageManager";
import {userMessageManager} from "../../Administration/UserMessageManager";
import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager";
@ -80,7 +80,7 @@ import CanvasTexture = Phaser.Textures.CanvasTexture;
import GameObject = Phaser.GameObjects.GameObject;
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
import DOMElement = Phaser.GameObjects.DOMElement;
import {Subscription} from "rxjs";
import type {Subscription} from "rxjs";
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
import RenderTexture = Phaser.GameObjects.RenderTexture;
@ -153,7 +153,7 @@ export class GameScene extends DirtyScene implements CenterListener {
private GlobalMessageManager!: GlobalMessageManager;
public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
private connectionAnswerPromiseResolve!: (value: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
// A promise that will resolve when the "create" method is called (signaling loading is ended)
private createPromise: Promise<void>;
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
@ -188,6 +188,8 @@ export class GameScene extends DirtyScene implements CenterListener {
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
private originalMapUrl: string|undefined;
private pinchManager: PinchManager|undefined;
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
private onVisibilityChangeCallback: () => void;
constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
super({
@ -203,10 +205,11 @@ export class GameScene extends DirtyScene implements CenterListener {
this.createPromise = new Promise<void>((resolve, reject): void => {
this.createPromiseResolve = resolve;
})
});
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
this.connectionAnswerPromiseResolve = resolve;
});
this.onVisibilityChangeCallback = this.onVisibilityChange.bind(this);
}
//hook preload scene
@ -505,6 +508,8 @@ export class GameScene extends DirtyScene implements CenterListener {
if (!this.room.isDisconnected()) {
this.connect();
}
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
}
/**
@ -626,6 +631,7 @@ export class GameScene extends DirtyScene implements CenterListener {
self.chatModeSprite.setVisible(false);
self.openChatIcon.setVisible(false);
audioManager.restoreVolume();
self.onVisibilityChange();
}
}
})
@ -884,6 +890,8 @@ ${escapedMessage}
}
private onMapExit(exitKey: string) {
if (this.mapTransitioning) return;
this.mapTransitioning = true;
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance);
if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);
urlManager.pushStartLayerNameToUrl(hash);
@ -903,6 +911,7 @@ ${escapedMessage}
this.initPositionFromLayerName(hash || defaultStartLayerName);
this.CurrentPlayer.x = this.startX;
this.CurrentPlayer.y = this.startY;
setTimeout(() => this.mapTransitioning = false, 500);
}
}
@ -928,6 +937,8 @@ ${escapedMessage}
for(const iframeEvents of this.iframeSubscriptionList){
iframeEvents.unsubscribe();
}
document.removeEventListener('visibilitychange', this.onVisibilityChangeCallback);
}
private removeAllRemotePlayers(): void {
@ -1492,6 +1503,8 @@ ${escapedMessage}
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
this.stopJitsi();
});
this.onVisibilityChange();
}
public stopJitsi(): void {
@ -1500,6 +1513,7 @@ ${escapedMessage}
mediaManager.showGameOverlay();
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
this.onVisibilityChange();
}
//todo: put this into an 'orchestrator' scene (EntryScene?)
@ -1539,4 +1553,20 @@ ${escapedMessage}
waScaleManager.zoomModifier *= zoomFactor;
this.updateCameraOffset();
}
private onVisibilityChange(): void {
// If the overlay is not displayed, we are in Jitsi. We don't need the webcam.
if (!mediaManager.isGameOverlayVisible()) {
mediaManager.blurCamera();
return;
}
if (document.visibilityState === 'visible') {
mediaManager.focusCamera();
} else {
if (this.simplePeer.getNbConnections() === 0) {
mediaManager.blurCamera();
}
}
}
}

View file

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

View file

@ -2,8 +2,8 @@
* This class is in charge of computing the position of all players.
* Player movement is delayed by 200ms so position depends on ticks.
*/
import {PlayerMovement} from "./PlayerMovement";
import {HasMovedEvent} from "./GameManager";
import type {PlayerMovement} from "./PlayerMovement";
import type {HasMovedEvent} from "./GameManager";
export class PlayersPositionInterpolator {
playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>();