Merge branch 'develop' of github.com:thecodingmachine/workadventure into metadataScriptingApi

This commit is contained in:
GRL 2021-05-18 11:50:03 +02:00
commit 2ee62c9e9e
84 changed files with 2218 additions and 1648 deletions

View File

@ -10,6 +10,8 @@ START_ROOM_URL=/_/global/maps.workadventure.localhost/Floor0/floor0.json
# If you are using Coturn, this is the value of the "static-auth-secret" parameter in your coturn config file. # If you are using Coturn, this is the value of the "static-auth-secret" parameter in your coturn config file.
# Keep empty if you are sharing hard coded / clear text credentials. # Keep empty if you are sharing hard coded / clear text credentials.
TURN_STATIC_AUTH_SECRET= TURN_STATIC_AUTH_SECRET=
DISABLE_NOTIFICATIONS=true
SKIP_RENDER_OPTIMIZATIONS=false
# The email address used by Let's encrypt to send renewal warnings (compulsory) # The email address used by Let's encrypt to send renewal warnings (compulsory)
ACME_EMAIL= ACME_EMAIL=

View File

@ -2,7 +2,7 @@ name: Build, push and deploy Docker image
on: on:
push: push:
branch: [master] branches: [master]
release: release:
types: [created] types: [created]
pull_request: pull_request:
@ -16,7 +16,7 @@ env:
jobs: jobs:
build-front: build-front:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }} if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -36,11 +36,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-front repository: thecodingmachine/workadventure-front
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }} tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true add_git_labels: true
build-back: build-back:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }} if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -59,11 +59,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-back repository: thecodingmachine/workadventure-back
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }} tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true add_git_labels: true
build-pusher: build-pusher:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }} if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -82,11 +82,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-pusher repository: thecodingmachine/workadventure-pusher
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }} tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true add_git_labels: true
build-uploader: build-uploader:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }} if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -105,11 +105,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-uploader repository: thecodingmachine/workadventure-uploader
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }} tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true add_git_labels: true
build-maps: build-maps:
if: ${{ github.event.release || github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }} if: ${{ github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -129,7 +129,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-maps repository: thecodingmachine/workadventure-maps
tags: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || github.event.release && env.GITHUB_REF_SLUG || 'master' }} tags: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
add_git_labels: true add_git_labels: true
deeploy: deeploy:
@ -140,7 +140,7 @@ jobs:
- build-maps - build-maps
- build-uploader - build-uploader
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event.push || contains(github.event.pull_request.labels.*.name, 'deploy') }} if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') }}
steps: steps:
- name: Checkout - name: Checkout
@ -158,13 +158,13 @@ jobs:
JITSI_URL: ${{ secrets.JITSI_URL }} JITSI_URL: ${{ secrets.JITSI_URL }}
SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }} SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }}
TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }} TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }}
DEPLOY_REF: ${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || 'master' }} DEPLOY_REF: ${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
with: with:
namespace: workadventure-${{ github.event.pull_request && env.GITHUB_HEAD_REF_SLUG || 'master' }} namespace: workadventure-${{ github.event_name == 'pull_request' && env.GITHUB_HEAD_REF_SLUG || env.GITHUB_REF_SLUG }}
- name: Add a comment in PR - name: Add a comment in PR
uses: unsplash/comment-on-pr@v1.2.0 uses: unsplash/comment-on-pr@v1.2.0
if: ${{ github.event.pull_request }} if: ${{ github.event_name == 'pull_request' }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:

View File

@ -49,7 +49,7 @@ jobs:
- name: "Build" - name: "Build"
run: yarn run build run: yarn run build
env: env:
API_URL: "localhost:8080" PUSHER_URL: "//localhost:8080"
working-directory: "front" working-directory: "front"
- name: "Lint" - name: "Lint"

View File

@ -2,7 +2,7 @@
local env = std.extVar("env"), local env = std.extVar("env"),
local namespace = env.DEPLOY_REF, local namespace = env.DEPLOY_REF,
local tag = namespace, local tag = namespace,
local url = if namespace == "master" then "workadventu.re" else namespace+".test.workadventu.re", local url = namespace+".test.workadventu.re",
// develop branch does not use admin because of issue with SSL certificate of admin as of now. // develop branch does not use admin because of issue with SSL certificate of admin as of now.
local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null, local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null,
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json", "$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
@ -25,10 +25,7 @@
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET, "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + (if adminUrl != null then { } + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl, "ADMIN_API_URL": adminUrl,
} else {}) + if namespace != "master" then { } else {})
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}, },
"back2": { "back2": {
"image": "thecodingmachine/workadventure-back:"+tag, "image": "thecodingmachine/workadventure-back:"+tag,
@ -47,10 +44,7 @@
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET, "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + (if adminUrl != null then { } + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl, "ADMIN_API_URL": adminUrl,
} else {}) + if namespace != "master" then { } else {})
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}, },
"pusher": { "pusher": {
"replicas": 2, "replicas": 2,
@ -69,10 +63,7 @@
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
} + (if adminUrl != null then { } + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl, "ADMIN_API_URL": adminUrl,
} else {}) + if namespace != "master" then { } else {})
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}, },
"front": { "front": {
"image": "thecodingmachine/workadventure-front:"+tag, "image": "thecodingmachine/workadventure-front:"+tag,

View File

@ -33,6 +33,8 @@ services:
STARTUP_COMMAND_1: ./templater.sh STARTUP_COMMAND_1: ./templater.sh
STARTUP_COMMAND_2: yarn install STARTUP_COMMAND_2: yarn install
TURN_SERVER: "turn:localhost:3478,turns:localhost:5349" TURN_SERVER: "turn:localhost:3478,turns:localhost:5349"
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials. # Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container # Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
TURN_USER: "" TURN_USER: ""

View File

@ -33,6 +33,8 @@ services:
STARTUP_COMMAND_2: yarn install STARTUP_COMMAND_2: yarn install
STUN_SERVER: "stun:stun.l.google.com:19302" STUN_SERVER: "stun:stun.l.google.com:19302"
TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349" TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349"
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials. # Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
# Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container # Advice: you should instead use Coturn REST API along the TURN_STATIC_AUTH_SECRET in the Back container
TURN_USER: "" TURN_USER: ""

View File

@ -25,6 +25,15 @@
], ],
"rules": { "rules": {
"no-unused-vars": "off", "no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "error" "@typescript-eslint/no-explicit-any": "error",
// TODO: remove those ignored rules and write a stronger code!
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/restrict-template-expressions": "off"
} }
} }

View File

@ -38,6 +38,8 @@
<div class="main-container" id="main-container"> <div class="main-container" id="main-container">
<!-- Create the editor container --> <!-- Create the editor container -->
<div id="game" class="game"> <div id="game" class="game">
<div id="svelte-overlay">
</div>
<div id="game-overlay" class="game-overlay"> <div id="game-overlay" class="game-overlay">
<div id="main-section" class="main-section"> <div id="main-section" class="main-section">
</div> </div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -4,25 +4,34 @@
"main": "index.js", "main": "index.js",
"license": "SEE LICENSE IN LICENSE.txt", "license": "SEE LICENSE IN LICENSE.txt",
"devDependencies": { "devDependencies": {
"@tsconfig/svelte": "^1.0.10",
"@types/google-protobuf": "^3.7.3", "@types/google-protobuf": "^3.7.3",
"@types/jasmine": "^3.5.10", "@types/jasmine": "^3.5.10",
"@types/mini-css-extract-plugin": "^1.4.3",
"@types/node": "^15.3.0",
"@types/quill": "^1.3.7", "@types/quill": "^1.3.7",
"@typescript-eslint/eslint-plugin": "^2.26.0", "@types/webpack-dev-server": "^3.11.4",
"@typescript-eslint/parser": "^2.26.0", "@typescript-eslint/eslint-plugin": "^4.23.0",
"css-loader": "^5.1.3", "@typescript-eslint/parser": "^4.23.0",
"eslint": "^6.8.0", "css-loader": "^5.2.4",
"html-webpack-plugin": "^4.3.0", "eslint": "^7.26.0",
"fork-ts-checker-webpack-plugin": "^6.2.9",
"html-webpack-plugin": "^5.3.1",
"jasmine": "^3.5.0", "jasmine": "^3.5.0",
"mini-css-extract-plugin": "^1.3.9", "mini-css-extract-plugin": "^1.6.0",
"sass": "^1.32.8", "node-polyfill-webpack-plugin": "^1.1.2",
"sass-loader": "10.1.1", "sass": "^1.32.12",
"ts-loader": "^6.2.2", "sass-loader": "^11.1.0",
"ts-node": "^8.10.2", "svelte": "^3.38.2",
"typescript": "^3.8.3", "svelte-loader": "^3.1.1",
"webpack": "^4.42.1", "svelte-preprocess": "^4.7.3",
"webpack-cli": "^3.3.11", "ts-loader": "^9.1.2",
"webpack-dev-server": "^3.10.3", "ts-node": "^9.1.1",
"webpack-merge": "^4.2.2" "tsconfig-paths": "^3.9.0",
"typescript": "^4.2.4",
"webpack": "^5.37.0",
"webpack-cli": "^4.7.0",
"webpack-dev-server": "^3.11.2"
}, },
"dependencies": { "dependencies": {
"@types/simple-peer": "^9.6.0", "@types/simple-peer": "^9.6.0",
@ -36,13 +45,12 @@
"quill": "1.3.6", "quill": "1.3.6",
"rxjs": "^6.6.3", "rxjs": "^6.6.3",
"simple-peer": "^9.6.2", "simple-peer": "^9.6.2",
"socket.io-client": "^2.3.0", "socket.io-client": "^2.3.0"
"webpack-require-http": "^0.4.3"
}, },
"scripts": { "scripts": {
"start": "webpack-dev-server --open", "start": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open",
"build": "webpack --config webpack.prod.js", "build": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack",
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "test": "TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
"lint": "node_modules/.bin/eslint src/ . --ext .ts", "lint": "node_modules/.bin/eslint src/ . --ext .ts",
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts" "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts"
} }

View File

@ -1,7 +1,7 @@
import {HtmlUtils} from "../WebRtc/HtmlUtils"; import {HtmlUtils} from "../WebRtc/HtmlUtils";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {RoomConnection} from "../Connexion/RoomConnection"; import type {RoomConnection} from "../Connexion/RoomConnection";
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels"; import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService"; import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService";
export const CLASS_CONSOLE_MESSAGE = 'main-console'; export const CLASS_CONSOLE_MESSAGE = 'main-console';

View File

@ -1,8 +1,8 @@
import {HtmlUtils} from "./../WebRtc/HtmlUtils"; import {HtmlUtils} from "./../WebRtc/HtmlUtils";
import {AUDIO_TYPE, MESSAGE_TYPE} from "./ConsoleGlobalMessageManager"; import {AUDIO_TYPE, MESSAGE_TYPE} from "./ConsoleGlobalMessageManager";
import {PUSHER_URL, UPLOADER_URL} from "../Enum/EnvironmentVariable"; import {PUSHER_URL, UPLOADER_URL} from "../Enum/EnvironmentVariable";
import {RoomConnection} from "../Connexion/RoomConnection"; import type {RoomConnection} from "../Connexion/RoomConnection";
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels"; import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
export class GlobalMessageManager { export class GlobalMessageManager {

View File

@ -1,4 +1,4 @@
import {TypeMessageInterface} from "./UserMessageManager"; import type {TypeMessageInterface} from "./UserMessageManager";
import {HtmlUtils} from "../WebRtc/HtmlUtils"; import {HtmlUtils} from "../WebRtc/HtmlUtils";
let modalTimeOut : NodeJS.Timeout; let modalTimeOut : NodeJS.Timeout;
@ -86,4 +86,4 @@ export class Banned extends TypeMessageExt {
showMessage(message: string){ showMessage(message: string){
super.showMessage(message, false); super.showMessage(message, false);
} }
} }

View File

@ -1,16 +1,16 @@
import { ButtonClickedEvent } from './ButtonClickedEvent'; import type { ButtonClickedEvent } from './ButtonClickedEvent';
import { ChatEvent } from './ChatEvent'; import type { ChatEvent } from './ChatEvent';
import { ClosePopupEvent } from './ClosePopupEvent'; import type { ClosePopupEvent } from './ClosePopupEvent';
import { EnterLeaveEvent } from './EnterLeaveEvent'; import type { EnterLeaveEvent } from './EnterLeaveEvent';
import { GoToPageEvent } from './GoToPageEvent'; import type { GoToPageEvent } from './GoToPageEvent';
import { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent'; import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
import { OpenPopupEvent } from './OpenPopupEvent'; import type { OpenPopupEvent } from './OpenPopupEvent';
import { OpenTabEvent } from './OpenTabEvent'; import type { OpenTabEvent } from './OpenTabEvent';
import { UserInputChatEvent } from './UserInputChatEvent'; import type { UserInputChatEvent } from './UserInputChatEvent';
import { LayerEvent } from './LayerEvent'; import type { LayerEvent } from './LayerEvent';
import { SetPropertyEvent } from "./setPropertyEvent"; import type { SetPropertyEvent } from "./setPropertyEvent";
export interface TypedMessageEvent<T> extends MessageEvent { export interface TypedMessageEvent<T> extends MessageEvent {

View File

@ -1,17 +1,16 @@
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import { ChatEvent, isChatEvent } from "./Events/ChatEvent"; import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
import * as crypto from "crypto";
import { HtmlUtils } from "../WebRtc/HtmlUtils"; import { HtmlUtils } from "../WebRtc/HtmlUtils";
import { EnterLeaveEvent } from "./Events/EnterLeaveEvent"; import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent"; import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent"; import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
import { ButtonClickedEvent } from "./Events/ButtonClickedEvent"; import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent"; import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
import { scriptUtils } from "./ScriptUtils"; import { scriptUtils } from "./ScriptUtils";
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent"; import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent"; import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent"; import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent";
import { UserInputChatEvent } from "./Events/UserInputChatEvent"; import type { UserInputChatEvent } from "./Events/UserInputChatEvent";
import { isLayerEvent, LayerEvent } from "./Events/LayerEvent"; import { isLayerEvent, LayerEvent } from "./Events/LayerEvent";
import { isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent"; import { isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent";
@ -193,7 +192,7 @@ class IframeListener {
} }
private getIFrameId(scriptUrl: string): string { private getIFrameId(scriptUrl: string): string {
return 'script' + crypto.createHash('md5').update(scriptUrl).digest("hex"); return 'script' + btoa(scriptUrl);
} }
unregisterScript(scriptUrl: string): void { unregisterScript(scriptUrl: string): void {

View File

@ -0,0 +1,11 @@
<script lang="typescript">
import MenuIcon from "./Menu/MenuIcon.svelte";
import {menuIconVisible} from "../Stores/MenuStore";
</script>
<div>
<!-- {#if $menuIconVisible}
<MenuIcon />
{/if} -->
</div>

View File

@ -0,0 +1,33 @@
<script lang="typescript">
</script>
<main class="menuIcon">
<section>
<button>
<img src="/static/images/menu.svg" alt="Open menu">
</button>
</section>
</main>
<style lang="scss">
.menuIcon button {
background-color: black;
color: white;
border-radius: 7px;
padding: 2px 8px;
img {
width: 14px;
padding-top: 0;
/*cursor: url('/resources/logos/cursor_pointer.png'), pointer;*/
}
}
.menuIcon section {
margin: 10px;
}
@media only screen and (max-height: 700px) {
.menuIcon section {
margin: 2px;
}
}
</style>

View File

@ -1,5 +1,5 @@
import {Subject} from "rxjs"; import {Subject} from "rxjs";
import {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb"; import type {BanUserMessage, SendUserMessage} from "../Messages/generated/messages_pb";
export enum AdminMessageEventTypes { export enum AdminMessageEventTypes {
admin = 'message', admin = 'message',
@ -19,11 +19,11 @@ interface AdminMessageEvent {
class AdminMessagesService { class AdminMessagesService {
private _messageStream: Subject<AdminMessageEvent> = new Subject(); private _messageStream: Subject<AdminMessageEvent> = new Subject();
public messageStream = this._messageStream.asObservable(); public messageStream = this._messageStream.asObservable();
constructor() { constructor() {
this.messageStream.subscribe((event) => console.log('message', event)) this.messageStream.subscribe((event) => console.log('message', event))
} }
onSendusermessage(message: SendUserMessage|BanUserMessage) { onSendusermessage(message: SendUserMessage|BanUserMessage) {
this._messageStream.next({ this._messageStream.next({
type: message.getType() as unknown as AdminMessageEventTypes, type: message.getType() as unknown as AdminMessageEventTypes,
@ -32,4 +32,4 @@ class AdminMessagesService {
} }
} }
export const adminMessagesService = new AdminMessagesService(); export const adminMessagesService = new AdminMessagesService();

View File

@ -1,7 +1,7 @@
import Axios from "axios"; import Axios from "axios";
import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable"; import {PUSHER_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable";
import {RoomConnection} from "./RoomConnection"; import {RoomConnection} from "./RoomConnection";
import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels"; import type {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
import {localUserStore} from "./LocalUserStore"; import {localUserStore} from "./LocalUserStore";
import {LocalUser} from "./LocalUser"; import {LocalUser} from "./LocalUser";

View File

@ -1,8 +1,8 @@
import {PlayerAnimationDirections} from "../Phaser/Player/Animation"; import {PlayerAnimationDirections} from "../Phaser/Player/Animation";
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
import {SignalData} from "simple-peer"; import type {SignalData} from "simple-peer";
import {RoomConnection} from "./RoomConnection"; import type {RoomConnection} from "./RoomConnection";
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
export enum EventMessage{ export enum EventMessage{
CONNECT = "connect", CONNECT = "connect",

View File

@ -27,11 +27,11 @@ import {
SendJitsiJwtMessage, SendJitsiJwtMessage,
CharacterLayerMessage, CharacterLayerMessage,
PingMessage, PingMessage,
SendUserMessage, SendUserMessage,
BanUserMessage BanUserMessage
} from "../Messages/generated/messages_pb" } from "../Messages/generated/messages_pb"
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import type {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
import Direction = PositionMessage.Direction; import Direction = PositionMessage.Direction;
import {ProtobufClientUtils} from "../Network/ProtobufClientUtils"; import {ProtobufClientUtils} from "../Network/ProtobufClientUtils";
import { import {
@ -42,7 +42,7 @@ import {
ViewportInterface, WebRtcDisconnectMessageInterface, ViewportInterface, WebRtcDisconnectMessageInterface,
WebRtcSignalReceivedMessageInterface, WebRtcSignalReceivedMessageInterface,
} from "./ConnexionModels"; } from "./ConnexionModels";
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures"; import type {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
import {adminMessagesService} from "./AdminMessagesService"; import {adminMessagesService} from "./AdminMessagesService";
import {worldFullMessageStream} from "./WorldFullMessageStream"; import {worldFullMessageStream} from "./WorldFullMessageStream";
import {worldFullWarningStream} from "./WorldFullWarningStream"; import {worldFullWarningStream} from "./WorldFullWarningStream";
@ -86,7 +86,7 @@ export class RoomConnection implements RoomConnection {
url += '&bottom='+Math.floor(viewport.bottom); url += '&bottom='+Math.floor(viewport.bottom);
url += '&left='+Math.floor(viewport.left); url += '&left='+Math.floor(viewport.left);
url += '&right='+Math.floor(viewport.right); url += '&right='+Math.floor(viewport.right);
if (typeof companion === 'string') { if (typeof companion === 'string') {
url += '&companion='+encodeURIComponent(companion); url += '&companion='+encodeURIComponent(companion);
} }

View File

@ -1,11 +1,11 @@
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true"; const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json'; const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json';
// For compatibility reasons with older versions, API_URL is the old host name of PUSHER_URL const PUSHER_URL = process.env.PUSHER_URL || '//pusher.workadventure.localhost';
const PUSHER_URL = process.env.PUSHER_URL || (process.env.API_URL ? '//'+process.env.API_URL : "//pusher.workadventure.localhost");
const UPLOADER_URL = process.env.UPLOADER_URL || '//uploader.workadventure.localhost'; const UPLOADER_URL = process.env.UPLOADER_URL || '//uploader.workadventure.localhost';
const ADMIN_URL = process.env.ADMIN_URL || "//workadventure.localhost";
const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302"; const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
const TURN_SERVER: string = process.env.TURN_SERVER || ""; const TURN_SERVER: string = process.env.TURN_SERVER || "";
const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true";
const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true";
const TURN_USER: string = process.env.TURN_USER || ''; const TURN_USER: string = process.env.TURN_USER || '';
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || ''; const TURN_PASSWORD: string = process.env.TURN_PASSWORD || '';
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
@ -20,9 +20,10 @@ export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window
export { export {
DEBUG_MODE, DEBUG_MODE,
START_ROOM_URL, START_ROOM_URL,
SKIP_RENDER_OPTIMIZATIONS,
DISABLE_NOTIFICATIONS,
PUSHER_URL, PUSHER_URL,
UPLOADER_URL, UPLOADER_URL,
ADMIN_URL,
POSITION_DELAY, POSITION_DELAY,
MAX_EXTRAPOLATION_TIME, MAX_EXTRAPOLATION_TIME,
STUN_SERVER, STUN_SERVER,

View File

@ -1,6 +1,6 @@
import {PositionMessage} from "../Messages/generated/messages_pb"; import {PositionMessage} from "../Messages/generated/messages_pb";
import Direction = PositionMessage.Direction; import Direction = PositionMessage.Direction;
import {PointInterface} from "../Connexion/ConnexionModels"; import type {PointInterface} from "../Connexion/ConnexionModels";
export class ProtobufClientUtils { export class ProtobufClientUtils {

View File

@ -1,6 +1,5 @@
import Sprite = Phaser.GameObjects.Sprite; import Sprite = Phaser.GameObjects.Sprite;
import Container = Phaser.GameObjects.Container; import Container = Phaser.GameObjects.Container;
import { lazyLoadCompanionResource } from "./CompanionTexturesLoadingManager";
import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation";
export interface CompanionStatus { export interface CompanionStatus {
@ -25,7 +24,7 @@ export class Companion extends Container {
constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise<string>) { constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise<string>) {
super(scene, x + 14, y + 4); super(scene, x + 14, y + 4);
this.sprites = new Map<string, Sprite>(); this.sprites = new Map<string, Sprite>();
this.delta = 0; this.delta = 0;
@ -104,7 +103,7 @@ export class Companion extends Container {
} }
} }
} }
this.setDepth(this.y); this.setDepth(this.y);
this.playAnimation(this.direction, this.animationType); this.playAnimation(this.direction, this.animationType);
} }
@ -137,7 +136,7 @@ export class Companion extends Container {
this.getAnimations(resource).forEach(animation => { this.getAnimations(resource).forEach(animation => {
this.scene.anims.create(animation); this.scene.anims.create(animation);
}); });
this.scene.sys.updateList.add(sprite); this.scene.sys.updateList.add(sprite);
this.sprites.set(resource, sprite); this.sprites.set(resource, sprite);
} }

View File

@ -1,5 +1,4 @@
import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js';
import ScaleManager = Phaser.Scale.ScaleManager;
import {waScaleManager} from "../Services/WaScaleManager"; import {waScaleManager} from "../Services/WaScaleManager";
//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free //the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free

View File

@ -17,14 +17,12 @@ export class SoundMeter {
} }
private init(context: AudioContext) { private init(context: AudioContext) {
if (this.context === undefined) { this.context = context;
this.context = context; this.analyser = this.context.createAnalyser();
this.analyser = this.context.createAnalyser();
this.analyser.fftSize = 2048; this.analyser.fftSize = 2048;
const bufferLength = this.analyser.fftSize; const bufferLength = this.analyser.fftSize;
this.dataArray = new Uint8Array(bufferLength); this.dataArray = new Uint8Array(bufferLength);
}
} }
public connectToSource(stream: MediaStream, context: AudioContext): void public connectToSource(stream: MediaStream, context: AudioContext): void

View File

@ -1,5 +1,5 @@
import Container = Phaser.GameObjects.Container; import Container = Phaser.GameObjects.Container;
import {Scene} from "phaser"; import type {Scene} from "phaser";
import GameObject = Phaser.GameObjects.GameObject; import GameObject = Phaser.GameObjects.GameObject;
import Rectangle = Phaser.GameObjects.Rectangle; import Rectangle = Phaser.GameObjects.Rectangle;

View File

@ -1,7 +1,5 @@
import {ITiledMapObject} from "../Map/ITiledMap"; import type {ITiledMapObject} from "../Map/ITiledMap";
import Text = Phaser.GameObjects.Text; import type {GameScene} from "../Game/GameScene";
import {GameScene} from "../Game/GameScene";
import TextStyle = Phaser.GameObjects.TextStyle;
export class TextUtils { export class TextUtils {
public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void { public static createTextFromITiledMapObject(scene: GameScene, object: ITiledMapObject): void {

View File

@ -1,6 +1,5 @@
import LoaderPlugin = Phaser.Loader.LoaderPlugin; import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import TextureManager = Phaser.Textures.TextureManager; import type {CharacterTexture} from "../../Connexion/LocalUser";
import {CharacterTexture} from "../../Connexion/LocalUser";
import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures"; import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures";

View File

@ -1,7 +1,7 @@
import {GameScene} from "../Game/GameScene"; import type {GameScene} from "../Game/GameScene";
import {PointInterface} from "../../Connexion/ConnexionModels"; import type {PointInterface} from "../../Connexion/ConnexionModels";
import {Character} from "../Entity/Character"; import {Character} from "../Entity/Character";
import {PlayerAnimationDirections} from "../Player/Animation"; import type {PlayerAnimationDirections} from "../Player/Animation";
/** /**
* Class representing the sprite of a remote player (a player that plays on another computer) * Class representing the sprite of a remote player (a player that plays on another computer)
@ -22,7 +22,7 @@ export class RemotePlayer extends Character {
companionTexturePromise?: Promise<string> companionTexturePromise?: Promise<string>
) { ) {
super(Scene, x, y, texturesPromise, name, direction, moving, 1); super(Scene, x, y, texturesPromise, name, direction, moving, 1);
//set data //set data
this.userId = userId; this.userId = userId;
@ -35,9 +35,9 @@ export class RemotePlayer extends Character {
this.playAnimation(position.direction as PlayerAnimationDirections, position.moving); this.playAnimation(position.direction as PlayerAnimationDirections, position.moving);
this.setX(position.x); this.setX(position.x);
this.setY(position.y); this.setY(position.y);
this.setDepth(position.y); //this is to make sure the perspective (player models closer the bottom of the screen will appear in front of models nearer the top of the screen). this.setDepth(position.y); //this is to make sure the perspective (player models closer the bottom of the screen will appear in front of models nearer the top of the screen).
if (this.companion) { if (this.companion) {
this.companion.setTarget(position.x, position.y, position.direction as PlayerAnimationDirections); this.companion.setTarget(position.x, position.y, position.direction as PlayerAnimationDirections);
} }

View File

@ -1,12 +1,12 @@
import Scene = Phaser.Scene; import Scene = Phaser.Scene;
import {Character} from "./Character"; import type {Character} from "./Character";
//todo: improve this WIP //todo: improve this WIP
export class SpeechBubble { export class SpeechBubble {
private bubble: Phaser.GameObjects.Graphics; private bubble: Phaser.GameObjects.Graphics;
private content: Phaser.GameObjects.Text; private content: Phaser.GameObjects.Text;
constructor(scene: Scene, player: Character, text: string = "") { constructor(scene: Scene, player: Character, text: string = "") {
const bubbleHeight = 50; const bubbleHeight = 50;

View File

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

View File

@ -2,6 +2,8 @@ import {ResizableScene} from "../Login/ResizableScene";
import GameObject = Phaser.GameObjects.GameObject; import GameObject = Phaser.GameObjects.GameObject;
import Events = Phaser.Scenes.Events; import Events = Phaser.Scenes.Events;
import AnimationEvents = Phaser.Animations.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. * 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. * Note: this does not work with animations from sprites inside containers.
*/ */
protected trackDirtyAnims(): void { protected trackDirtyAnims(): void {
if (this.isAlreadyTracking) { if (this.isAlreadyTracking || SKIP_RENDER_OPTIMIZATIONS) {
return; return;
} }
this.isAlreadyTracking = true; this.isAlreadyTracking = true;
const trackAnimationFunction = this.trackAnimation.bind(this); 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; this.objectListChanged = true;
gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction); gameObject.on(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction);
}); });
this.sys.updateList.on(StructEvents.PROCESS_QUEUE_REMOVE, (gameObject: GameObject) => {
this.events.on(Events.REMOVED_FROM_SCENE, (gameObject: GameObject) => {
this.objectListChanged = true; this.objectListChanged = true;
gameObject.removeListener(AnimationEvents.ANIMATION_UPDATE, trackAnimationFunction); 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; const Events = Phaser.Core.Events;
/** /**
@ -5,8 +10,27 @@ const Events = Phaser.Core.Events;
* It comes with an optimization to skip rendering. * It comes with an optimization to skip rendering.
* *
* Beware, the "step" function might vary in future versions of Phaser. * 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 { 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) public step(time: number, delta: number)
{ {
// @ts-ignore // @ts-ignore
@ -35,7 +59,7 @@ export class Game extends Phaser.Game {
eventEmitter.emit(Events.POST_STEP, time, delta); eventEmitter.emit(Events.POST_STEP, time, delta);
// This "if" is the changed introduced by the new "Game" class to avoid rendering unnecessarily. // 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; const renderer = this.renderer;
// Run the Pre-render (clearing the canvas, setting background colors, etc) // Run the Pre-render (clearing the canvas, setting background colors, etc)
@ -62,6 +86,11 @@ export class Game extends Phaser.Game {
} }
private isDirty(): boolean { private isDirty(): boolean {
if (this._isDirty) {
this._isDirty = false;
return true;
}
// Loop through the scenes in forward order // Loop through the scenes in forward order
for (let i = 0; i < this.scene.scenes.length; i++) for (let i = 0; i < this.scene.scenes.length; i++)
{ {
@ -85,4 +114,11 @@ export class Game extends Phaser.Game {
return false; 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 {GameScene} from "./GameScene";
import {connectionManager} from "../../Connexion/ConnectionManager"; import {connectionManager} from "../../Connexion/ConnectionManager";
import {Room} from "../../Connexion/Room"; import type {Room} from "../../Connexion/Room";
import {MenuScene, MenuSceneName} from "../Menu/MenuScene"; import {MenuScene, MenuSceneName} from "../Menu/MenuScene";
import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene"; import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene";
import {LoginSceneName} from "../Login/LoginScene"; import {LoginSceneName} from "../Login/LoginScene";
@ -24,7 +24,7 @@ export class GameManager {
private companion: string|null; private companion: string|null;
private startRoom!:Room; private startRoom!:Room;
currentGameSceneName: string|null = null; currentGameSceneName: string|null = null;
constructor() { constructor() {
this.playerName = localUserStore.getName(); this.playerName = localUserStore.getName();
this.characterLayers = localUserStore.getCharacterLayers(); this.characterLayers = localUserStore.getCharacterLayers();
@ -65,7 +65,7 @@ export class GameManager {
return this.characterLayers; return this.characterLayers;
} }
setCompanion(companion: string|null): void { setCompanion(companion: string|null): void {
this.companion = companion; this.companion = companion;
} }
@ -91,7 +91,7 @@ export class GameManager {
scenePlugin.launch(MenuSceneName); scenePlugin.launch(MenuSceneName);
scenePlugin.launch(HelpCameraSettingsSceneName);//700 scenePlugin.launch(HelpCameraSettingsSceneName);//700
} }
public gameSceneIsCreated(scene: GameScene) { public gameSceneIsCreated(scene: GameScene) {
this.currentGameSceneName = scene.scene.key; this.currentGameSceneName = scene.scene.key;
const menuScene: MenuScene = scene.scene.get(MenuSceneName) as MenuScene; const menuScene: MenuScene = scene.scene.get(MenuSceneName) as MenuScene;
@ -125,7 +125,7 @@ export class GameManager {
scene.scene.run(fallbackSceneName) scene.scene.run(fallbackSceneName)
} }
} }
public getCurrentGameScene(scene: Phaser.Scene): GameScene { public getCurrentGameScene(scene: Phaser.Scene): GameScene {
if (this.currentGameSceneName === null) throw 'No current scene id set!'; if (this.currentGameSceneName === null) throw 'No current scene id set!';
return scene.scene.get(this.currentGameSceneName) as GameScene return scene.scene.get(this.currentGameSceneName) as GameScene

View File

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

View File

@ -1,5 +1,5 @@
import {gameManager, HasMovedEvent} from "./GameManager"; import {gameManager, HasMovedEvent} from "./GameManager";
import { import type {
GroupCreatedUpdatedMessageInterface, GroupCreatedUpdatedMessageInterface,
MessageUserJoined, MessageUserJoined,
MessageUserMovedInterface, MessageUserMovedInterface,
@ -16,7 +16,7 @@ import {
MAX_PER_GROUP, MAX_PER_GROUP,
POSITION_DELAY, POSITION_DELAY,
} from "../../Enum/EnvironmentVariable"; } from "../../Enum/EnvironmentVariable";
import { import type {
ITiledMap, ITiledMap,
ITiledMapLayer, ITiledMapLayer,
ITiledMapLayerProperty, ITiledMapLayerProperty,
@ -25,7 +25,7 @@ import {
ITiledMapTileLayer, ITiledMapTileLayer,
ITiledTileSet ITiledTileSet
} from "../Map/ITiledMap"; } from "../Map/ITiledMap";
import {AddPlayerInterface} from "./AddPlayerInterface"; import type {AddPlayerInterface} from "./AddPlayerInterface";
import {PlayerAnimationDirections} from "../Player/Animation"; import {PlayerAnimationDirections} from "../Player/Animation";
import {PlayerMovement} from "./PlayerMovement"; import {PlayerMovement} from "./PlayerMovement";
import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator";
@ -49,13 +49,13 @@ import {
import {GameMap} from "./GameMap"; import {GameMap} from "./GameMap";
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import {mediaManager} from "../../WebRtc/MediaManager"; import {mediaManager} from "../../WebRtc/MediaManager";
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; import type {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
import {ActionableItem} from "../Items/ActionableItem"; import type {ActionableItem} from "../Items/ActionableItem";
import {UserInputManager} from "../UserInput/UserInputManager"; 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 {ProtobufClientUtils} from "../../Network/ProtobufClientUtils";
import {connectionManager} from "../../Connexion/ConnectionManager"; import {connectionManager} from "../../Connexion/ConnectionManager";
import {RoomConnection} from "../../Connexion/RoomConnection"; import type {RoomConnection} from "../../Connexion/RoomConnection";
import {GlobalMessageManager} from "../../Administration/GlobalMessageManager"; import {GlobalMessageManager} from "../../Administration/GlobalMessageManager";
import {userMessageManager} from "../../Administration/UserMessageManager"; import {userMessageManager} from "../../Administration/UserMessageManager";
import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager"; import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager";
@ -80,7 +80,7 @@ import CanvasTexture = Phaser.Textures.CanvasTexture;
import GameObject = Phaser.GameObjects.GameObject; import GameObject = Phaser.GameObjects.GameObject;
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
import DOMElement = Phaser.GameObjects.DOMElement; import DOMElement = Phaser.GameObjects.DOMElement;
import {Subscription} from "rxjs"; import type {Subscription} from "rxjs";
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
import RenderTexture = Phaser.GameObjects.RenderTexture; import RenderTexture = Phaser.GameObjects.RenderTexture;
@ -152,7 +152,7 @@ export class GameScene extends DirtyScene implements CenterListener {
private GlobalMessageManager!: GlobalMessageManager; private GlobalMessageManager!: GlobalMessageManager;
public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager; public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>; 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) // A promise that will resolve when the "create" method is called (signaling loading is ended)
private createPromise: Promise<void>; private createPromise: Promise<void>;
private createPromiseResolve!: (value?: void | PromiseLike<void>) => void; private createPromiseResolve!: (value?: void | PromiseLike<void>) => void;
@ -187,6 +187,9 @@ export class GameScene extends DirtyScene implements CenterListener {
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>(); private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
private originalMapUrl: string|undefined; private originalMapUrl: string|undefined;
private pinchManager: PinchManager|undefined; private pinchManager: PinchManager|undefined;
private physicsEnabled: boolean = true;
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) { constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) {
super({ super({
@ -202,10 +205,11 @@ export class GameScene extends DirtyScene implements CenterListener {
this.createPromise = new Promise<void>((resolve, reject): void => { this.createPromise = new Promise<void>((resolve, reject): void => {
this.createPromiseResolve = resolve; this.createPromiseResolve = resolve;
}) });
this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => { this.connectionAnswerPromise = new Promise<RoomJoinedMessageInterface>((resolve, reject): void => {
this.connectionAnswerPromiseResolve = resolve; this.connectionAnswerPromiseResolve = resolve;
}); });
this.onVisibilityChangeCallback = this.onVisibilityChange.bind(this);
} }
//hook preload scene //hook preload scene
@ -495,6 +499,8 @@ export class GameScene extends DirtyScene implements CenterListener {
if (!this.room.isDisconnected()) { if (!this.room.isDisconnected()) {
this.connect(); this.connect();
} }
document.addEventListener('visibilitychange', this.onVisibilityChangeCallback);
} }
/** /**
@ -616,6 +622,7 @@ export class GameScene extends DirtyScene implements CenterListener {
self.chatModeSprite.setVisible(false); self.chatModeSprite.setVisible(false);
self.openChatIcon.setVisible(false); self.openChatIcon.setVisible(false);
audioManager.restoreVolume(); audioManager.restoreVolume();
self.onVisibilityChange();
} }
} }
}) })
@ -916,6 +923,8 @@ ${escapedMessage}
} }
private onMapExit(exitKey: string) { private onMapExit(exitKey: string) {
if (this.mapTransitioning) return;
this.mapTransitioning = true;
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); 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); if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);
urlManager.pushStartLayerNameToUrl(hash); urlManager.pushStartLayerNameToUrl(hash);
@ -933,6 +942,7 @@ ${escapedMessage}
this.initPositionFromLayerName(hash || defaultStartLayerName); this.initPositionFromLayerName(hash || defaultStartLayerName);
this.CurrentPlayer.x = this.startX; this.CurrentPlayer.x = this.startX;
this.CurrentPlayer.y = this.startY; this.CurrentPlayer.y = this.startY;
setTimeout(() => this.mapTransitioning = false, 500);
} }
} }
@ -958,6 +968,8 @@ ${escapedMessage}
for(const iframeEvents of this.iframeSubscriptionList){ for(const iframeEvents of this.iframeSubscriptionList){
iframeEvents.unsubscribe(); iframeEvents.unsubscribe();
} }
document.removeEventListener('visibilitychange', this.onVisibilityChangeCallback);
} }
private removeAllRemotePlayers(): void { private removeAllRemotePlayers(): void {
@ -1107,6 +1119,7 @@ ${escapedMessage}
createCollisionWithPlayer() { createCollisionWithPlayer() {
this.physics.disableUpdate(); this.physics.disableUpdate();
this.physicsEnabled = false;
//add collision layer //add collision layer
for (const Layer of this.gameMap.flatLayers) { for (const Layer of this.gameMap.flatLayers) {
if (Layer.type == "tilelayer") { if (Layer.type == "tilelayer") {
@ -1251,12 +1264,15 @@ ${escapedMessage}
this.CurrentPlayer.moveUser(delta); this.CurrentPlayer.moveUser(delta);
if (this.CurrentPlayer.isMoving()) { if (this.CurrentPlayer.isMoving()) {
this.dirty = true; this.dirty = true;
this.physics.enableUpdate(); if (!this.physicsEnabled) {
} else { this.physics.enableUpdate();
this.physicsEnabled = true;
}
} else if (this.physicsEnabled) {
this.physics.disableUpdate(); this.physics.disableUpdate();
this.physicsEnabled = false;
} }
// Let's handle all events // Let's handle all events
while (this.pendingEvents.length !== 0) { while (this.pendingEvents.length !== 0) {
this.dirty = true; this.dirty = true;
@ -1523,6 +1539,8 @@ ${escapedMessage}
mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => { mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => {
this.stopJitsi(); this.stopJitsi();
}); });
this.onVisibilityChange();
} }
public stopJitsi(): void { public stopJitsi(): void {
@ -1531,6 +1549,7 @@ ${escapedMessage}
mediaManager.showGameOverlay(); mediaManager.showGameOverlay();
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi'); mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
this.onVisibilityChange();
} }
//todo: put this into an 'orchestrator' scene (EntryScene?) //todo: put this into an 'orchestrator' scene (EntryScene?)
@ -1570,4 +1589,20 @@ ${escapedMessage}
waScaleManager.zoomModifier *= zoomFactor; waScaleManager.zoomModifier *= zoomFactor;
this.updateCameraOffset(); 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 {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable";
import {PositionInterface} from "../../Connexion/ConnexionModels"; import type {PositionInterface} from "../../Connexion/ConnexionModels";
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,8 +2,8 @@
* 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 type {PlayerMovement} from "./PlayerMovement";
import {HasMovedEvent} from "./GameManager"; import type {HasMovedEvent} from "./GameManager";
export class PlayersPositionInterpolator { export class PlayersPositionInterpolator {
playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>(); playerMovements: Map<number, PlayerMovement> = new Map<number, PlayerMovement>();

View File

@ -4,7 +4,7 @@
*/ */
import Sprite = Phaser.GameObjects.Sprite; import Sprite = Phaser.GameObjects.Sprite;
import {OutlinePipeline} from "../Shaders/OutlinePipeline"; import {OutlinePipeline} from "../Shaders/OutlinePipeline";
import {GameScene} from "../Game/GameScene"; import type {GameScene} from "../Game/GameScene";
type EventCallback = (state: unknown, parameters: unknown) => void; type EventCallback = (state: unknown, parameters: unknown) => void;

View File

@ -1,9 +1,9 @@
import * as Phaser from 'phaser'; import * as Phaser from 'phaser';
import {Scene} from "phaser"; import {Scene} from "phaser";
import Sprite = Phaser.GameObjects.Sprite; import Sprite = Phaser.GameObjects.Sprite;
import {ITiledMapObject} from "../../Map/ITiledMap"; import type {ITiledMapObject} from "../../Map/ITiledMap";
import {ItemFactoryInterface} from "../ItemFactoryInterface"; import type {ItemFactoryInterface} from "../ItemFactoryInterface";
import {GameScene} from "../../Game/GameScene"; import type {GameScene} from "../../Game/GameScene";
import {ActionableItem} from "../ActionableItem"; import {ActionableItem} from "../ActionableItem";
import * as tg from "generic-type-guard"; import * as tg from "generic-type-guard";

View File

@ -1,7 +1,7 @@
import type {GameScene} from "../Game/GameScene";
import type {ITiledMapObject} from "../Map/ITiledMap";
import type {ActionableItem} from "./ActionableItem";
import LoaderPlugin = Phaser.Loader.LoaderPlugin; import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import {GameScene} from "../Game/GameScene";
import {ITiledMapObject} from "../Map/ITiledMap";
import {ActionableItem} from "./ActionableItem";
export interface ItemFactoryInterface { export interface ItemFactoryInterface {
preload: (loader: LoaderPlugin) => void; preload: (loader: LoaderPlugin) => void;

View File

@ -1,8 +1,8 @@
import {ResizableScene} from "./ResizableScene"; import {ResizableScene} from "./ResizableScene";
import {localUserStore} from "../../Connexion/LocalUserStore"; import {localUserStore} from "../../Connexion/LocalUserStore";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager"; import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
import {CharacterTexture} from "../../Connexion/LocalUser"; import type {CharacterTexture} from "../../Connexion/LocalUser";
export abstract class AbstractCharacterScene extends ResizableScene { export abstract class AbstractCharacterScene extends ResizableScene {
@ -38,4 +38,4 @@ export abstract class AbstractCharacterScene extends ResizableScene {
const localUser = localUserStore.getLocalUser(); const localUser = localUserStore.getLocalUser();
return localUser?.textures; return localUser?.textures;
} }
} }

View File

@ -6,7 +6,7 @@ import Container = Phaser.GameObjects.Container;
import {gameManager} from "../Game/GameManager"; import {gameManager} from "../Game/GameManager";
import {localUserStore} from "../../Connexion/LocalUserStore"; import {localUserStore} from "../../Connexion/LocalUserStore";
import {addLoader} from "../Components/Loader"; import {addLoader} from "../Components/Loader";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser"; import {areCharacterLayersValid} from "../../Connexion/LocalUser";
import { MenuScene } from "../Menu/MenuScene"; import { MenuScene } from "../Menu/MenuScene";

View File

@ -252,7 +252,6 @@ export class EnableCameraScene extends ResizableScene {
update(time: number, delta: number): void { update(time: number, delta: number): void {
this.soundMeterSprite.setVolume(this.soundMeter.getVolume()); this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
mediaManager.updateScene();
this.centerXDomElement(this.enableCameraSceneElement, 300); this.centerXDomElement(this.enableCameraSceneElement, 300);
} }

View File

@ -1,18 +1,4 @@
import {gameManager} from "../Game/GameManager"; import { SelectCharacterScene } from "./SelectCharacterScene";
import {TextField} from "../Components/TextField";
import Image = Phaser.GameObjects.Image;
import Rectangle = Phaser.GameObjects.Rectangle;
import {EnableCameraSceneName} from "./EnableCameraScene";
import {CustomizeSceneName} from "./CustomizeScene";
import {localUserStore} from "../../Connexion/LocalUserStore";
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {PinchManager} from "../UserInput/PinchManager";
import {MenuScene} from "../Menu/MenuScene";
import { SelectCharacterScene, SelectCharacterSceneName } from "./SelectCharacterScene";
export class SelectCharacterMobileScene extends SelectCharacterScene { export class SelectCharacterMobileScene extends SelectCharacterScene {
@ -20,7 +6,7 @@ export class SelectCharacterMobileScene extends SelectCharacterScene {
super.create(); super.create();
this.selectedRectangle.destroy(); this.selectedRectangle.destroy();
} }
protected defineSetupPlayer(numero: number){ protected defineSetupPlayer(numero: number){
const deltaX = 30; const deltaX = 30;
const deltaY = 2; const deltaY = 2;

View File

@ -1,18 +1,16 @@
import {gameManager} from "../Game/GameManager"; import {gameManager} from "../Game/GameManager";
import Image = Phaser.GameObjects.Image;
import Rectangle = Phaser.GameObjects.Rectangle; import Rectangle = Phaser.GameObjects.Rectangle;
import {EnableCameraSceneName} from "./EnableCameraScene"; import {EnableCameraSceneName} from "./EnableCameraScene";
import {CustomizeSceneName} from "./CustomizeScene"; import {CustomizeSceneName} from "./CustomizeScene";
import {localUserStore} from "../../Connexion/LocalUserStore"; import {localUserStore} from "../../Connexion/LocalUserStore";
import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager"; import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager";
import {addLoader} from "../Components/Loader"; import {addLoader} from "../Components/Loader";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import type {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser"; import {areCharacterLayersValid} from "../../Connexion/LocalUser";
import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {PinchManager} from "../UserInput/PinchManager"; import {PinchManager} from "../UserInput/PinchManager";
import {MenuScene} from "../Menu/MenuScene"; import {MenuScene} from "../Menu/MenuScene";
import { SelectCharacterMobileScene } from "./SelectCharacterMobileScene";
//todo: put this constants in a dedicated file //todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene"; export const SelectCharacterSceneName = "SelectCharacterScene";

View File

@ -5,7 +5,7 @@ import { gameManager} from "../Game/GameManager";
import { ResizableScene } from "./ResizableScene"; import { ResizableScene } from "./ResizableScene";
import { EnableCameraSceneName } from "./EnableCameraScene"; import { EnableCameraSceneName } from "./EnableCameraScene";
import { localUserStore } from "../../Connexion/LocalUserStore"; import { localUserStore } from "../../Connexion/LocalUserStore";
import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; import type { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures";
import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager"; import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager";
import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {PinchManager} from "../UserInput/PinchManager"; import {PinchManager} from "../UserInput/PinchManager";

View File

@ -1,10 +1,10 @@
import {ITiledMap, ITiledMapLayer} from "./ITiledMap"; import type {ITiledMap, ITiledMapLayer} from "./ITiledMap";
/** /**
* Flatten the grouped layers * Flatten the grouped layers
*/ */
export function flattenGroupLayersMap(map: ITiledMap) { export function flattenGroupLayersMap(map: ITiledMap) {
let flatLayers: ITiledMapLayer[] = []; const flatLayers: ITiledMapLayer[] = [];
flattenGroupLayers(map.layers, '', flatLayers); flattenGroupLayers(map.layers, '', flatLayers);
return flatLayers; return flatLayers;
} }

View File

@ -109,15 +109,18 @@ export class HelpCameraSettingsScene extends DirtyScene {
public onResize(ev: UIEvent): void { public onResize(ev: UIEvent): void {
super.onResize(ev); super.onResize(ev);
const middleX = this.getMiddleX(); if (this.helpCameraSettingsOpened) {
const middleY = this.getMiddleY(); const middleX = this.getMiddleX();
this.tweens.add({ const middleY = this.getMiddleY();
targets: this.helpCameraSettingsElement, this.tweens.add({
x: middleX, targets: this.helpCameraSettingsElement,
y: middleY, x: middleX,
duration: 1000, y: middleY,
ease: 'Power3' duration: 1000,
}); ease: 'Power3'
});
this.dirty = true;
}
} }
private getMiddleX() : number{ private getMiddleX() : number{

View File

@ -9,6 +9,7 @@ import {connectionManager} from "../../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../../Url/UrlManager"; import {GameConnexionTypes} from "../../Url/UrlManager";
import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer"; import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer";
import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream"; import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream";
import {menuIconVisible} from "../../Stores/MenuStore";
export const MenuSceneName = 'MenuScene'; export const MenuSceneName = 'MenuScene';
const gameMenuKey = 'gameMenu'; const gameMenuKey = 'gameMenu';
@ -53,6 +54,7 @@ export class MenuScene extends Phaser.Scene {
} }
create() { create() {
menuIconVisible.set(true);
this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey); this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey);
this.menuElement.setOrigin(0); this.menuElement.setOrigin(0);
MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu'); MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu');

View File

@ -1,5 +1,5 @@
import {PlayerAnimationDirections} from "./Animation"; import {PlayerAnimationDirections} from "./Animation";
import {GameScene} from "../Game/GameScene"; import type {GameScene} from "../Game/GameScene";
import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager";
import {Character} from "../Entity/Character"; import {Character} from "../Entity/Character";

View File

@ -1,4 +1,3 @@
import ScaleManager = Phaser.Scale.ScaleManager;
interface Size { interface Size {
width: number; width: number;
@ -13,8 +12,7 @@ export class HdpiManager {
* @param minRecommendedGamePixelsNumber The minimum number of pixels we want to display "by default" to the user * @param minRecommendedGamePixelsNumber The minimum number of pixels we want to display "by default" to the user
* @param absoluteMinPixelNumber The very minimum of game pixels to display. Below, we forbid zooming more * @param absoluteMinPixelNumber The very minimum of game pixels to display. Below, we forbid zooming more
*/ */
public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) { public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) {}
}
/** /**
* Returns the optimal size in "game pixels" based on the screen size in "real pixels". * Returns the optimal size in "game pixels" based on the screen size in "real pixels".
@ -36,16 +34,12 @@ export class HdpiManager {
}; };
} }
let i = 1; const optimalZoomLevel = this.getOptimalZoomLevel(realPixelNumber);
while (realPixelNumber > this.minRecommendedGamePixelsNumber * i * i) {
i++;
}
// Has the canvas more pixels than the screen? This is forbidden // Has the canvas more pixels than the screen? This is forbidden
if ((i - 1) * this._zoomModifier < 1) { if (optimalZoomLevel * this._zoomModifier < 1) {
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter) // Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
this._zoomModifier = 1 / (i - 1); this._zoomModifier = 1 / optimalZoomLevel;
return { return {
game: { game: {
@ -59,8 +53,8 @@ export class HdpiManager {
} }
} }
const gameWidth = Math.ceil(realPixelScreenSize.width / (i - 1) / this._zoomModifier); const gameWidth = Math.ceil(realPixelScreenSize.width / optimalZoomLevel / this._zoomModifier);
const gameHeight = Math.ceil(realPixelScreenSize.height / (i - 1) / this._zoomModifier); const gameHeight = Math.ceil(realPixelScreenSize.height / optimalZoomLevel / this._zoomModifier);
// Let's ensure we display a minimum of pixels, even if crazily zoomed in. // Let's ensure we display a minimum of pixels, even if crazily zoomed in.
if (gameWidth * gameHeight < this.absoluteMinPixelNumber) { if (gameWidth * gameHeight < this.absoluteMinPixelNumber) {
@ -68,7 +62,7 @@ export class HdpiManager {
const minGameWidth = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.width / realPixelScreenSize.height); const minGameWidth = Math.sqrt(this.absoluteMinPixelNumber * realPixelScreenSize.width / realPixelScreenSize.height);
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter) // Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
this._zoomModifier = realPixelScreenSize.width / minGameWidth / (i - 1); this._zoomModifier = realPixelScreenSize.width / minGameWidth / optimalZoomLevel;
return { return {
game: { game: {
@ -89,12 +83,24 @@ export class HdpiManager {
height: gameHeight, height: gameHeight,
}, },
real: { real: {
width: Math.ceil(realPixelScreenSize.width / (i - 1)) * (i - 1), width: Math.ceil(realPixelScreenSize.width / optimalZoomLevel) * optimalZoomLevel,
height: Math.ceil(realPixelScreenSize.height / (i - 1)) * (i - 1), height: Math.ceil(realPixelScreenSize.height / optimalZoomLevel) * optimalZoomLevel,
} }
} }
} }
/**
* We only accept integer but we make an exception for 1.5
*/
private getOptimalZoomLevel(realPixelNumber: number): number {
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
if (1.5 <= result && result < 2) {
return 1.5
} else {
return Math.floor(result);
}
}
public get zoomModifier(): number { public get zoomModifier(): number {
return this._zoomModifier; return this._zoomModifier;
} }

View File

@ -1,18 +1,21 @@
import {HdpiManager} from "./HdpiManager"; import {HdpiManager} from "./HdpiManager";
import ScaleManager = Phaser.Scale.ScaleManager; import ScaleManager = Phaser.Scale.ScaleManager;
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import type {Game} from "../Game/Game";
class WaScaleManager { class WaScaleManager {
private hdpiManager: HdpiManager; private hdpiManager: HdpiManager;
private scaleManager!: ScaleManager; private scaleManager!: ScaleManager;
private game!: Game;
public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) { public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber); this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber);
} }
public setScaleManager(scaleManager: ScaleManager) { public setGame(game: Game): void {
this.scaleManager = scaleManager; this.scaleManager = game.scale;
this.game = game;
} }
public applyNewSize() { public applyNewSize() {
@ -32,6 +35,8 @@ class WaScaleManager {
const style = this.scaleManager.canvas.style; const style = this.scaleManager.canvas.style;
style.width = Math.ceil(realSize.width / devicePixelRatio) + 'px'; style.width = Math.ceil(realSize.width / devicePixelRatio) + 'px';
style.height = Math.ceil(realSize.height / devicePixelRatio) + 'px'; style.height = Math.ceil(realSize.height / devicePixelRatio) + 'px';
this.game.markDirty();
} }
public get zoomModifier(): number { public get zoomModifier(): number {
@ -42,6 +47,7 @@ class WaScaleManager {
this.hdpiManager.zoomModifier = zoomModifier; this.hdpiManager.zoomModifier = zoomModifier;
this.applyNewSize(); this.applyNewSize();
} }
} }
export const waScaleManager = new WaScaleManager(640*480, 196*196); export const waScaleManager = new WaScaleManager(640*480, 196*196);

View File

@ -1,5 +1,5 @@
import { Direction } from "../../types"; import type { Direction } from "../../types";
import {GameScene} from "../Game/GameScene"; import type {GameScene} from "../Game/GameScene";
import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {MobileJoystick} from "../Components/MobileJoystick"; import {MobileJoystick} from "../Components/MobileJoystick";
@ -173,7 +173,7 @@ export class UserInputManager {
} }
destroy(): void { destroy(): void {
this.joystick.destroy(); this.joystick?.destroy();
} }
private initMouseWheel() { private initMouseWheel() {

View File

@ -0,0 +1,3 @@
import { derived, writable, Writable } from "svelte/store";
export const menuIconVisible = writable(false);

View File

@ -1,4 +1,4 @@
import {Room} from "../Connexion/Room"; import type {Room} from "../Connexion/Room";
export enum GameConnexionTypes { export enum GameConnexionTypes {
anonymous=1, anonymous=1,

View File

@ -32,7 +32,7 @@ class CoWebsiteManager {
private resizing: boolean = false; private resizing: boolean = false;
private cowebsiteMainDom: HTMLDivElement; private cowebsiteMainDom: HTMLDivElement;
private cowebsiteAsideDom: HTMLDivElement; private cowebsiteAsideDom: HTMLDivElement;
get width(): number { get width(): number {
return this.cowebsiteDiv.clientWidth; return this.cowebsiteDiv.clientWidth;
} }
@ -137,14 +137,14 @@ class CoWebsiteManager {
if (allowPolicy) { if (allowPolicy) {
iframe.allow = allowPolicy; iframe.allow = allowPolicy;
} }
const onloadPromise = new Promise((resolve) => { const onloadPromise = new Promise<void>((resolve) => {
iframe.onload = () => resolve(); iframe.onload = () => resolve();
}); });
if (allowApi) { if (allowApi) {
iframeListener.registerIframe(iframe); iframeListener.registerIframe(iframe);
} }
this.cowebsiteMainDom.appendChild(iframe); this.cowebsiteMainDom.appendChild(iframe);
const onTimeoutPromise = new Promise((resolve) => { const onTimeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => resolve(), 2000); setTimeout(() => resolve(), 2000);
}); });
this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => { this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => {

View File

@ -1,6 +1,6 @@
import {HtmlUtils} from "./HtmlUtils"; import {HtmlUtils} from "./HtmlUtils";
import {mediaManager, ReportCallback, ShowReportCallBack} from "./MediaManager"; import type {ShowReportCallBack} from "./MediaManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {connectionManager} from "../Connexion/ConnectionManager"; import {connectionManager} from "../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../Url/UrlManager"; import {GameConnexionTypes} from "../Url/UrlManager";
import {iframeListener} from "../Api/IframeListener"; import {iframeListener} from "../Api/IframeListener";

View File

@ -10,9 +10,10 @@ interface jitsiConfigInterface {
} }
const getDefaultConfig = () : jitsiConfigInterface => { const getDefaultConfig = () : jitsiConfigInterface => {
const constraints = mediaManager.getConstraintRequestedByUser();
return { return {
startWithAudioMuted: !mediaManager.constraintsMedia.audio, startWithAudioMuted: !constraints.audio,
startWithVideoMuted: mediaManager.constraintsMedia.video === false, startWithVideoMuted: constraints.video === false,
prejoinPageEnabled: false prejoinPageEnabled: false
} }
} }
@ -71,7 +72,7 @@ class JitsiFactory {
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
private audioCallback = this.onAudioChange.bind(this); private audioCallback = this.onAudioChange.bind(this);
private videoCallback = this.onVideoChange.bind(this); private videoCallback = this.onVideoChange.bind(this);
private previousConfigMeet? : jitsiConfigInterface; private previousConfigMeet! : jitsiConfigInterface;
private jitsiScriptLoaded: boolean = false; private jitsiScriptLoaded: boolean = false;
/** /**
@ -136,32 +137,24 @@ class JitsiFactory {
//restore previous config //restore previous config
if(this.previousConfigMeet?.startWithAudioMuted){ if(this.previousConfigMeet?.startWithAudioMuted){
mediaManager.disableMicrophone(); await mediaManager.disableMicrophone();
}else{ }else{
mediaManager.enableMicrophone(); await mediaManager.enableMicrophone();
} }
if(this.previousConfigMeet?.startWithVideoMuted){ if(this.previousConfigMeet?.startWithVideoMuted){
mediaManager.disableCamera(); await mediaManager.disableCamera();
}else{ }else{
mediaManager.enableCamera(); await mediaManager.enableCamera();
} }
} }
private onAudioChange({muted}: {muted: boolean}): void { private onAudioChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.audio === true) { this.previousConfigMeet.startWithAudioMuted = muted;
mediaManager.disableMicrophone();
} else if(!muted && mediaManager.constraintsMedia.audio === false) {
mediaManager.enableMicrophone();
}
} }
private onVideoChange({muted}: {muted: boolean}): void { private onVideoChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.video !== false) { this.previousConfigMeet.startWithVideoMuted = muted;
mediaManager.disableCamera();
} else if(!muted && mediaManager.constraintsMedia.video === false) {
mediaManager.enableCamera();
}
} }
private async loadJitsiScript(domain: string): Promise<void> { private async loadJitsiScript(domain: string): Promise<void> {

View File

@ -1,4 +1,4 @@
import { UserInputManager } from "../Phaser/UserInput/UserInputManager"; import type { UserInputManager } from "../Phaser/UserInput/UserInputManager";
import {HtmlUtils} from "./HtmlUtils"; import {HtmlUtils} from "./HtmlUtils";
export enum LayoutMode { export enum LayoutMode {
@ -324,7 +324,7 @@ class LayoutManager {
public addActionButton(id: string, text: string, callBack: Function, userInputManager: UserInputManager){ public addActionButton(id: string, text: string, callBack: Function, userInputManager: UserInputManager){
//delete previous element //delete previous element
this.removeActionButton(id, userInputManager); this.removeActionButton(id, userInputManager);
//create div and text html component //create div and text html component
const p = document.createElement('p'); const p = document.createElement('p');
p.classList.add('action-body'); p.classList.add('action-body');

View File

@ -1,10 +1,11 @@
import {DivImportance, layoutManager} from "./LayoutManager"; import {DivImportance, layoutManager} from "./LayoutManager";
import {HtmlUtils} from "./HtmlUtils"; import {HtmlUtils} from "./HtmlUtils";
import {discussionManager, SendMessageCallback} from "./DiscussionManager"; import {discussionManager, SendMessageCallback} from "./DiscussionManager";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager"; import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {localUserStore} from "../Connexion/LocalUserStore"; import {localUserStore} from "../Connexion/LocalUserStore";
import {UserSimplePeerInterface} from "./SimplePeer"; import type {UserSimplePeerInterface} from "./SimplePeer";
import {SoundMeter} from "../Phaser/Components/SoundMeter"; import {SoundMeter} from "../Phaser/Components/SoundMeter";
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
@ -20,7 +21,7 @@ const audioConstraint: boolean|MediaTrackConstraints = {
//TODO: make these values configurable in the game settings menu and store them in localstorage //TODO: make these values configurable in the game settings menu and store them in localstorage
autoGainControl: false, autoGainControl: false,
echoCancellation: true, echoCancellation: true,
noiseSuppression: false noiseSuppression: true
}; };
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void; export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
@ -43,7 +44,8 @@ export class MediaManager {
microphoneClose: HTMLImageElement; microphoneClose: HTMLImageElement;
microphone: HTMLImageElement; microphone: HTMLImageElement;
webrtcInAudio: HTMLAudioElement; webrtcInAudio: HTMLAudioElement;
mySoundMeterElement: HTMLDivElement; //FIX ME SOUNDMETER: check stalability of sound meter calculation
//mySoundMeterElement: HTMLDivElement;
private webrtcOutAudio: HTMLAudioElement; private webrtcOutAudio: HTMLAudioElement;
constraintsMedia : MediaStreamConstraints = { constraintsMedia : MediaStreamConstraints = {
audio: audioConstraint, audio: audioConstraint,
@ -54,7 +56,7 @@ export class MediaManager {
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>(); stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>(); showReportModalCallBacks : Set<ShowReportCallBack> = new Set<ShowReportCallBack>();
helpCameraSettingsCallBacks : Set<HelpCameraSettingsCallBack> = new Set<HelpCameraSettingsCallBack>(); helpCameraSettingsCallBacks : Set<HelpCameraSettingsCallBack> = new Set<HelpCameraSettingsCallBack>();
private microphoneBtn: HTMLDivElement; private microphoneBtn: HTMLDivElement;
private cinemaBtn: HTMLDivElement; private cinemaBtn: HTMLDivElement;
private monitorBtn: HTMLDivElement; private monitorBtn: HTMLDivElement;
@ -62,18 +64,16 @@ export class MediaManager {
private previousConstraint : MediaStreamConstraints; private previousConstraint : MediaStreamConstraints;
private focused : boolean = true; private focused : boolean = true;
private lastUpdateScene : Date = new Date();
private setTimeOutlastUpdateScene? : NodeJS.Timeout;
private hasCamera = true; private hasCamera = true;
private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>(); private triggerCloseJistiFrame : Map<String, Function> = new Map<String, Function>();
private userInputManager?: UserInputManager; private userInputManager?: UserInputManager;
private mySoundMeter?: SoundMeter|null; //FIX ME SOUNDMETER: check stalability of sound meter calculation
/*private mySoundMeter?: SoundMeter|null;
private soundMeters: Map<string, SoundMeter> = new Map<string, SoundMeter>(); private soundMeters: Map<string, SoundMeter> = new Map<string, SoundMeter>();
private soundMeterElements: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>(); private soundMeterElements: Map<string, HTMLDivElement> = new Map<string, HTMLDivElement>();*/
constructor() { constructor() {
@ -132,17 +132,19 @@ export class MediaManager {
this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia)); this.previousConstraint = JSON.parse(JSON.stringify(this.constraintsMedia));
this.pingCameraStatus(); this.pingCameraStatus();
this.checkActiveUser(); //todo: desactivated in case of bug //FIX ME SOUNDMETER: check stalability of sound meter calculation
/*this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
this.mySoundMeterElement = (HtmlUtils.getElementByIdOrFail('mySoundMeter'));
this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => { this.mySoundMeterElement.childNodes.forEach((value: ChildNode, index) => {
this.mySoundMeterElement.children.item(index)?.classList.remove('active'); this.mySoundMeterElement.children.item(index)?.classList.remove('active');
}); });*/
//Check of ask notification navigator permission
this.getNotification();
} }
public updateScene(){ public updateScene(){
this.lastUpdateScene = new Date(); //FIX ME SOUNDMETER: check stalability of sound meter calculation
this.updateSoudMeter(); //this.updateSoudMeter();
} }
public blurCamera() { public blurCamera() {
@ -154,6 +156,13 @@ export class MediaManager {
this.disableCamera(); this.disableCamera();
} }
/**
* Returns the constraint that the user wants (independently of the visibility / jitsi state...)
*/
public getConstraintRequestedByUser(): MediaStreamConstraints {
return this.previousConstraint ?? this.constraintsMedia;
}
public focusCamera() { public focusCamera() {
if(this.focused){ if(this.focused){
return; return;
@ -196,7 +205,7 @@ export class MediaManager {
} }
} }
public showGameOverlay(){ public showGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.add('active'); gameOverlay.classList.add('active');
@ -207,7 +216,7 @@ export class MediaManager {
buttonCloseFrame.removeEventListener('click', functionTrigger); buttonCloseFrame.removeEventListener('click', functionTrigger);
} }
public hideGameOverlay(){ public hideGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay'); const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.remove('active'); gameOverlay.classList.remove('active');
@ -218,6 +227,11 @@ export class MediaManager {
buttonCloseFrame.addEventListener('click', functionTrigger); buttonCloseFrame.addEventListener('click', functionTrigger);
} }
public isGameOverlayVisible(): boolean {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
return gameOverlay.classList.contains('active');
}
public updateCameraQuality(value: number) { public updateCameraQuality(value: number) {
this.enableCameraStyle(); this.enableCameraStyle();
const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint)); const newVideoConstraint = JSON.parse(JSON.stringify(videoConstraint));
@ -229,29 +243,32 @@ export class MediaManager {
}); });
} }
public enableCamera() { public async enableCamera() {
this.constraintsMedia.video = videoConstraint; this.constraintsMedia.video = videoConstraint;
this.getCamera().then((stream: MediaStream) => { try {
const stream = await this.getCamera()
//TODO show error message tooltip upper of camera button //TODO show error message tooltip upper of camera button
//TODO message : please check camera permission of your navigator //TODO message : please check camera permission of your navigator
if(stream.getVideoTracks().length === 0) { if(stream.getVideoTracks().length === 0) {
throw Error('Video track is empty, please check camera permission of your navigator') throw new Error('Video track is empty, please check camera permission of your navigator')
} }
this.enableCameraStyle(); this.enableCameraStyle();
this.triggerUpdatedLocalStreamCallbacks(stream); this.triggerUpdatedLocalStreamCallbacks(stream);
}).catch((err) => { } catch(err) {
console.error(err); console.error(err);
this.disableCameraStyle(); this.disableCameraStyle();
this.stopCamera();
layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => { layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
this.showHelpCameraSettingsCallBack(); this.showHelpCameraSettingsCallBack();
}, this.userInputManager); }, this.userInputManager);
}); }
} }
public async disableCamera() { public async disableCamera() {
this.disableCameraStyle(); this.disableCameraStyle();
this.stopCamera();
if (this.constraintsMedia.audio !== false) { if (this.constraintsMedia.audio !== false) {
const stream = await this.getCamera(); const stream = await this.getCamera();
@ -261,25 +278,27 @@ export class MediaManager {
} }
} }
public enableMicrophone() { public async enableMicrophone() {
this.constraintsMedia.audio = audioConstraint; this.constraintsMedia.audio = audioConstraint;
this.getCamera().then((stream) => { try {
const stream = await this.getCamera();
//TODO show error message tooltip upper of camera button //TODO show error message tooltip upper of camera button
//TODO message : please check microphone permission of your navigator //TODO message : please check microphone permission of your navigator
if(stream.getAudioTracks().length === 0) { if (stream.getAudioTracks().length === 0) {
throw Error('Audio track is empty, please check microphone permission of your navigator') throw Error('Audio track is empty, please check microphone permission of your navigator')
} }
this.enableMicrophoneStyle(); this.enableMicrophoneStyle();
this.triggerUpdatedLocalStreamCallbacks(stream); this.triggerUpdatedLocalStreamCallbacks(stream);
}).catch((err) => { } catch(err) {
console.error(err); console.error(err);
this.disableMicrophoneStyle(); this.disableMicrophoneStyle();
layoutManager.addInformation('warning', 'Microphone access denied. Click here and check navigators permissions.', () => { layoutManager.addInformation('warning', 'Microphone access denied. Click here and check navigators permissions.', () => {
this.showHelpCameraSettingsCallBack(); this.showHelpCameraSettingsCallBack();
}, this.userInputManager); }, this.userInputManager);
}); }
} }
public async disableMicrophone() { public async disableMicrophone() {
@ -324,7 +343,6 @@ export class MediaManager {
this.cinemaBtn.classList.add("disabled"); this.cinemaBtn.classList.add("disabled");
this.constraintsMedia.video = false; this.constraintsMedia.video = false;
this.myCamVideo.srcObject = null; this.myCamVideo.srcObject = null;
this.stopCamera();
} }
private enableMicrophoneStyle(){ private enableMicrophoneStyle(){
@ -411,7 +429,7 @@ export class MediaManager {
} }
private _startScreenCapture() { private _startScreenCapture() {
if (navigator.getDisplayMedia) { if (navigator.getDisplayMedia) {
return navigator.getDisplayMedia({video: true}); return navigator.getDisplayMedia({video: true});
} else if (navigator.mediaDevices.getDisplayMedia) { } else if (navigator.mediaDevices.getDisplayMedia) {
return navigator.mediaDevices.getDisplayMedia({video: true}); return navigator.mediaDevices.getDisplayMedia({video: true});
@ -435,6 +453,8 @@ export class MediaManager {
return this.getLocalStream().catch((err) => { return this.getLocalStream().catch((err) => {
console.info('Error get camera, trying with video option at null =>', err); console.info('Error get camera, trying with video option at null =>', err);
this.disableCameraStyle(); this.disableCameraStyle();
this.stopCamera();
return this.getLocalStream().then((stream : MediaStream) => { return this.getLocalStream().then((stream : MediaStream) => {
this.hasCamera = false; this.hasCamera = false;
return stream; return stream;
@ -457,12 +477,12 @@ export class MediaManager {
this.localStream = stream; this.localStream = stream;
this.myCamVideo.srcObject = this.localStream; this.myCamVideo.srcObject = this.localStream;
//init sound meter //FIX ME SOUNDMETER: check stalability of sound meter calculation
this.mySoundMeter = null; /*this.mySoundMeter = null;
if(this.constraintsMedia.audio){ if(this.constraintsMedia.audio){
this.mySoundMeter = new SoundMeter(); this.mySoundMeter = new SoundMeter();
this.mySoundMeter.connectToSource(stream, new AudioContext()); this.mySoundMeter.connectToSource(stream, new AudioContext());
} }*/
return stream; return stream;
}).catch((err: Error) => { }).catch((err: Error) => {
throw err; throw err;
@ -489,7 +509,7 @@ export class MediaManager {
track.stop(); track.stop();
} }
} }
this.mySoundMeter?.stop(); //this.mySoundMeter?.stop();
} }
setCamera(id: string): Promise<MediaStream> { setCamera(id: string): Promise<MediaStream> {
@ -546,7 +566,7 @@ export class MediaManager {
`; `;
layoutManager.add(DivImportance.Normal, userId, html); layoutManager.add(DivImportance.Normal, userId, html);
this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail<HTMLVideoElement>(userId)); this.remoteVideo.set(userId, HtmlUtils.getElementByIdOrFail<HTMLVideoElement>(userId));
//permit to create participant in discussion part //permit to create participant in discussion part
@ -564,7 +584,7 @@ export class MediaManager {
showReportUser(); showReportUser();
}); });
} }
addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){
userId = this.getScreenSharingId(userId); userId = this.getScreenSharingId(userId);
@ -590,7 +610,7 @@ export class MediaManager {
} }
element.classList.add('active') //todo: why does a method 'disable' add a class 'active'? element.classList.add('active') //todo: why does a method 'disable' add a class 'active'?
} }
enabledMicrophoneByUserId(userId: number){ enabledMicrophoneByUserId(userId: number){
const element = document.getElementById(`microphone-${userId}`); const element = document.getElementById(`microphone-${userId}`);
if(!element){ if(!element){
@ -598,7 +618,7 @@ export class MediaManager {
} }
element.classList.remove('active') //todo: why does a method 'enable' remove a class 'active'? element.classList.remove('active') //todo: why does a method 'enable' remove a class 'active'?
} }
disabledVideoByUserId(userId: number) { disabledVideoByUserId(userId: number) {
let element = document.getElementById(`${userId}`); let element = document.getElementById(`${userId}`);
if (element) { if (element) {
@ -609,7 +629,7 @@ export class MediaManager {
element.style.display = "block"; element.style.display = "block";
} }
} }
enabledVideoByUserId(userId: number){ enabledVideoByUserId(userId: number){
let element = document.getElementById(`${userId}`); let element = document.getElementById(`${userId}`);
if(element){ if(element){
@ -632,11 +652,12 @@ export class MediaManager {
} }
remoteVideo.srcObject = stream; remoteVideo.srcObject = stream;
//FIX ME SOUNDMETER: check stalability of sound meter calculation
//sound metter //sound metter
const soundMeter = new SoundMeter(); /*const soundMeter = new SoundMeter();
soundMeter.connectToSource(stream, new AudioContext()); soundMeter.connectToSource(stream, new AudioContext());
this.soundMeters.set(userId, soundMeter); this.soundMeters.set(userId, soundMeter);
this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId)); this.soundMeterElements.set(userId, HtmlUtils.getElementByIdOrFail<HTMLImageElement>('soundMeter-'+userId));*/
} }
addStreamRemoteScreenSharing(userId: string, stream : MediaStream){ addStreamRemoteScreenSharing(userId: string, stream : MediaStream){
// In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet // In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
@ -647,14 +668,15 @@ export class MediaManager {
this.addStreamRemoteVideo(this.getScreenSharingId(userId), stream); this.addStreamRemoteVideo(this.getScreenSharingId(userId), stream);
} }
removeActiveVideo(userId: string){ removeActiveVideo(userId: string){
layoutManager.remove(userId); layoutManager.remove(userId);
this.remoteVideo.delete(userId); this.remoteVideo.delete(userId);
this.soundMeters.get(userId)?.stop(); //FIX ME SOUNDMETER: check stalability of sound meter calculation
/*this.soundMeters.get(userId)?.stop();
this.soundMeters.delete(userId); this.soundMeters.delete(userId);
this.soundMeterElements.delete(userId); this.soundMeterElements.delete(userId);*/
//permit to remove user in discussion part //permit to remove user in discussion part
this.removeParticipant(userId); this.removeParticipant(userId);
@ -662,7 +684,7 @@ export class MediaManager {
removeActiveScreenSharingVideo(userId: string) { removeActiveScreenSharingVideo(userId: string) {
this.removeActiveVideo(this.getScreenSharingId(userId)) this.removeActiveVideo(this.getScreenSharingId(userId))
} }
playWebrtcOutSound(): void { playWebrtcOutSound(): void {
this.webrtcOutAudio.play(); this.webrtcOutAudio.play();
} }
@ -708,7 +730,7 @@ export class MediaManager {
const connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement|null; const connnectingSpinnerDiv = element.getElementsByClassName('connecting-spinner').item(0) as HTMLDivElement|null;
return connnectingSpinnerDiv; return connnectingSpinnerDiv;
} }
private getColorByString(str: String) : String|null { private getColorByString(str: String) : String|null {
let hash = 0; let hash = 0;
if (str.length === 0) return null; if (str.length === 0) return null;
@ -776,22 +798,6 @@ export class MediaManager {
this.userInputManager = userInputManager; this.userInputManager = userInputManager;
discussionManager.setUserInputManager(userInputManager); discussionManager.setUserInputManager(userInputManager);
} }
//check if user is active
private checkActiveUser(){
if(this.setTimeOutlastUpdateScene){
clearTimeout(this.setTimeOutlastUpdateScene);
}
this.setTimeOutlastUpdateScene = setTimeout(() => {
const now = new Date();
//if last update is more of 10 sec
if( (now.getTime() - this.lastUpdateScene.getTime()) > 10000) {
this.blurCamera();
}else{
this.focusCamera();
}
this.checkActiveUser();
}, this.focused ? 10000 : 1000);
}
public setShowReportModalCallBacks(callback: ShowReportCallBack){ public setShowReportModalCallBacks(callback: ShowReportCallBack){
this.showReportModalCallBacks.add(callback); this.showReportModalCallBacks.add(callback);
@ -807,11 +813,12 @@ export class MediaManager {
} }
} }
updateSoudMeter(){ //FIX ME SOUNDMETER: check stalability of sound meter calculation
/*updateSoudMeter(){
try{ try{
const volume = parseInt(((this.mySoundMeter ? this.mySoundMeter.getVolume() : 0) / 10).toFixed(0)); const volume = parseInt(((this.mySoundMeter ? this.mySoundMeter.getVolume() : 0) / 10).toFixed(0));
this.setVolumeSoundMeter(volume, this.mySoundMeterElement); this.setVolumeSoundMeter(volume, this.mySoundMeterElement);
for(const indexUserId of this.soundMeters.keys()){ for(const indexUserId of this.soundMeters.keys()){
const soundMeter = this.soundMeters.get(indexUserId); const soundMeter = this.soundMeters.get(indexUserId);
const soundMeterElement = this.soundMeterElements.get(indexUserId); const soundMeterElement = this.soundMeterElements.get(indexUserId);
@ -824,7 +831,7 @@ export class MediaManager {
}catch(err){ }catch(err){
//console.error(err); //console.error(err);
} }
} }*/
private setVolumeSoundMeter(volume: number, element: HTMLDivElement){ private setVolumeSoundMeter(volume: number, element: HTMLDivElement){
if(volume <= 0 && !element.classList.contains('active')){ if(volume <= 0 && !element.classList.contains('active')){
@ -847,6 +854,32 @@ export class MediaManager {
elementChildre.classList.add('active'); elementChildre.classList.add('active');
}); });
} }
public getNotification(){
//Get notification
if (!DISABLE_NOTIFICATIONS && window.Notification && Notification.permission !== "granted") {
Notification.requestPermission().catch((err) => {
console.error(`Notification permission error`, err);
});
}
}
public createNotification(userName: string){
if(this.focused){
return;
}
if (window.Notification && Notification.permission === "granted") {
const title = 'WorkAdventure';
const options = {
body: `Hi! ${userName} wants to discuss with you, don't be afraid!`,
icon: '/resources/logos/logo-WA-min.png',
image: '/resources/logos/logo-WA-min.png',
badge: '/resources/logos/logo-WA-min.png',
};
new Notification(title, options);
//new Notification(`Hi! ${userName} wants to discuss with you, don't be afraid!`);
}
}
} }
export const mediaManager = new MediaManager(); export const mediaManager = new MediaManager();

View File

@ -1,9 +1,9 @@
import * as SimplePeerNamespace from "simple-peer"; import type * as SimplePeerNamespace from "simple-peer";
import {mediaManager} from "./MediaManager"; import {mediaManager} from "./MediaManager";
import {STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable"; import {STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable";
import {RoomConnection} from "../Connexion/RoomConnection"; import type {RoomConnection} from "../Connexion/RoomConnection";
import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer"; import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer";
import {UserSimplePeerInterface} from "./SimplePeer"; import type {UserSimplePeerInterface} from "./SimplePeer";
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');

View File

@ -1,4 +1,4 @@
import { import type {
WebRtcDisconnectMessageInterface, WebRtcDisconnectMessageInterface,
WebRtcSignalReceivedMessageInterface, WebRtcSignalReceivedMessageInterface,
} from "../Connexion/ConnexionModels"; } from "../Connexion/ConnexionModels";
@ -10,7 +10,7 @@ import {
} from "./MediaManager"; } from "./MediaManager";
import {ScreenSharingPeer} from "./ScreenSharingPeer"; import {ScreenSharingPeer} from "./ScreenSharingPeer";
import {MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer"; import {MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer} from "./VideoPeer";
import {RoomConnection} from "../Connexion/RoomConnection"; import type {RoomConnection} from "../Connexion/RoomConnection";
import {connectionManager} from "../Connexion/ConnectionManager"; import {connectionManager} from "../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../Url/UrlManager"; import {GameConnexionTypes} from "../Url/UrlManager";
import {blackListManager} from "./BlackListManager"; import {blackListManager} from "./BlackListManager";
@ -158,6 +158,11 @@ export class SimplePeer {
this.sendLocalScreenSharingStreamToUser(user.userId); this.sendLocalScreenSharingStreamToUser(user.userId);
} }
}); });
//Create a notification for first user in circle discussion
if(this.PeerConnectionArray.size === 0){
mediaManager.createNotification(user.name??'');
}
this.PeerConnectionArray.set(user.userId, peer); this.PeerConnectionArray.set(user.userId, peer);
for (const peerConnectionListener of this.peerConnectionListeners) { for (const peerConnectionListener of this.peerConnectionListeners) {

View File

@ -1,10 +1,10 @@
import * as SimplePeerNamespace from "simple-peer"; import type * as SimplePeerNamespace from "simple-peer";
import {mediaManager} from "./MediaManager"; import {mediaManager} from "./MediaManager";
import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable"; import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable";
import {RoomConnection} from "../Connexion/RoomConnection"; import type {RoomConnection} from "../Connexion/RoomConnection";
import {blackListManager} from "./BlackListManager"; import {blackListManager} from "./BlackListManager";
import {Subscription} from "rxjs"; import type {Subscription} from "rxjs";
import {UserSimplePeerInterface} from "./SimplePeer"; import type {UserSimplePeerInterface} from "./SimplePeer";
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');

View File

@ -1,16 +1,16 @@
import { ChatEvent } from "./Api/Events/ChatEvent"; import type { ChatEvent } from "./Api/Events/ChatEvent";
import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent"; import { 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";
import { OpenPopupEvent } from "./Api/Events/OpenPopupEvent"; import type { OpenPopupEvent } from "./Api/Events/OpenPopupEvent";
import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent"; import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent";
import { ClosePopupEvent } from "./Api/Events/ClosePopupEvent"; import type { ClosePopupEvent } from "./Api/Events/ClosePopupEvent";
import { OpenTabEvent } from "./Api/Events/OpenTabEvent"; import type { OpenTabEvent } from "./Api/Events/OpenTabEvent";
import { GoToPageEvent } from "./Api/Events/GoToPageEvent"; import type { GoToPageEvent } from "./Api/Events/GoToPageEvent";
import { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent"; import type { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent";
import { LayerEvent } from "./Api/Events/LayerEvent"; import type { LayerEvent } from "./Api/Events/LayerEvent";
import { SetPropertyEvent } from "./Api/Events/setPropertyEvent"; import type { SetPropertyEvent } from "./Api/Events/setPropertyEvent";
interface WorkAdventureApi { interface WorkAdventureApi {
sendChatMessage(message: string, author: string): void; sendChatMessage(message: string, author: string): void;

View File

@ -1,6 +1,6 @@
import 'phaser'; import 'phaser';
import GameConfig = Phaser.Types.Core.GameConfig; import GameConfig = Phaser.Types.Core.GameConfig;
import "../dist/resources/style/index.scss"; import "../style/index.scss";
import {DEBUG_MODE, isMobile} from "./Enum/EnvironmentVariable"; import {DEBUG_MODE, isMobile} from "./Enum/EnvironmentVariable";
import {LoginScene} from "./Phaser/Login/LoginScene"; import {LoginScene} from "./Phaser/Login/LoginScene";
@ -21,6 +21,8 @@ import { SelectCharacterMobileScene } from './Phaser/Login/SelectCharacterMobile
import {HdpiManager} from "./Phaser/Services/HdpiManager"; import {HdpiManager} from "./Phaser/Services/HdpiManager";
import {waScaleManager} from "./Phaser/Services/WaScaleManager"; import {waScaleManager} from "./Phaser/Services/WaScaleManager";
import {Game} from "./Phaser/Game/Game"; import {Game} from "./Phaser/Game/Game";
import App from './Components/App.svelte';
import {HtmlUtils} from "./WebRtc/HtmlUtils";
const {width, height} = coWebsiteManager.getGameSize(); const {width, height} = coWebsiteManager.getGameSize();
@ -127,19 +129,12 @@ const config: GameConfig = {
//const game = new Phaser.Game(config); //const game = new Phaser.Game(config);
const game = new Game(config); const game = new Game(config);
waScaleManager.setScaleManager(game.scale); waScaleManager.setGame(game);
window.addEventListener('resize', function (event) { window.addEventListener('resize', function (event) {
coWebsiteManager.resetStyle(); coWebsiteManager.resetStyle();
waScaleManager.applyNewSize(); waScaleManager.applyNewSize();
// Let's trigger the onResize method of any active scene that is a ResizableScene
for (const scene of game.scene.getScenes(true)) {
if (scene instanceof ResizableScene) {
scene.onResize(event);
}
}
}); });
coWebsiteManager.onResize.subscribe(() => { coWebsiteManager.onResize.subscribe(() => {
@ -147,3 +142,10 @@ coWebsiteManager.onResize.subscribe(() => {
}); });
iframeListener.init(); iframeListener.init();
const app = new App({
target: HtmlUtils.getElementByIdOrFail('svelte-overlay'),
props: { },
})
export default app

View File

@ -1,4 +1,4 @@
import Phaser from "phaser"; import type Phaser from "phaser";
export type CursorKey = { export type CursorKey = {
isDown: boolean isDown: boolean

View File

@ -4,7 +4,7 @@
position: fixed; position: fixed;
transition: transform 0.5s; transition: transform 0.5s;
background-color: white; background-color: white;
&.loading { &.loading {
background-color: gray; background-color: gray;
} }
@ -15,7 +15,7 @@
height: 100%; height: 100%;
} }
} }
aside { aside {
background: gray; background: gray;
align-items: center; align-items: center;
@ -32,7 +32,7 @@
position: absolute; position: absolute;
background: none; background: none;
border: none; border: none;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
img { img {
height: 25px; height: 25px;

View File

Before

Width:  |  Height:  |  Size: 979 B

After

Width:  |  Height:  |  Size: 979 B

View File

Before

Width:  |  Height:  |  Size: 937 B

After

Width:  |  Height:  |  Size: 937 B

View File

@ -1,9 +1,9 @@
*{ *{
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
cursor: url('/resources/logos/cursor_normal.png'), auto; cursor: url('./images/cursor_normal.png'), auto;
} }
* a, button, select{ * a, button, select{
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
body{ body{
overflow: hidden; overflow: hidden;
@ -39,7 +39,7 @@ body .message-info.warning{
position: relative; position: relative;
transition: all 0.2s ease; transition: all 0.2s ease;
background-color: #00000099; background-color: #00000099;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.video-container i{ .video-container i{
position: absolute; position: absolute;
@ -75,7 +75,7 @@ body .message-info.warning{
.video-container button.report{ .video-container button.report{
display: block; display: block;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
background: none; background: none;
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
border: none; border: none;
@ -108,7 +108,7 @@ body .message-info.warning{
left: 5px; left: 5px;
margin: 0; margin: 0;
padding: 0; padding: 0;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
width: 25px; width: 25px;
height: 25px; height: 25px;
} }
@ -118,7 +118,7 @@ body .message-info.warning{
left: 36px; left: 36px;
color: white; color: white;
font-size: 16px; font-size: 16px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.video-container img.active { .video-container img.active {
display: block !important; display: block !important;
@ -126,7 +126,7 @@ body .message-info.warning{
.video-container video{ .video-container video{
height: 100%; height: 100%;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.video-container video:focus{ .video-container video:focus{
@ -206,7 +206,7 @@ video#myCamVideo{
} }
/*btn animation*/ /*btn animation*/
.btn-cam-action div{ .btn-cam-action div{
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
/*position: absolute;*/ /*position: absolute;*/
border: solid 0px black; border: solid 0px black;
width: 44px; width: 44px;
@ -260,7 +260,7 @@ video#myCamVideo{
top: calc(48px - 37px); top: calc(48px - 37px);
left: calc(48px - 41px); left: calc(48px - 41px);
position: relative; position: relative;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
/* Spinner */ /* Spinner */
@ -572,7 +572,7 @@ input[type=range]:focus::-ms-fill-upper {
margin: 2%; margin: 2%;
flex-basis: 96%; flex-basis: 96%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s; transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
pointer-events: auto; pointer-events: auto;
/*flex-shrink: 2;*/ /*flex-shrink: 2;*/
} }
@ -590,7 +590,7 @@ input[type=range]:focus::-ms-fill-upper {
.sidebar > div { .sidebar > div {
margin: 2%; margin: 2%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s; transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
border-radius: 15px 15px 15px 15px; border-radius: 15px 15px 15px 15px;
pointer-events: auto; pointer-events: auto;
} }
@ -600,7 +600,7 @@ input[type=range]:focus::-ms-fill-upper {
} }
.sidebar > div video { .sidebar > div video {
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
/* Let's make sure videos are vertically centered if they need to be cropped */ /* Let's make sure videos are vertically centered if they need to be cropped */
@ -625,7 +625,7 @@ input[type=range]:focus::-ms-fill-upper {
margin: 1%; margin: 1%;
max-height: 96%; max-height: 96%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s; transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.chat-mode > div:hover { .chat-mode > div:hover {
@ -715,7 +715,7 @@ input[type=range]:focus::-ms-fill-upper {
margin-top: 6px; margin-top: 6px;
width: 30px; width: 30px;
height: 30px; height: 30px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
padding: 0 5px; padding: 0 5px;
transition: all .5s ease; transition: all .5s ease;
transform: rotateY(0); transform: rotateY(0);
@ -739,7 +739,7 @@ input[type=range]:focus::-ms-fill-upper {
.main-console div.console:hover, .main-console div.console:hover,
.message-container div.clear:hover { .message-container div.clear:hover {
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
top: calc(100% + 5px); top: calc(100% + 5px);
transform: scale(1.2) translateY(3px); transform: scale(1.2) translateY(3px);
} }
@ -772,7 +772,7 @@ input[type=range]:focus::-ms-fill-upper {
transition: all .2s ease; transition: all .2s ease;
} }
.main-console .btn-action .btn:hover{ .main-console .btn-action .btn:hover{
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
background-color: #ffda01; background-color: #ffda01;
color: black; color: black;
border: 1px solid black; border: 1px solid black;
@ -787,7 +787,7 @@ input[type=range]:focus::-ms-fill-upper {
.main-console .menu span { .main-console .menu span {
margin: 20px; margin: 20px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.main-console .menu span.active { .main-console .menu span.active {
@ -821,10 +821,10 @@ input[type=range]:focus::-ms-fill-upper {
} }
.main-console section div.upload label img{ .main-console section div.upload label img{
height: 150px; height: 150px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.main-console section div.upload label img{ .main-console section div.upload label img{
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
@ -917,7 +917,7 @@ div.modal-report-user{
right: 0; right: 0;
left: auto; left: auto;
top: 0; top: 0;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
width: 15px; width: 15px;
height: 15px; height: 15px;
margin: 10px; margin: 10px;
@ -936,7 +936,7 @@ div.modal-report-user{
transition: all .2s ease; transition: all .2s ease;
} }
.modal-report-user button:hover{ .modal-report-user button:hover{
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
background-color: #ffda01; background-color: #ffda01;
color: black; color: black;
border: 1px solid black; border: 1px solid black;
@ -979,7 +979,7 @@ div.modal-report-user{
} }
.discussion .active-btn{ .discussion .active-btn{
display: none; display: none;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
height: 50px; height: 50px;
width: 50px; width: 50px;
background-color: #2d2d2dba; background-color: #2d2d2dba;
@ -1008,7 +1008,7 @@ div.modal-report-user{
right: 10px; right: 10px;
background: none; background: none;
border: none; border: none;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.discussion .close-btn img{ .discussion .close-btn img{
height: 15px; height: 15px;
@ -1033,7 +1033,7 @@ div.modal-report-user{
background-color: #ffffff69; background-color: #ffffff69;
padding: 5px; padding: 5px;
border-radius: 15px; border-radius: 15px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.discussion .participants .participant:hover{ .discussion .participants .participant:hover{
@ -1066,7 +1066,7 @@ div.modal-report-user{
} }
.discussion .participants .participant button.report-btn{ .discussion .participants .participant button.report-btn{
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
position: absolute; position: absolute;
background-color: #2d2d2dba; background-color: #2d2d2dba;
right: 34px; right: 34px;
@ -1176,7 +1176,7 @@ div.action.danger{
animation-timing-function: ease-in-out; animation-timing-function: ease-in-out;
} }
div.action p.action-body{ div.action p.action-body{
cursor: url('/resources/logos/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
padding: 10px; padding: 10px;
background-color: #2d2d2dba; background-color: #2d2d2dba;
color: #fff; color: #fff;
@ -1225,3 +1225,11 @@ div.action.danger p.action-body{
50% {bottom: 30px;} 50% {bottom: 30px;}
100% {bottom: 40px;} 100% {bottom: 40px;}
} }
#svelte-overlay {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}

View File

@ -1,7 +1,7 @@
import "jasmine"; import "jasmine";
import {Room} from "../../../src/Connexion/Room"; import {Room} from "../../../src/Connexion/Room";
import {flattenGroupLayersMap} from "../../../src/Phaser/Map/LayersFlattener"; import {flattenGroupLayersMap} from "../../../src/Phaser/Map/LayersFlattener";
import {ITiledMapLayer} from "../../../src/Phaser/Map/ITiledMap"; import type {ITiledMapLayer} from "../../../src/Phaser/Map/ITiledMap";
describe("Layers flattener", () => { describe("Layers flattener", () => {
it("should iterate maps with no group", () => { it("should iterate maps with no group", () => {

View File

@ -50,6 +50,6 @@ describe("Test HdpiManager", () => {
const result = hdpiManager.getOptimalGameSize({ width: 1280, height: 768 }); const result = hdpiManager.getOptimalGameSize({ width: 1280, height: 768 });
expect(result.game.width).toEqual(1280); expect(result.game.width).toEqual(1280);
expect(result.game.height).toEqual(768); expect(result.game.height).toEqual(768);
expect(hdpiManager.zoomModifier).toEqual(1); expect(hdpiManager.zoomModifier).toEqual(2 / 3);
}); });
}); });

View File

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"include": ["./src/**/*", "./tests/**/*"],
"compilerOptions": {
"module": "CommonJS",
"lib": ["es2015","dom"],
"types": ["svelte", "node"]
},
}

View File

@ -0,0 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"esModuleInterop": true
}
}

View File

@ -1,16 +1,22 @@
{ {
// "include": ["src/**/*", "webpack.config.ts"],
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./dist/", "outDir": "./dist/",
"sourceMap": true, "sourceMap": true,
"moduleResolution": "node", "moduleResolution": "node",
"module": "CommonJS", //"module": "CommonJS",
"target": "ES2015", "module": "ESNext",
"target": "ES2017",
"declaration": false, "declaration": false,
"downlevelIteration": true, "downlevelIteration": true,
"jsx": "react", "jsx": "react",
"allowJs": true, "allowJs": true,
"esModuleInterop": true, "esModuleInterop": true,
"importsNotUsedAsValues": "error",
"strict": true, /* Enable all strict type-checking options. */ "strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */ "strictNullChecks": true, /* Enable strict null checks. */

View File

@ -1,88 +0,0 @@
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
'main': './src/index.ts',
'iframe_api': './src/iframe_api.ts'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
host: '0.0.0.0',
sockPort: 80,
disableHostCheck: true,
historyApiFallback: {
rewrites: [
{ from: /^_\/.*$/, to: '/index.html' }
],
disableDotRule: true
},
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader?url=false', 'sass-loader'],
},
],
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
},
output: {
filename: (pathData) => {
// Add a content hash only for the main bundle.
// We want the iframe_api.js file to keep its name as it will be referenced from outside iframes.
return pathData.chunk.name === 'main' ? 'js/[name].[contenthash].js': '[name].js';
},
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
externals:[
require('webpack-require-http')
],
plugins: [
new MiniCssExtractPlugin({filename: 'style.[contenthash].css'}),
new HtmlWebpackPlugin(
{
template: './dist/index.tmpl.html.tmp',
minify: {
collapseWhitespace: true,
keepClosingSlash: true,
removeComments: false,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
},
chunks: ['main']
}
),
new webpack.ProvidePlugin({
Phaser: 'phaser'
}),
new webpack.EnvironmentPlugin({
'API_URL': null,
'PUSHER_URL': undefined,
'UPLOADER_URL': null,
'ADMIN_URL': null,
'DEBUG_MODE': null,
'STUN_SERVER': null,
'TURN_SERVER': null,
'TURN_USER': null,
'TURN_PASSWORD': null,
'JITSI_URL': null,
'JITSI_PRIVATE_MODE': null,
'START_ROOM_URL': null
})
],
};

174
front/webpack.config.ts Normal file
View File

@ -0,0 +1,174 @@
import type {Configuration} from "webpack";
import type WebpackDevServer from "webpack-dev-server";
import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import sveltePreprocess from 'svelte-preprocess';
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import NodePolyfillPlugin from 'node-polyfill-webpack-plugin';
const mode = process.env.NODE_ENV ?? 'development';
const isProduction = mode === 'production';
const isDevelopment = !isProduction;
module.exports = {
entry: {
'main': './src/index.ts',
'iframe_api': './src/iframe_api.ts'
},
mode: mode,
devtool: isDevelopment ? 'inline-source-map' : 'source-map',
devServer: {
contentBase: './dist',
host: '0.0.0.0',
sockPort: 80,
disableHostCheck: true,
historyApiFallback: {
rewrites: [
{ from: /^_\/.*$/, to: '/index.html' }
],
disableDotRule: true
},
},
module: {
rules: [
{
test: /\.tsx?$/,
//use: 'ts-loader',
exclude: /node_modules/,
loader: 'ts-loader',
options: {
transpileOnly: true,
},
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader, {
loader: 'css-loader',
options: {
//url: false,
sourceMap: true
}
}, 'sass-loader'],
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
//url: false,
sourceMap: true
}
}
]
},
{
test: /\.(html|svelte)$/,
exclude: /node_modules/,
use: {
loader: 'svelte-loader',
options: {
compilerOptions: {
// Dev mode must be enabled for HMR to work!
dev: isDevelopment
},
emitCss: isProduction,
hotReload: isDevelopment,
hotOptions: {
// List of options and defaults: https://www.npmjs.com/package/svelte-loader-hot#usage
noPreserveState: false,
optimistic: true,
},
preprocess: sveltePreprocess({
scss: true,
sass: true,
})
}
}
},
// Required to prevent errors from Svelte on Webpack 5+, omit on Webpack 4
// See: https://github.com/sveltejs/svelte-loader#usage
{
test: /node_modules\/svelte\/.*\.mjs$/,
resolve: {
fullySpecified: false
}
},
{
test: /\.(ttf|eot|svg|png|gif|jpg)$/,
exclude: /node_modules/,
type: 'asset'
}
],
},
resolve: {
alias: {
svelte: path.resolve('node_modules', 'svelte')
},
extensions: [ '.tsx', '.ts', '.js', '.svelte' ],
mainFields: ['svelte', 'browser', 'module', 'main']
},
output: {
filename: (pathData) => {
// Add a content hash only for the main bundle.
// We want the iframe_api.js file to keep its name as it will be referenced from outside iframes.
return pathData.chunk?.name === 'main' ? 'js/[name].[contenthash].js': '[name].js';
},
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new ForkTsCheckerWebpackPlugin({
eslint: {
files: './src/**/*.ts'
}
}),
new MiniCssExtractPlugin({filename: '[name].[contenthash].css'}),
new HtmlWebpackPlugin(
{
template: './dist/index.tmpl.html.tmp',
minify: {
collapseWhitespace: true,
keepClosingSlash: true,
removeComments: false,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
},
chunks: ['main']
}
),
new webpack.ProvidePlugin({
Phaser: 'phaser'
}),
new NodePolyfillPlugin(),
new webpack.EnvironmentPlugin({
'API_URL': null,
'SKIP_RENDER_OPTIMIZATIONS': false,
'DISABLE_NOTIFICATIONS': false,
'PUSHER_URL': undefined,
'UPLOADER_URL': null,
'ADMIN_URL': null,
'DEBUG_MODE': null,
'STUN_SERVER': null,
'TURN_SERVER': null,
'TURN_USER': null,
'TURN_PASSWORD': null,
'JITSI_URL': null,
'JITSI_PRIVATE_MODE': null,
'START_ROOM_URL': null,
'MAX_USERNAME_LENGTH': 8,
'MAX_PER_GROUP': 4
})
],
} as Configuration & WebpackDevServer.Configuration;

View File

@ -1,7 +0,0 @@
const merge = require('webpack-merge');
const common = require('./webpack.config.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map'
});

File diff suppressed because it is too large Load Diff