diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..f579acb1
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+**/node_modules/**
+**/Dockerfile
diff --git a/.env.template b/.env.template
index 330f3865..a83bd171 100644
--- a/.env.template
+++ b/.env.template
@@ -6,3 +6,7 @@ JITSI_ISS=
SECRET_JITSI_KEY=
ADMIN_API_TOKEN=123
START_ROOM_URL=/_/global/maps.workadventure.localhost/Floor0/floor0.json
+# If your Turn server is configured to use the Turn REST API, you should put the shared auth secret here.
+# 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.
+TURN_STATIC_AUTH_SECRET=
diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml
index 5b0c711e..ce186cec 100644
--- a/.github/workflows/build-and-deploy.yml
+++ b/.github/workflows/build-and-deploy.yml
@@ -150,6 +150,7 @@ jobs:
JITSI_ISS: ${{ secrets.JITSI_ISS }}
JITSI_URL: ${{ secrets.JITSI_URL }}
SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }}
+ TURN_STATIC_AUTH_SECRET: ${{ secrets.TURN_STATIC_AUTH_SECRET }}
with:
namespace: workadventure-${{ env.GITHUB_REF_SLUG }}
@@ -159,34 +160,5 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- msg: Environment deployed at https://${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
+ msg: Environment deployed at https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
check_for_duplicate_msg: true
-
- - name: Run Cypress tests
- uses: cypress-io/github-action@v2
- if: ${{ env.GITHUB_REF_SLUG != 'master' }}
- env:
- CYPRESS_BASE_URL: https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
- with:
- env: host=play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com,port=80
- spec: cypress/integration/spec.js
- wait-on: https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
- working-directory: e2e
-
- - name: Run Cypress tests in prod
- uses: cypress-io/github-action@v2
- if: ${{ env.GITHUB_REF_SLUG == 'master' }}
- env:
- CYPRESS_BASE_URL: https://play.workadventu.re
- with:
- env: host=play.workadventu.re
- spec: cypress/integration/spec.js
- wait-on: https://workadventu.re
- working-directory: e2e
-
- - name: "Upload the screenshot on test failure"
- uses: actions/upload-artifact@v1
- if: failure()
- with:
- name: "screenshot"
- path: "./e2e/cypress/screenshots/spec.js/WorkAdventureGame -- loads (failed).png"
diff --git a/README.md b/README.md
index faafed98..a8c186b6 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,6 @@ Demo here : [https://workadventu.re/](https://workadventu.re/).
# Work Adventure
-## Work in progress
-
Work Adventure is a web-based collaborative workspace for small to medium teams (2-100 people) presented in the form of a
16-bit video game.
@@ -15,7 +13,7 @@ In Work Adventure, you can move around your office and talk to your colleagues (
triggered when you move next to a colleague).
-## Getting started
+## Setting up a development environment
Install Docker.
@@ -101,5 +99,7 @@ Vagrant destroy
* `Vagrant halt`: stop your VM Vagrant.
* `Vagrant destroy`: delete your VM Vagrant.
-## Features developed
-You have more details of features developed in back [README.md](./back/README.md).
+## Setting up a production environment
+
+The way you set up your production environment will highly depend on your servers.
+We provide a production ready `docker-compose` file that you can use as a good starting point in the [contrib/docker](https://github.com/thecodingmachine/workadventure/tree/master/contrib/docker) directory.
diff --git a/back/Dockerfile b/back/Dockerfile
index 5ec83a8f..e95145cd 100644
--- a/back/Dockerfile
+++ b/back/Dockerfile
@@ -1,16 +1,26 @@
-FROM thecodingmachine/workadventure-back-base:latest as builder
-WORKDIR /var/www/messages
-COPY --chown=docker:docker messages .
+# protobuf build
+FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder
+WORKDIR /usr/src
+COPY messages .
RUN yarn install && yarn proto
-FROM thecodingmachine/nodejs:12
-
-COPY --chown=docker:docker back .
-COPY --from=builder --chown=docker:docker /var/www/messages/generated /usr/src/app/src/Messages/generated
+# typescript build
+FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder2
+WORKDIR /usr/src
+COPY back/yarn.lock back/package.json ./
RUN yarn install
-
+COPY back .
+COPY --from=builder /usr/src/generated src/Messages/generated
ENV NODE_ENV=production
RUN yarn run tsc
-CMD ["yarn", "run", "runprod"]
+# final production image
+FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76
+WORKDIR /usr/src
+COPY back/yarn.lock back/package.json ./
+COPY --from=builder2 /usr/src/dist /usr/src/dist
+ENV NODE_ENV=production
+RUN yarn install --production
+USER node
+CMD ["yarn", "run", "runprod"]
diff --git a/back/README.md b/back/README.md
deleted file mode 100644
index 8a78f403..00000000
--- a/back/README.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Back Features
-
-## Login
-To start your game, you must authenticate on the server back.
-When you are authenticated, the back server return token and room starting.
-```
-POST => /login
-Params :
- email: email of user.
-```
-
-## Join a room
-When a user is connected, the user can join a room.
-So you must send emit `join-room` with information user:
-```
-Socket.io => 'join-room'
-
- userId: user id of gamer
- roomId: room id when user enter in game
- position: {
- x: position x on map
- y: position y on map
- }
-```
-All data users are stocked on socket client.
-
-## Send position user
-When user move on the map, you can share new position on back with event `user-position`.
-The information sent:
-```
-Socket.io => 'user-position'
-
- userId: user id of gamer
- roomId: room id when user enter in game
- position: {
- x: position x on map
- y: position y on map
- }
-```
-All data users are updated on socket client.
-
-## Receive positions of all users
-The application sends position of all users in each room in every few 10 milliseconds.
-The data will pushed on event `user-position`:
-```
-Socket.io => 'user-position'
-
- [
- {
- userId: user id of gamer
- roomId: room id when user enter in game
- position: {
- x: position x on map
- y: position y on map
- }
- },
- ...
- ]
-```
-
-[<<< back](../README.md)
\ No newline at end of file
diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts
index 2cbfbf2e..b12f0542 100644
--- a/back/src/Enum/EnvironmentVariable.ts
+++ b/back/src/Enum/EnvironmentVariable.ts
@@ -1,4 +1,3 @@
-const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false;
@@ -12,9 +11,9 @@ const SECRET_JITSI_KEY = process.env.SECRET_JITSI_KEY || '';
const HTTP_PORT = parseInt(process.env.HTTP_PORT || '8080') || 8080;
const GRPC_PORT = parseInt(process.env.GRPC_PORT || '50051') || 50051;
export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed
+export const TURN_STATIC_AUTH_SECRET = process.env.TURN_STATIC_AUTH_SECRET || '';
export {
- SECRET_KEY,
MINIMUM_DISTANCE,
ADMIN_API_URL,
ADMIN_API_TOKEN,
diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts
index 9c46a41b..3e2dd3e0 100644
--- a/back/src/Services/AdminApi.ts
+++ b/back/src/Services/AdminApi.ts
@@ -100,11 +100,12 @@ class AdminApi {
return res.data;
}
- reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string) {
+ reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string, reportWorldSlug: string) {
return Axios.post(`${ADMIN_API_URL}/api/report`, {
reportedUserUuid,
reportedUserComment,
reporterUserUuid,
+ reportWorldSlug,
},
{
headers: {"Authorization": `${ADMIN_API_TOKEN}`}
diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts
index c90b51cf..3d6906ea 100644
--- a/back/src/Services/SocketManager.ts
+++ b/back/src/Services/SocketManager.ts
@@ -28,7 +28,13 @@ import {User, UserSocket} from "../Model/User";
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
import {Group} from "../Model/Group";
import {cpuTracker} from "./CpuTracker";
-import {GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable";
+import {
+ GROUP_RADIUS,
+ JITSI_ISS,
+ MINIMUM_DISTANCE,
+ SECRET_JITSI_KEY,
+ TURN_STATIC_AUTH_SECRET
+} from "../Enum/EnvironmentVariable";
import {Movable} from "../Model/Movable";
import {PositionInterface} from "../Model/PositionInterface";
import {adminApi, CharacterTexture} from "./AdminApi";
@@ -40,6 +46,8 @@ import {ZoneSocket} from "../RoomManager";
import {Zone} from "_Model/Zone";
import Debug from "debug";
import {Admin} from "_Model/Admin";
+import crypto from "crypto";
+
const debug = Debug('sockermanager');
@@ -275,6 +283,12 @@ export class SocketManager {
const webrtcSignalToClient = new WebRtcSignalToClientMessage();
webrtcSignalToClient.setUserid(user.id);
webrtcSignalToClient.setSignal(data.getSignal());
+ // TODO: only compute credentials if data.signal.type === "offer"
+ if (TURN_STATIC_AUTH_SECRET !== '') {
+ const {username, password} = this.getTURNCredentials(''+user.id, TURN_STATIC_AUTH_SECRET);
+ webrtcSignalToClient.setWebrtcusername(username);
+ webrtcSignalToClient.setWebrtcpassword(password);
+ }
const serverToClientMessage = new ServerToClientMessage();
serverToClientMessage.setWebrtcsignaltoclientmessage(webrtcSignalToClient);
@@ -295,6 +309,12 @@ export class SocketManager {
const webrtcSignalToClient = new WebRtcSignalToClientMessage();
webrtcSignalToClient.setUserid(user.id);
webrtcSignalToClient.setSignal(data.getSignal());
+ // TODO: only compute credentials if data.signal.type === "offer"
+ if (TURN_STATIC_AUTH_SECRET !== '') {
+ const {username, password} = this.getTURNCredentials(''+user.id, TURN_STATIC_AUTH_SECRET);
+ webrtcSignalToClient.setWebrtcusername(username);
+ webrtcSignalToClient.setWebrtcpassword(password);
+ }
const serverToClientMessage = new ServerToClientMessage();
serverToClientMessage.setWebrtcscreensharingsignaltoclientmessage(webrtcSignalToClient);
@@ -487,6 +507,11 @@ export class SocketManager {
webrtcStartMessage1.setUserid(otherUser.id);
webrtcStartMessage1.setName(otherUser.name);
webrtcStartMessage1.setInitiator(true);
+ if (TURN_STATIC_AUTH_SECRET !== '') {
+ const {username, password} = this.getTURNCredentials(''+otherUser.id, TURN_STATIC_AUTH_SECRET);
+ webrtcStartMessage1.setWebrtcusername(username);
+ webrtcStartMessage1.setWebrtcpassword(password);
+ }
const serverToClientMessage1 = new ServerToClientMessage();
serverToClientMessage1.setWebrtcstartmessage(webrtcStartMessage1);
@@ -500,6 +525,11 @@ export class SocketManager {
webrtcStartMessage2.setUserid(user.id);
webrtcStartMessage2.setName(user.name);
webrtcStartMessage2.setInitiator(false);
+ if (TURN_STATIC_AUTH_SECRET !== '') {
+ const {username, password} = this.getTURNCredentials(''+user.id, TURN_STATIC_AUTH_SECRET);
+ webrtcStartMessage2.setWebrtcusername(username);
+ webrtcStartMessage2.setWebrtcpassword(password);
+ }
const serverToClientMessage2 = new ServerToClientMessage();
serverToClientMessage2.setWebrtcstartmessage(webrtcStartMessage2);
@@ -512,6 +542,25 @@ export class SocketManager {
}
}
+ /**
+ * Computes a unique user/password for the TURN server, using a shared secret between the WorkAdventure API server
+ * and the Coturn server.
+ * The Coturn server should be initialized with parameters: `--use-auth-secret --static-auth-secret=MySecretKey`
+ */
+ private getTURNCredentials(name: string, secret: string): {username: string, password: string} {
+ const unixTimeStamp = Math.floor(Date.now()/1000) + 4*3600; // this credential would be valid for the next 4 hours
+ const username = [unixTimeStamp, name].join(':');
+ const hmac = crypto.createHmac('sha1', secret);
+ hmac.setEncoding('base64');
+ hmac.write(username);
+ hmac.end();
+ const password = hmac.read();
+ return {
+ username: username,
+ password: password
+ };
+ }
+
//disconnect user
private disConnectedUser(user: User, group: Group) {
// Most of the time, sending a disconnect event to one of the players is enough (the player will close the connection
diff --git a/contrib/docker/.env.prod.template b/contrib/docker/.env.prod.template
new file mode 100644
index 00000000..c0c10181
--- /dev/null
+++ b/contrib/docker/.env.prod.template
@@ -0,0 +1,20 @@
+# The base domain
+DOMAIN=workadventure.localhost
+
+DEBUG_MODE=false
+JITSI_URL=meet.jit.si
+# If your Jitsi environment has authentication set up, you MUST set JITSI_PRIVATE_MODE to "true" and you MUST pass a SECRET_JITSI_KEY to generate the JWT secret
+JITSI_PRIVATE_MODE=false
+JITSI_ISS=
+SECRET_JITSI_KEY=
+
+# URL of the TURN server (needed to "punch a hole" through some networks for P2P connections)
+TURN_SERVER=
+TURN_USER=
+TURN_PASSWORD=
+
+# The URL used by default, in the form: "/_/global/map/url.json"
+START_ROOM_URL=/_/global/maps.workadventu.re/Floor0/floor0.json
+
+# The email address used by Let's encrypt to send renewal warnings (compulsory)
+ACME_EMAIL=
diff --git a/contrib/docker/docker-compose.prod.yaml b/contrib/docker/docker-compose.prod.yaml
new file mode 100644
index 00000000..22860748
--- /dev/null
+++ b/contrib/docker/docker-compose.prod.yaml
@@ -0,0 +1,100 @@
+version: "3.3"
+services:
+ reverse-proxy:
+ image: traefik:v2.3
+ command:
+ - --log.level=WARN
+ #- --api.insecure=true
+ - --providers.docker
+ - --entryPoints.web.address=:80
+ - --entrypoints.web.http.redirections.entryPoint.to=websecure
+ - --entrypoints.web.http.redirections.entryPoint.scheme=https
+ - --entryPoints.websecure.address=:443
+ - --certificatesresolvers.myresolver.acme.email=d.negrier@thecodingmachine.com
+ - --certificatesresolvers.myresolver.acme.storage=/acme.json
+ # used during the challenge
+ - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
+ ports:
+ - "80:80"
+ - "443:443"
+ # The Web UI (enabled by --api.insecure=true)
+ #- "8080:8080"
+ depends_on:
+ - pusher
+ - front
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ - ./acme.json:/acme.json
+ restart: unless-stopped
+
+
+ front:
+ build:
+ context: ../..
+ dockerfile: front/Dockerfile
+ #image: thecodingmachine/workadventure-front:master
+ environment:
+ DEBUG_MODE: "$DEBUG_MODE"
+ JITSI_URL: $JITSI_URL
+ JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
+ API_URL: pusher.${DOMAIN}
+ TURN_SERVER: "${TURN_SERVER}"
+ TURN_USER: "${TURN_USER}"
+ TURN_PASSWORD: "${TURN_PASSWORD}"
+ START_ROOM_URL: "${START_ROOM_URL}"
+ labels:
+ - "traefik.http.routers.front.rule=Host(`play.${DOMAIN}`)"
+ - "traefik.http.routers.front.entryPoints=web,traefik"
+ - "traefik.http.services.front.loadbalancer.server.port=80"
+ - "traefik.http.routers.front-ssl.rule=Host(`play.${DOMAIN}`)"
+ - "traefik.http.routers.front-ssl.entryPoints=websecure"
+ - "traefik.http.routers.front-ssl.tls=true"
+ - "traefik.http.routers.front-ssl.service=front"
+ - "traefik.http.routers.front-ssl.tls.certresolver=myresolver"
+ restart: unless-stopped
+
+ pusher:
+ build:
+ context: ../..
+ dockerfile: pusher/Dockerfile
+ #image: thecodingmachine/workadventure-pusher:master
+ command: yarn run runprod
+ environment:
+ SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
+ SECRET_KEY: yourSecretKey
+ API_URL: back:50051
+ JITSI_URL: $JITSI_URL
+ JITSI_ISS: $JITSI_ISS
+ labels:
+ - "traefik.http.routers.pusher.rule=Host(`pusher.${DOMAIN}`)"
+ - "traefik.http.routers.pusher.entryPoints=web,traefik"
+ - "traefik.http.services.pusher.loadbalancer.server.port=8080"
+ - "traefik.http.routers.pusher-ssl.rule=Host(`pusher.${DOMAIN}`)"
+ - "traefik.http.routers.pusher-ssl.entryPoints=websecure"
+ - "traefik.http.routers.pusher-ssl.tls=true"
+ - "traefik.http.routers.pusher-ssl.service=pusher"
+ - "traefik.http.routers.pusher-ssl.tls.certresolver=myresolver"
+ restart: unless-stopped
+
+ back:
+ build:
+ context: ../..
+ dockerfile: back/Dockerfile
+ #image: thecodingmachine/workadventure-back:master
+ command: yarn run runprod
+ environment:
+ SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
+ ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
+ ADMIN_API_URL: "$ADMIN_API_URL"
+ JITSI_URL: $JITSI_URL
+ JITSI_ISS: $JITSI_ISS
+ labels:
+ - "traefik.http.routers.back.rule=Host(`api.${DOMAIN}`)"
+ - "traefik.http.routers.back.entryPoints=web"
+ - "traefik.http.services.back.loadbalancer.server.port=8080"
+ - "traefik.http.routers.back-ssl.rule=Host(`api.${DOMAIN}`)"
+ - "traefik.http.routers.back-ssl.entryPoints=websecure"
+ - "traefik.http.routers.back-ssl.tls=true"
+ - "traefik.http.routers.back-ssl.service=back"
+ - "traefik.http.routers.back-ssl.tls.certresolver=myresolver"
+ restart: unless-stopped
diff --git a/deeployer.libsonnet b/deeployer.libsonnet
index 89571945..5093c86a 100644
--- a/deeployer.libsonnet
+++ b/deeployer.libsonnet
@@ -3,7 +3,8 @@
local namespace = env.GITHUB_REF_SLUG,
local tag = namespace,
local url = if namespace == "master" then "workadventu.re" else namespace+".workadventure.test.thecodingmachine.com",
- local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://"+url else null,
+ // 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,
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
"version": "1.0",
"containers": {
@@ -21,6 +22,7 @@
"JITSI_ISS": env.JITSI_ISS,
"JITSI_URL": env.JITSI_URL,
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
+ "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {}
@@ -39,6 +41,7 @@
"JITSI_ISS": env.JITSI_ISS,
"JITSI_URL": env.JITSI_URL,
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
+ "TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {}
@@ -79,6 +82,7 @@
"TURN_USER": "workadventure",
"TURN_PASSWORD": "WorkAdventure123",
"JITSI_PRIVATE_MODE": if env.SECRET_JITSI_KEY != '' then "true" else "false",
+ "START_ROOM_URL": "/_/global/maps."+url+"/Floor0/floor0.json"
//"GA_TRACKING_ID": "UA-10196481-11"
}
},
diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml
deleted file mode 100644
index bdf5855a..00000000
--- a/docker-compose.ci.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-version: '3'
-
-services:
-
- wait_app:
- image: dadarek/wait-for-dependencies
- depends_on:
- - reverse-proxy
- command: front:8080
- cypress:
- # the Docker image to use from https://github.com/cypress-io/cypress-docker-images
- image: "cypress/included:3.8.3"
- depends_on:
- - reverse-proxy
- environment:
- # pass base url to test pointing at the web application
- - CYPRESS_baseUrl=http://front:8080
- working_dir: /e2e
- volumes:
- - ./e2e/:/e2e
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 286c12ba..98071437 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -31,9 +31,12 @@ services:
ADMIN_URL: workadventure.localhost
STARTUP_COMMAND_1: ./templater.sh
STARTUP_COMMAND_2: yarn install
- TURN_SERVER: "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443"
- TURN_USER: workadventure
- TURN_PASSWORD: WorkAdventure123
+ STUN_SERVER: "stun:stun.l.google.com:19302"
+ TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349"
+ # 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
+ TURN_USER: ""
+ TURN_PASSWORD: ""
START_ROOM_URL: "$START_ROOM_URL"
command: yarn run start
volumes:
@@ -108,6 +111,7 @@ services:
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
+ TURN_STATIC_AUTH_SECRET: SomeStaticAuthSecret
volumes:
- ./back:/usr/src/app
labels:
@@ -149,3 +153,28 @@ services:
- ./back:/usr/src/back
- ./front:/usr/src/front
- ./pusher:/usr/src/pusher
+
+# coturn:
+# image: coturn/coturn:4.5.2
+# command:
+# - turnserver
+# #- -c=/etc/coturn/turnserver.conf
+# - --log-file=stdout
+# - --external-ip=$$(detect-external-ip)
+# - --listening-port=3478
+# - --min-port=10000
+# - --max-port=10010
+# - --tls-listening-port=5349
+# - --listening-ip=0.0.0.0
+# - --realm=coturn.workadventure.localhost
+# - --server-name=coturn.workadventure.localhost
+# - --lt-cred-mech
+# # Enable Coturn "REST API" to validate temporary passwords.
+# #- --use-auth-secret
+# #- --static-auth-secret=SomeStaticAuthSecret
+# #- --userdb=/var/lib/turn/turndb
+# - --user=workadventure:WorkAdventure123
+# # use real-valid certificate/privatekey files
+# #- --cert=/root/letsencrypt/fullchain.pem
+# #- --pkey=/root/letsencrypt/privkey.pem
+# network_mode: host
diff --git a/e2e/.gitignore b/e2e/.gitignore
deleted file mode 100644
index 92fb84fd..00000000
--- a/e2e/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-screenshots/
-videos/
-node_modules/
\ No newline at end of file
diff --git a/e2e/CYPRESS.md b/e2e/CYPRESS.md
deleted file mode 100644
index dce9d698..00000000
--- a/e2e/CYPRESS.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Testing with cypress
-
-This project use [cypress](https://www.cypress.io/) to do functional testing of the website.
-Unfortunately we cannot integrate it with docker-compose for the moment, so you will need to install some packages locally on your pc.
-
-## Getting Started
-
-You will need to install theses dependancies on linux (don't know about mac):
-
-```bash
-sudo apt update
-sudo apt install libgtk2.0-0 libgtk-3-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
-```
-
-Cypress can be installed locally in the e2e directory
-```bash
-cd e2e
-npm install
-```
-
-
-How to use:
-```bash
-npm run cy:run
-npm run cy:open
-```
-
-The first command will run all tests in the terminal, while the second will open the interactive task runner which allow you to easily manage the test workflow
-
-[More details here](https://docs.cypress.io/guides/getting-started/testing-your-app.html#Step-1-Start-your-server)
-
-## How to test a game
-
-Cypress cannot "see" and so cannot directly manipulate the canva created by Phaser.
-
-This means we have to do workarounds such as exposing core objects in the window so that cypress can manipulate them or doing console that cypress can catch.
\ No newline at end of file
diff --git a/e2e/cypress.json b/e2e/cypress.json
deleted file mode 100644
index cea4979b..00000000
--- a/e2e/cypress.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "baseUrl": "http://workadventure.localhost",
- "video": false,
- "defaultCommandTimeout": 20000,
- "pluginsFile": false,
- "supportFile": false
-}
\ No newline at end of file
diff --git a/e2e/cypress/integration/spec.js b/e2e/cypress/integration/spec.js
deleted file mode 100644
index 7f94a502..00000000
--- a/e2e/cypress/integration/spec.js
+++ /dev/null
@@ -1,25 +0,0 @@
-Cypress.on('window:before:load', (win) => {
- // because this is called before any scripts
- // have loaded - the ga function is undefined
- // so we need to create it.
- win.cypressAsserter = cy.stub().as('ca')
-})
-
-describe('WorkAdventureGame', () => {
- beforeEach(() => {
- cy.visit('/', {
- onBeforeLoad (win) {
- cy.spy(win.console, 'log').as('console.log')
- },
- })
-
- });
-
- it('loads', () => {
- cy.get('@console.log').should('be.calledWith', 'Started the game')
- cy.get('@console.log').should('be.calledWith', 'Preloading')
- cy.get('@console.log').should('be.calledWith', 'Preloading done')
- cy.get('@console.log').should('be.calledWith', 'startInit')
- cy.get('@console.log').should('be.calledWith', 'startInit done')
- });
-});
\ No newline at end of file
diff --git a/e2e/package-lock.json b/e2e/package-lock.json
deleted file mode 100644
index bfd6255c..00000000
--- a/e2e/package-lock.json
+++ /dev/null
@@ -1,1406 +0,0 @@
-{
- "requires": true,
- "lockfileVersion": 1,
- "dependencies": {
- "@cypress/listr-verbose-renderer": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz",
- "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=",
- "requires": {
- "chalk": "1.1.3",
- "cli-cursor": "1.0.2",
- "date-fns": "1.30.1",
- "figures": "1.7.0"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "@cypress/xvfb": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
- "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==",
- "requires": {
- "debug": "3.2.6",
- "lodash.once": "4.1.1"
- }
- },
- "@types/sizzle": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
- "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg=="
- },
- "ajv": {
- "version": "6.11.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz",
- "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==",
- "requires": {
- "fast-deep-equal": "3.1.1",
- "fast-json-stable-stringify": "2.1.0",
- "json-schema-traverse": "0.4.1",
- "uri-js": "4.2.2"
- }
- },
- "ansi-escapes": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
- "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4="
- },
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
- },
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
- },
- "arch": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz",
- "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg=="
- },
- "asn1": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
- "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
- "requires": {
- "safer-buffer": "2.1.2"
- }
- },
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
- },
- "async": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
- "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
- "requires": {
- "lodash": "4.17.15"
- }
- },
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
- },
- "aws-sign2": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
- "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
- },
- "aws4": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
- "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
- },
- "bcrypt-pbkdf": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
- "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
- "requires": {
- "tweetnacl": "0.14.5"
- }
- },
- "bluebird": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
- "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw="
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "requires": {
- "balanced-match": "1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "buffer-crc32": {
- "version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
- },
- "buffer-from": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
- },
- "cachedir": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-1.3.0.tgz",
- "integrity": "sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg==",
- "requires": {
- "os-homedir": "1.0.2"
- }
- },
- "caseless": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "requires": {
- "ansi-styles": "3.2.1",
- "escape-string-regexp": "1.0.5",
- "supports-color": "5.5.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "requires": {
- "color-convert": "1.9.3"
- }
- }
- }
- },
- "check-more-types": {
- "version": "2.24.0",
- "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
- "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA="
- },
- "ci-info": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
- "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
- },
- "cli-cursor": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
- "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
- "requires": {
- "restore-cursor": "1.0.1"
- }
- },
- "cli-spinners": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz",
- "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw="
- },
- "cli-truncate": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz",
- "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=",
- "requires": {
- "slice-ansi": "0.0.4",
- "string-width": "1.0.2"
- }
- },
- "code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
- },
- "combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "requires": {
- "delayed-stream": "1.0.0"
- }
- },
- "commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
- },
- "common-tags": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
- "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw=="
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
- },
- "concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "requires": {
- "buffer-from": "1.1.1",
- "inherits": "2.0.4",
- "readable-stream": "2.3.7",
- "typedarray": "0.0.6"
- }
- },
- "core-util-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
- },
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "requires": {
- "nice-try": "1.0.5",
- "path-key": "2.0.1",
- "semver": "5.7.1",
- "shebang-command": "1.2.0",
- "which": "1.3.1"
- }
- },
- "cypress": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/cypress/-/cypress-3.8.3.tgz",
- "integrity": "sha512-I9L/d+ilTPPA4vq3NC1OPKmw7jJIpMKNdyfR8t1EXYzYCjyqbc59migOm1YSse/VRbISLJ+QGb5k4Y3bz2lkYw==",
- "requires": {
- "@cypress/listr-verbose-renderer": "0.4.1",
- "@cypress/xvfb": "1.2.4",
- "@types/sizzle": "2.3.2",
- "arch": "2.1.1",
- "bluebird": "3.5.0",
- "cachedir": "1.3.0",
- "chalk": "2.4.2",
- "check-more-types": "2.24.0",
- "commander": "2.15.1",
- "common-tags": "1.8.0",
- "debug": "3.2.6",
- "eventemitter2": "4.1.2",
- "execa": "0.10.0",
- "executable": "4.1.1",
- "extract-zip": "1.6.7",
- "fs-extra": "5.0.0",
- "getos": "3.1.1",
- "is-ci": "1.2.1",
- "is-installed-globally": "0.1.0",
- "lazy-ass": "1.6.0",
- "listr": "0.12.0",
- "lodash": "4.17.15",
- "log-symbols": "2.2.0",
- "minimist": "1.2.0",
- "moment": "2.24.0",
- "ramda": "0.24.1",
- "request": "2.88.0",
- "request-progress": "3.0.0",
- "supports-color": "5.5.0",
- "tmp": "0.1.0",
- "untildify": "3.0.3",
- "url": "0.11.0",
- "yauzl": "2.10.0"
- }
- },
- "dashdash": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
- "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
- "requires": {
- "assert-plus": "1.0.0"
- }
- },
- "date-fns": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
- "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
- },
- "debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
- "requires": {
- "ms": "2.1.2"
- }
- },
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
- },
- "ecc-jsbn": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
- "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
- "requires": {
- "jsbn": "0.1.1",
- "safer-buffer": "2.1.2"
- }
- },
- "elegant-spinner": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz",
- "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4="
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
- },
- "eventemitter2": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz",
- "integrity": "sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU="
- },
- "execa": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
- "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
- "requires": {
- "cross-spawn": "6.0.5",
- "get-stream": "3.0.0",
- "is-stream": "1.1.0",
- "npm-run-path": "2.0.2",
- "p-finally": "1.0.0",
- "signal-exit": "3.0.2",
- "strip-eof": "1.0.0"
- }
- },
- "executable": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz",
- "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==",
- "requires": {
- "pify": "2.3.0"
- }
- },
- "exit-hook": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
- "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g="
- },
- "extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
- },
- "extract-zip": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
- "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
- "requires": {
- "concat-stream": "1.6.2",
- "debug": "2.6.9",
- "mkdirp": "0.5.1",
- "yauzl": "2.4.1"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
- },
- "yauzl": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
- "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
- "requires": {
- "fd-slicer": "1.0.1"
- }
- }
- }
- },
- "extsprintf": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
- },
- "fast-deep-equal": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
- "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
- },
- "fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
- },
- "fd-slicer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
- "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
- "requires": {
- "pend": "1.2.0"
- }
- },
- "figures": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
- "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
- "requires": {
- "escape-string-regexp": "1.0.5",
- "object-assign": "4.1.1"
- }
- },
- "forever-agent": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
- "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
- },
- "form-data": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
- "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
- "requires": {
- "asynckit": "0.4.0",
- "combined-stream": "1.0.8",
- "mime-types": "2.1.26"
- }
- },
- "fs-extra": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
- "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
- "requires": {
- "graceful-fs": "4.2.3",
- "jsonfile": "4.0.0",
- "universalify": "0.1.2"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
- },
- "get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
- },
- "getos": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/getos/-/getos-3.1.1.tgz",
- "integrity": "sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg==",
- "requires": {
- "async": "2.6.1"
- }
- },
- "getpass": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
- "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
- "requires": {
- "assert-plus": "1.0.0"
- }
- },
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "requires": {
- "fs.realpath": "1.0.0",
- "inflight": "1.0.6",
- "inherits": "2.0.4",
- "minimatch": "3.0.4",
- "once": "1.4.0",
- "path-is-absolute": "1.0.1"
- }
- },
- "global-dirs": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
- "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
- "requires": {
- "ini": "1.3.5"
- }
- },
- "graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
- },
- "har-schema": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
- "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
- },
- "har-validator": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
- "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
- "requires": {
- "ajv": "6.11.0",
- "har-schema": "2.0.0"
- }
- },
- "has-ansi": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
- },
- "http-signature": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
- "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
- "requires": {
- "assert-plus": "1.0.0",
- "jsprim": "1.4.1",
- "sshpk": "1.16.1"
- }
- },
- "indent-string": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
- "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
- "requires": {
- "repeating": "2.0.1"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "ini": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
- },
- "is-ci": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
- "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
- "requires": {
- "ci-info": "1.6.0"
- }
- },
- "is-finite": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
- "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
- "requires": {
- "number-is-nan": "1.0.1"
- }
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "requires": {
- "number-is-nan": "1.0.1"
- }
- },
- "is-installed-globally": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
- "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
- "requires": {
- "global-dirs": "0.1.1",
- "is-path-inside": "1.0.1"
- }
- },
- "is-path-inside": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
- "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
- "requires": {
- "path-is-inside": "1.0.2"
- }
- },
- "is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
- "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
- },
- "is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
- },
- "is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
- },
- "isstream": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
- "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
- },
- "jsbn": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
- },
- "json-schema": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
- "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
- },
- "json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
- },
- "json-stringify-safe": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
- "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
- },
- "jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
- "requires": {
- "graceful-fs": "4.2.3"
- }
- },
- "jsprim": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
- "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
- "requires": {
- "assert-plus": "1.0.0",
- "extsprintf": "1.3.0",
- "json-schema": "0.2.3",
- "verror": "1.10.0"
- }
- },
- "lazy-ass": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
- "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM="
- },
- "listr": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz",
- "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=",
- "requires": {
- "chalk": "1.1.3",
- "cli-truncate": "0.2.1",
- "figures": "1.7.0",
- "indent-string": "2.1.0",
- "is-promise": "2.1.0",
- "is-stream": "1.1.0",
- "listr-silent-renderer": "1.1.1",
- "listr-update-renderer": "0.2.0",
- "listr-verbose-renderer": "0.4.1",
- "log-symbols": "1.0.2",
- "log-update": "1.0.2",
- "ora": "0.2.3",
- "p-map": "1.2.0",
- "rxjs": "5.5.12",
- "stream-to-observable": "0.1.0",
- "strip-ansi": "3.0.1"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
- }
- },
- "log-symbols": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
- "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
- "requires": {
- "chalk": "1.1.3"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "listr-silent-renderer": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz",
- "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4="
- },
- "listr-update-renderer": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz",
- "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=",
- "requires": {
- "chalk": "1.1.3",
- "cli-truncate": "0.2.1",
- "elegant-spinner": "1.0.1",
- "figures": "1.7.0",
- "indent-string": "3.2.0",
- "log-symbols": "1.0.2",
- "log-update": "1.0.2",
- "strip-ansi": "3.0.1"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
- }
- },
- "indent-string": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
- "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok="
- },
- "log-symbols": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
- "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
- "requires": {
- "chalk": "1.1.3"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "listr-verbose-renderer": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz",
- "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=",
- "requires": {
- "chalk": "1.1.3",
- "cli-cursor": "1.0.2",
- "date-fns": "1.30.1",
- "figures": "1.7.0"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
- },
- "lodash.once": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
- "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
- },
- "log-symbols": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
- "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
- "requires": {
- "chalk": "2.4.2"
- }
- },
- "log-update": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz",
- "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=",
- "requires": {
- "ansi-escapes": "1.4.0",
- "cli-cursor": "1.0.2"
- }
- },
- "mime-db": {
- "version": "1.43.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
- },
- "mime-types": {
- "version": "2.1.26",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
- "requires": {
- "mime-db": "1.43.0"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "requires": {
- "brace-expansion": "1.1.11"
- }
- },
- "minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "requires": {
- "minimist": "0.0.8"
- },
- "dependencies": {
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
- }
- }
- },
- "moment": {
- "version": "2.24.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
- },
- "npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
- "requires": {
- "path-key": "2.0.1"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
- },
- "oauth-sign": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
- "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
- },
- "object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "requires": {
- "wrappy": "1.0.2"
- }
- },
- "onetime": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
- "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k="
- },
- "ora": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz",
- "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=",
- "requires": {
- "chalk": "1.1.3",
- "cli-cursor": "1.0.2",
- "cli-spinners": "0.1.2",
- "object-assign": "4.1.1"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- }
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
- },
- "p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
- },
- "p-map": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
- "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA=="
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
- },
- "path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
- },
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
- },
- "pend": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
- },
- "performance-now": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
- },
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
- },
- "process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
- },
- "psl": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
- "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
- },
- "punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
- },
- "qs": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
- },
- "querystring": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
- },
- "ramda": {
- "version": "0.24.1",
- "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz",
- "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc="
- },
- "readable-stream": {
- "version": "2.3.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
- "requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.4",
- "isarray": "1.0.0",
- "process-nextick-args": "2.0.1",
- "safe-buffer": "5.1.2",
- "string_decoder": "1.1.1",
- "util-deprecate": "1.0.2"
- }
- },
- "repeating": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
- "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
- "requires": {
- "is-finite": "1.0.2"
- }
- },
- "request": {
- "version": "2.88.0",
- "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
- "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
- "requires": {
- "aws-sign2": "0.7.0",
- "aws4": "1.9.1",
- "caseless": "0.12.0",
- "combined-stream": "1.0.8",
- "extend": "3.0.2",
- "forever-agent": "0.6.1",
- "form-data": "2.3.3",
- "har-validator": "5.1.3",
- "http-signature": "1.2.0",
- "is-typedarray": "1.0.0",
- "isstream": "0.1.2",
- "json-stringify-safe": "5.0.1",
- "mime-types": "2.1.26",
- "oauth-sign": "0.9.0",
- "performance-now": "2.1.0",
- "qs": "6.5.2",
- "safe-buffer": "5.1.2",
- "tough-cookie": "2.4.3",
- "tunnel-agent": "0.6.0",
- "uuid": "3.4.0"
- }
- },
- "request-progress": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
- "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=",
- "requires": {
- "throttleit": "1.0.0"
- }
- },
- "restore-cursor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
- "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
- "requires": {
- "exit-hook": "1.1.1",
- "onetime": "1.1.0"
- }
- },
- "rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "requires": {
- "glob": "7.1.6"
- }
- },
- "rxjs": {
- "version": "5.5.12",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz",
- "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==",
- "requires": {
- "symbol-observable": "1.0.1"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
- "safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- },
- "shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
- "requires": {
- "shebang-regex": "1.0.0"
- }
- },
- "shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
- },
- "signal-exit": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
- },
- "slice-ansi": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
- "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU="
- },
- "sshpk": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
- "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
- "requires": {
- "asn1": "0.2.4",
- "assert-plus": "1.0.0",
- "bcrypt-pbkdf": "1.0.2",
- "dashdash": "1.14.1",
- "ecc-jsbn": "0.1.2",
- "getpass": "0.1.7",
- "jsbn": "0.1.1",
- "safer-buffer": "2.1.2",
- "tweetnacl": "0.14.5"
- }
- },
- "stream-to-observable": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz",
- "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4="
- },
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "requires": {
- "code-point-at": "1.1.0",
- "is-fullwidth-code-point": "1.0.0",
- "strip-ansi": "3.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "5.1.2"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "requires": {
- "has-flag": "3.0.0"
- }
- },
- "symbol-observable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
- "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ="
- },
- "throttleit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
- "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw="
- },
- "tmp": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
- "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
- "requires": {
- "rimraf": "2.7.1"
- }
- },
- "tough-cookie": {
- "version": "2.4.3",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
- "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
- "requires": {
- "psl": "1.7.0",
- "punycode": "1.4.1"
- },
- "dependencies": {
- "punycode": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
- "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
- }
- }
- },
- "tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
- "requires": {
- "safe-buffer": "5.1.2"
- }
- },
- "tweetnacl": {
- "version": "0.14.5",
- "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
- },
- "typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
- },
- "universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
- },
- "untildify": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz",
- "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA=="
- },
- "uri-js": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
- "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
- "requires": {
- "punycode": "2.1.1"
- }
- },
- "url": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
- "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
- "requires": {
- "punycode": "1.3.2",
- "querystring": "0.2.0"
- },
- "dependencies": {
- "punycode": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
- "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
- }
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
- },
- "uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
- },
- "verror": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
- "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
- "requires": {
- "assert-plus": "1.0.0",
- "core-util-is": "1.0.2",
- "extsprintf": "1.3.0"
- }
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "requires": {
- "isexe": "2.0.0"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
- },
- "yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
- "requires": {
- "buffer-crc32": "0.2.13",
- "fd-slicer": "1.1.0"
- },
- "dependencies": {
- "fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
- "requires": {
- "pend": "1.2.0"
- }
- }
- }
- }
- }
-}
diff --git a/e2e/package.json b/e2e/package.json
deleted file mode 100644
index 97bd098c..00000000
--- a/e2e/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "dependencies": {
- "cypress": "^3.8.3"
- },
- "scripts": {
- "cy:run": "cypress run",
- "cy:open": "cypress open"
- }
-}
diff --git a/front/dist/.gitignore b/front/dist/.gitignore
new file mode 100644
index 00000000..64233a9e
--- /dev/null
+++ b/front/dist/.gitignore
@@ -0,0 +1 @@
+index.html
\ No newline at end of file
diff --git a/front/dist/index.html.tmpl b/front/dist/index.tmpl.html
similarity index 100%
rename from front/dist/index.html.tmpl
rename to front/dist/index.tmpl.html
diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html
index 88c76ca2..665d4316 100644
--- a/front/dist/resources/html/gameMenu.html
+++ b/front/dist/resources/html/gameMenu.html
@@ -15,6 +15,14 @@
#gameMenu section {
margin: 10px;
}
+ section#socialLinks{
+ position: absolute;
+ margin-bottom: 0;
+ }
+ section#socialLinks img{
+ width: 32px;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
+ }
diff --git a/front/dist/resources/html/gameReport.html b/front/dist/resources/html/gameReport.html
new file mode 100644
index 00000000..59ca3592
--- /dev/null
+++ b/front/dist/resources/html/gameReport.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+ Moderate
+ What action do you want to take?
+
+
+ Block:
+ Block any communication from and to this user. This can be reverted.
+
+
+
+ Report:
+ Send a report message to the administrators of this room. They may later ban this user.
+
+
+
+
diff --git a/front/dist/resources/html/gameShare.html b/front/dist/resources/html/gameShare.html
index 4e487328..21c65014 100644
--- a/front/dist/resources/html/gameShare.html
+++ b/front/dist/resources/html/gameShare.html
@@ -14,9 +14,6 @@
width: 298px;
height: 150px;
}
- #gameShare .cautiousText {
- font-size: 50%;
- }
#gameShare h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
diff --git a/front/dist/resources/logos/blockSign.svg b/front/dist/resources/logos/blockSign.svg
new file mode 100644
index 00000000..c64ba294
--- /dev/null
+++ b/front/dist/resources/logos/blockSign.svg
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/front/dist/resources/logos/blockingIcon.png b/front/dist/resources/logos/blockingIcon.png
new file mode 100644
index 00000000..ef5f66cc
Binary files /dev/null and b/front/dist/resources/logos/blockingIcon.png differ
diff --git a/front/dist/resources/logos/cancel.png b/front/dist/resources/logos/cancel.png
new file mode 100644
index 00000000..5bf9b6d2
Binary files /dev/null and b/front/dist/resources/logos/cancel.png differ
diff --git a/front/dist/resources/logos/logo.png b/front/dist/resources/logos/logo.png
new file mode 100644
index 00000000..f4440ad5
Binary files /dev/null and b/front/dist/resources/logos/logo.png differ
diff --git a/front/dist/resources/logos/report.back.svg b/front/dist/resources/logos/report.back.svg
new file mode 100644
index 00000000..1cb3b068
--- /dev/null
+++ b/front/dist/resources/logos/report.back.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/front/dist/resources/logos/report.svg b/front/dist/resources/logos/report.svg
index 1cb3b068..14753256 100644
--- a/front/dist/resources/logos/report.svg
+++ b/front/dist/resources/logos/report.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/front/dist/resources/objects/facebook-icon.png b/front/dist/resources/objects/facebook-icon.png
new file mode 100644
index 00000000..7b74b9bf
Binary files /dev/null and b/front/dist/resources/objects/facebook-icon.png differ
diff --git a/front/dist/resources/objects/talk.png b/front/dist/resources/objects/talk.png
index b9ecdb30..bc06d3b0 100644
Binary files a/front/dist/resources/objects/talk.png and b/front/dist/resources/objects/talk.png differ
diff --git a/front/dist/resources/objects/twitter-icon.png b/front/dist/resources/objects/twitter-icon.png
new file mode 100644
index 00000000..f2fa90f1
Binary files /dev/null and b/front/dist/resources/objects/twitter-icon.png differ
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css
index 2e2c6c10..4bf05455 100644
--- a/front/dist/resources/style/style.css
+++ b/front/dist/resources/style/style.css
@@ -39,6 +39,7 @@ body .message-info.warning{
position: relative;
transition: all 0.2s ease;
background-color: #00000099;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
.video-container i{
position: absolute;
@@ -53,25 +54,71 @@ body .message-info.warning{
font-size: 28px;
color: white;
}
-.video-container img.active{
- display: block;
-}
+
.video-container img{
position: absolute;
display: none;
- width: 15px;
- height: 15px;
- background: #d93025;
- border-radius: 48px;
+ width: 25px;
+ height: 25px;
left: 5px;
bottom: 5px;
padding: 10px;
z-index: 2;
}
+.video-container img.block-logo {
+ left: 30%;
+ bottom: 15%;
+ width: 150px;
+ height: 150px;
+}
-.video-container img.report{
+.video-container button.report{
+ display: block;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
+ background: none;
+ background-color: rgba(0, 0, 0, 0);
+ border: none;
+ background-color: black;
+ border-radius: 15px;
+ position: absolute;
+ width: 0px;
+ height: 35px;
right: 5px;
- left: auto;
+ bottom: 5px;
+ padding: 0px;
+ overflow: hidden;
+ z-index: 2;
+ transition: all .5s ease;
+}
+
+.video-container:hover button.report{
+ width: 35px;
+ padding: 10px;
+}
+
+.video-container button.report:hover {
+ width: 150px;
+}
+
+.video-container button.report img{
+ position: absolute;
+ display: block;
+ bottom: 5px;
+ left: 5px;
+ margin: 0;
+ padding: 0;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
+}
+.video-container button.report span{
+ position: absolute;
+ bottom: 8px;
+ left: 36px;
+ color: white;
+ font-size: 16px;
+ cursor: url('/resources/logos/cursor_pointer.png'), pointer;
+}
+.video-container img.active {
+ display: block !important;
}
.video-container video{
@@ -150,10 +197,7 @@ video#myCamVideo{
transition: all .2s;
right: 224px;
}
-/*.btn-call{
- transition: all .1s;
- left: 0px;
-}*/
+
.btn-cam-action div img{
height: 22px;
width: 30px;
@@ -352,6 +396,7 @@ body {
#cowebsite {
position: fixed;
transition: transform 0.5s;
+ background-color: white;
}
#cowebsite.loading {
background-color: gray;
@@ -1078,7 +1123,7 @@ div.modal-report-user{
white-space: pre-wrap;
word-wrap: break-word;
}
-.discussion .messages .message p.a{
+.discussion .messages .message p a{
color: white;
}
diff --git a/front/package.json b/front/package.json
index 9776f0fd..0e50ba80 100644
--- a/front/package.json
+++ b/front/package.json
@@ -26,9 +26,10 @@
"axios": "^0.21.1",
"generic-type-guard": "^3.2.0",
"google-protobuf": "^3.13.0",
- "phaser": "^3.52.0",
+ "phaser": "3.24.1",
"queue-typescript": "^1.0.1",
"quill": "^1.3.7",
+ "rxjs": "^6.6.3",
"simple-peer": "^9.6.2",
"socket.io-client": "^2.3.0",
"webpack-require-http": "^0.4.3"
diff --git a/front/src/Administration/ConsoleGlobalMessageManager.ts b/front/src/Administration/ConsoleGlobalMessageManager.ts
index 5391b6a0..1833d7cd 100644
--- a/front/src/Administration/ConsoleGlobalMessageManager.ts
+++ b/front/src/Administration/ConsoleGlobalMessageManager.ts
@@ -332,7 +332,7 @@ export class ConsoleGlobalMessageManager {
}
active(){
- this.userInputManager.clearAllInputKeyboard();
+ this.userInputManager.clearAllKeys();
this.divMainConsole.style.top = '0';
this.activeConsole = true;
}
diff --git a/front/src/Administration/UserMessageManager.ts b/front/src/Administration/UserMessageManager.ts
index 12022b03..a20b4729 100644
--- a/front/src/Administration/UserMessageManager.ts
+++ b/front/src/Administration/UserMessageManager.ts
@@ -1,5 +1,8 @@
import {RoomConnection} from "../Connexion/RoomConnection";
import * as TypeMessages from "./TypeMessage";
+import List = Phaser.Structs.List;
+import {UpdatedLocalStreamCallback} from "../WebRtc/MediaManager";
+import {Banned} from "./TypeMessage";
export interface TypeMessageInterface {
showMessage(message: string): void;
@@ -8,6 +11,7 @@ export interface TypeMessageInterface {
export class UserMessageManager {
typeMessages: Map = new Map();
+ receiveBannedMessageListener: Set = new Set();
constructor(private Connection: RoomConnection) {
const valueTypeMessageTab = Object.values(TypeMessages);
@@ -21,7 +25,14 @@ export class UserMessageManager {
initialise() {
//receive signal to show message
this.Connection.receiveUserMessage((type: string, message: string) => {
- this.showMessage(type, message);
+ const typeMessage = this.showMessage(type, message);
+
+ //listener on banned receive message
+ if(typeMessage instanceof Banned) {
+ for (const callback of this.receiveBannedMessageListener) {
+ callback();
+ }
+ }
});
}
@@ -32,5 +43,10 @@ export class UserMessageManager {
return;
}
classTypeMessage.showMessage(message);
+ return classTypeMessage;
+ }
+
+ setReceiveBanListener(callback: Function){
+ this.receiveBannedMessageListener.add(callback);
}
}
\ No newline at end of file
diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts
index 2e6451f3..a0ae3119 100644
--- a/front/src/Connexion/ConnexionModels.ts
+++ b/front/src/Connexion/ConnexionModels.ts
@@ -96,7 +96,9 @@ export interface WebRtcSignalSentMessageInterface {
export interface WebRtcSignalReceivedMessageInterface {
userId: number,
- signal: SignalData
+ signal: SignalData,
+ webRtcUser: string | undefined,
+ webRtcPassword: string | undefined
}
export interface StartMapInterface {
diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts
index bd330ad9..cebd7606 100644
--- a/front/src/Connexion/RoomConnection.ts
+++ b/front/src/Connexion/RoomConnection.ts
@@ -427,7 +427,9 @@ export class RoomConnection implements RoomConnection {
callback({
userId: message.getUserid(),
name: message.getName(),
- initiator: message.getInitiator()
+ initiator: message.getInitiator(),
+ webRtcUser: message.getWebrtcusername() ?? undefined,
+ webRtcPassword: message.getWebrtcpassword() ?? undefined,
});
});
}
@@ -436,7 +438,9 @@ export class RoomConnection implements RoomConnection {
this.onMessage(EventMessage.WEBRTC_SIGNAL, (message: WebRtcSignalToClientMessage) => {
callback({
userId: message.getUserid(),
- signal: JSON.parse(message.getSignal())
+ signal: JSON.parse(message.getSignal()),
+ webRtcUser: message.getWebrtcusername() ?? undefined,
+ webRtcPassword: message.getWebrtcpassword() ?? undefined,
});
});
}
@@ -445,7 +449,9 @@ export class RoomConnection implements RoomConnection {
this.onMessage(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, (message: WebRtcSignalToClientMessage) => {
callback({
userId: message.getUserid(),
- signal: JSON.parse(message.getSignal())
+ signal: JSON.parse(message.getSignal()),
+ webRtcUser: message.getWebrtcusername() ?? undefined,
+ webRtcPassword: message.getWebrtcpassword() ?? undefined,
});
});
}
@@ -464,7 +470,8 @@ export class RoomConnection implements RoomConnection {
});
}
- public getUserId(): number|null {
+ public getUserId(): number {
+ if (this.userId === null) throw 'UserId cannot be null!'
return this.userId;
}
@@ -583,7 +590,7 @@ export class RoomConnection implements RoomConnection {
public hasTag(tag: string): boolean {
return this.tags.includes(tag);
}
-
+
public isAdmin(): boolean {
return this.hasTag('admin');
}
diff --git a/front/src/Cypress/CypressAsserter.ts b/front/src/Cypress/CypressAsserter.ts
deleted file mode 100644
index 82eeab1f..00000000
--- a/front/src/Cypress/CypressAsserter.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-declare let window:WindowWithCypressAsserter;
-
-interface WindowWithCypressAsserter extends Window {
- cypressAsserter: CypressAsserter;
-}
-
-//this class is used to communicate with cypress, our e2e testing client
-//Since cypress cannot manipulate canvas, we notified it with console logs
-class CypressAsserter {
-
- constructor() {
- window.cypressAsserter = this
- }
-
- gameStarted() {
- console.log('Started the game')
- }
-
- preloadStarted() {
- console.log('Preloading')
- }
-
- preloadFinished() {
- console.log('Preloading done')
- }
-
- initStarted() {
- console.log('startInit')
- }
-
- initFinished() {
- console.log('startInit done')
- }
-}
-
-export const cypressAsserter = new CypressAsserter()
diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts
index a71506eb..844bf564 100644
--- a/front/src/Enum/EnvironmentVariable.ts
+++ b/front/src/Enum/EnvironmentVariable.ts
@@ -3,9 +3,10 @@ const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.wo
const API_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.API_URL || "pusher.workadventure.localhost");
const UPLOADER_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.UPLOADER_URL || 'uploader.workadventure.localhost');
const ADMIN_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.ADMIN_URL || "workadventure.localhost");
-const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca";
-const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com';
-const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$';
+const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302";
+const TURN_SERVER: string = process.env.TURN_SERVER || "";
+const TURN_USER: string = process.env.TURN_USER || '';
+const TURN_PASSWORD: string = process.env.TURN_PASSWORD || '';
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true";
const RESOLUTION = 2;
@@ -23,6 +24,7 @@ export {
ZOOM_LEVEL,
POSITION_DELAY,
MAX_EXTRAPOLATION_TIME,
+ STUN_SERVER,
TURN_SERVER,
TURN_USER,
TURN_PASSWORD,
diff --git a/front/src/Phaser/Components/Loader.ts b/front/src/Phaser/Components/Loader.ts
index ab9c0d95..1ee18b32 100644
--- a/front/src/Phaser/Components/Loader.ts
+++ b/front/src/Phaser/Components/Loader.ts
@@ -1,14 +1,54 @@
+import ImageFrameConfig = Phaser.Types.Loader.FileTypes.ImageFrameConfig;
-export const addLoader = (scene:Phaser.Scene): void => {
- const loadingText = scene.add.text(scene.game.renderer.width / 2, 200, 'Loading');
+const LogoNameIndex: string = 'logoLoading';
+const TextName: string = 'Loading...';
+const LogoResource: string = 'resources/logos/logo.png';
+const LogoFrame: ImageFrameConfig = {frameWidth: 307, frameHeight: 59};
+
+export const addLoader = (scene: Phaser.Scene): void => {
+ // If there is nothing to load, do not display the loader.
+ if (scene.load.list.entries.length === 0) {
+ return;
+ }
+ let loadingText: Phaser.GameObjects.Text|null = null;
+ const loadingBarWidth: number = Math.floor(scene.game.renderer.width / 3);
+ const loadingBarHeight: number = 16;
+ const padding: number = 5;
+
+ const promiseLoadLogoTexture = new Promise((res) => {
+ if(scene.load.textureManager.exists(LogoNameIndex)){
+ return res(scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex));
+ }else{
+ //add loading if logo image is not ready
+ loadingText = scene.add.text(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 50, TextName);
+ }
+ scene.load.spritesheet(LogoNameIndex, LogoResource, LogoFrame);
+ scene.load.once(`filecomplete-spritesheet-${LogoNameIndex}`, () => {
+ if(loadingText){
+ loadingText.destroy();
+ }
+ return res(scene.add.image(scene.game.renderer.width / 2, scene.game.renderer.height / 2 - 150, LogoNameIndex));
+ });
+ });
+
+ const progressContainer = scene.add.graphics();
const progress = scene.add.graphics();
+ progressContainer.fillStyle(0x444444, 0.8);
+ progressContainer.fillRect((scene.game.renderer.width - loadingBarWidth) / 2 - padding, scene.game.renderer.height / 2 + 50 - padding, loadingBarWidth + padding * 2, loadingBarHeight + padding * 2);
+
scene.load.on('progress', (value: number) => {
progress.clear();
- progress.fillStyle(0xffffff, 1);
- progress.fillRect(0, 270, 800 * value, 60);
+ progress.fillStyle(0xBBBBBB, 1);
+ progress.fillRect((scene.game.renderer.width - loadingBarWidth) / 2, scene.game.renderer.height / 2 + 50, loadingBarWidth * value, loadingBarHeight);
});
scene.load.on('complete', () => {
- loadingText.destroy();
+ if(loadingText){
+ loadingText.destroy();
+ }
+ promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => {
+ resLoadingImage.destroy();
+ });
progress.destroy();
+ progressContainer.destroy();
});
-}
\ No newline at end of file
+}
diff --git a/front/src/Phaser/Components/OpenChatIcon.ts b/front/src/Phaser/Components/OpenChatIcon.ts
index bf293bab..1e9429e8 100644
--- a/front/src/Phaser/Components/OpenChatIcon.ts
+++ b/front/src/Phaser/Components/OpenChatIcon.ts
@@ -3,14 +3,12 @@ import {discussionManager} from "../../WebRtc/DiscussionManager";
export const openChatIconName = 'openChatIcon';
export class OpenChatIcon extends Phaser.GameObjects.Image {
constructor(scene: Phaser.Scene, x: number, y: number) {
- super(scene, x, y, openChatIconName);
+ super(scene, x, y, openChatIconName, 3);
scene.add.existing(this);
this.setScrollFactor(0, 0);
this.setOrigin(0, 1);
- this.displayWidth = 30;
- this.displayHeight = 30;
this.setInteractive();
- this.setVisible(false)
+ this.setVisible(false);
this.setDepth(99999);
this.on("pointerup", () => discussionManager.showDiscussionPart());
diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts
index 7d10cc1a..d0542d6a 100644
--- a/front/src/Phaser/Entity/PlayerTextures.ts
+++ b/front/src/Phaser/Entity/PlayerTextures.ts
@@ -6,7 +6,8 @@ export interface BodyResourceDescriptionListInterface {
export interface BodyResourceDescriptionInterface {
name: string,
- img: string
+ img: string,
+ level?: number
}
export const PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {
diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
index 776d1f5c..78b66c10 100644
--- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
+++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
@@ -23,21 +23,26 @@ export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptio
});
return returnArray;
}
-export const loadCustomTexture = (load: LoaderPlugin, texture: CharacterTexture) => {
+
+export const loadCustomTexture = (loaderPlugin: LoaderPlugin, texture: CharacterTexture) : Promise => {
const name = 'customCharacterTexture'+texture.id;
- load.spritesheet(name,texture.url,{frameWidth: 32, frameHeight: 32});
- return name;
+ const playerResourceDescriptor: BodyResourceDescriptionInterface = {name, img: texture.url, level: texture.level}
+ return createLoadingPromise(loaderPlugin, playerResourceDescriptor);
}
-export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, texturePlugin: TextureManager, texturekeys:Array): Promise => {
- const promisesList:Promise[] = [];
+export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, texturekeys:Array): Promise => {
+ const promisesList:Promise[] = [];
texturekeys.forEach((textureKey: string|BodyResourceDescriptionInterface) => {
- const playerResourceDescriptor = getRessourceDescriptor(textureKey);
- if(!texturePlugin.exists(playerResourceDescriptor.name)) {
- console.log('Loading '+playerResourceDescriptor.name)
- promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor));
+ try {
+ //TODO refactor
+ const playerResourceDescriptor = getRessourceDescriptor(textureKey);
+ if (playerResourceDescriptor && !loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
+ promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor));
+ }
+ }catch (err){
+ console.error(err);
}
- })
+ });
let returnPromise:Promise>;
if (promisesList.length > 0) {
loadPlugin.start();
@@ -66,8 +71,14 @@ export const getRessourceDescriptor = (textureKey: string|BodyResourceDescriptio
}
const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) => {
- return new Promise((res, rej) => {
- loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, {frameWidth: 32, frameHeight: 32});
- loadPlugin.once('filecomplete-spritesheet-'+playerResourceDescriptor.name, () => res());
+ return new Promise((res) => {
+ if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
+ return res(playerResourceDescriptor);
+ }
+ loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, {
+ frameWidth: 32,
+ frameHeight: 32
+ });
+ loadPlugin.once('filecomplete-spritesheet-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor));
});
}
\ No newline at end of file
diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts
index 822e4e36..81ea00e1 100644
--- a/front/src/Phaser/Entity/RemotePlayer.ts
+++ b/front/src/Phaser/Entity/RemotePlayer.ts
@@ -1,7 +1,6 @@
import {GameScene} from "../Game/GameScene";
import {PointInterface} from "../../Connexion/ConnexionModels";
import {Character} from "../Entity/Character";
-import {Sprite} from "./Sprite";
/**
* Class representing the sprite of a remote player (a player that plays on another computer)
@@ -23,6 +22,11 @@ export class RemotePlayer extends Character {
//set data
this.userId = userId;
+
+ //todo: implement on click action
+ /*this.playerName.setInteractive();
+ this.playerName.on('pointerup', () => {
+ });*/
}
updatePosition(position: PointInterface): void {
diff --git a/front/src/Phaser/Entity/SpeechBubble.ts b/front/src/Phaser/Entity/SpeechBubble.ts
index 06a64bd4..231de875 100644
--- a/front/src/Phaser/Entity/SpeechBubble.ts
+++ b/front/src/Phaser/Entity/SpeechBubble.ts
@@ -14,7 +14,7 @@ export class SpeechBubble {
const bubbleWidth = bubblePadding * 2 + text.length * 10;
const arrowHeight = bubbleHeight / 4;
- this.bubble = scene.add.graphics({ x: player.x + 16, y: player.y - 80 });
+ this.bubble = scene.add.graphics({ x: 16, y: -80 });
player.add(this.bubble);
// Bubble shadow
diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts
index 9f3157a0..12da0514 100644
--- a/front/src/Phaser/Game/GameMap.ts
+++ b/front/src/Phaser/Game/GameMap.ts
@@ -49,6 +49,10 @@ export class GameMap {
this.lastProperties = newProps;
}
+ public getCurrentProperties(): Map {
+ return this.lastProperties;
+ }
+
private getProperties(key: number): Map {
const properties = new Map();
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 51126013..d6dc7dbb 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -30,10 +30,11 @@ import {RemotePlayer} from "../Entity/RemotePlayer";
import {Queue} from 'queue-typescript';
import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer";
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
-import {lazyLoadPlayerCharacterTextures} from "../Entity/PlayerTexturesLoadingManager";
+import {lazyLoadPlayerCharacterTextures, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
import {
CenterListener,
- EXIT_MESSAGE_PROPERTIES, JITSI_MESSAGE_PROPERTIES,
+ EXIT_MESSAGE_PROPERTIES,
+ JITSI_MESSAGE_PROPERTIES,
layoutManager,
LayoutMode,
ON_ACTION_TRIGGER_BUTTON,
@@ -72,6 +73,8 @@ import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCha
import {TextureError} from "../../Exception/TextureError";
import {addLoader} from "../Components/Loader";
import {ErrorSceneName} from "../Reconnecting/ErrorScene";
+import {localUserStore} from "../../Connexion/LocalUserStore";
+import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
export interface GameSceneInitInterface {
initPosition: PointInterface|null,
@@ -116,7 +119,7 @@ export class GameScene extends ResizableScene implements CenterListener {
MapPlayers!: Phaser.Physics.Arcade.Group;
MapPlayersByKey : Map = new Map();
Map!: Phaser.Tilemaps.Tilemap;
- Layers!: Array;
+ Layers!: Array;
Objects!: Array;
mapFile!: ITiledMap;
groups: Map;
@@ -157,7 +160,7 @@ export class GameScene extends ResizableScene implements CenterListener {
private actionableItems: Map = new Map();
// The item that can be selected by pressing the space key.
private outlinedItem: ActionableItem|null = null;
- private userInputManager!: UserInputManager;
+ public userInputManager!: UserInputManager;
private isReconnecting: boolean = false;
private startLayerName!: string | null;
private openChatIcon!: OpenChatIcon;
@@ -186,7 +189,13 @@ export class GameScene extends ResizableScene implements CenterListener {
//hook preload scene
preload(): void {
- addLoader(this);
+ const localUser = localUserStore.getLocalUser();
+ const textures = localUser?.textures;
+ if (textures) {
+ for (const texture of textures) {
+ loadCustomTexture(this.load, texture);
+ }
+ }
this.load.image(openChatIconName, 'resources/objects/talk.png');
this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => {
@@ -210,6 +219,8 @@ export class GameScene extends ResizableScene implements CenterListener {
this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32});
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
+
+ addLoader(this);
}
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
@@ -344,11 +355,11 @@ export class GameScene extends ResizableScene implements CenterListener {
this.physics.world.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
//add layer on map
- this.Layers = new Array();
+ this.Layers = new Array();
let depth = -2;
for (const layer of this.mapFile.layers) {
if (layer.type === 'tilelayer') {
- this.addLayer(this.Map.createLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
+ this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
const exitSceneUrl = this.getExitSceneUrl(layer);
if (exitSceneUrl !== undefined) {
@@ -529,6 +540,7 @@ export class GameScene extends ResizableScene implements CenterListener {
this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName);
this.GlobalMessageManager = new GlobalMessageManager(this.connection);
this.UserMessageManager = new UserMessageManager(this.connection);
+ this.UserMessageManager.setReceiveBanListener(this.bannedUser.bind(this));
const self = this;
this.simplePeer.registerPeerConnectionListener({
@@ -625,6 +637,15 @@ export class GameScene extends ResizableScene implements CenterListener {
}
}
+ private safeParseJSONstring(jsonString: string|undefined, propertyName: string) {
+ try {
+ return jsonString ? JSON.parse(jsonString) : {};
+ } catch(e) {
+ console.warn('Invalid JSON found in property "' + propertyName + '" of the map:' + jsonString, e);
+ return {}
+ }
+ }
+
private triggerOnMapLayerPropertyChange(){
/* @deprecated
this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => {
@@ -656,7 +677,7 @@ export class GameScene extends ResizableScene implements CenterListener {
coWebsiteManager.closeCoWebsite();
}else{
const openWebsiteFunction = () => {
- coWebsiteManager.loadCoWebsite(newValue as string);
+ coWebsiteManager.loadCoWebsite(newValue as string, allProps.get('openWebsitePolicy') as string | undefined);
layoutManager.removeActionButton('openWebsite', this.userInputManager);
};
@@ -680,11 +701,13 @@ export class GameScene extends ResizableScene implements CenterListener {
this.stopJitsi();
}else{
const openJitsiRoomFunction = () => {
+ const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance);
if (JITSI_PRIVATE_MODE) {
const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined;
- this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag);
+
+ this.connection.emitQueryJitsiJwtMessage(roomName, adminTag);
} else {
- this.startJitsi(newValue as string);
+ this.startJitsi(roomName, undefined);
}
layoutManager.removeActionButton('jitsiRoom', this.userInputManager);
}
@@ -725,6 +748,10 @@ export class GameScene extends ResizableScene implements CenterListener {
if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey);
urlManager.pushStartLayerNameToUrl(hash);
if (roomId !== this.scene.key) {
+ if (this.scene.get(roomId) === null) {
+ console.error("next room not loaded", exitKey);
+ return;
+ }
this.cleanupClosingScene();
this.scene.stop();
this.scene.remove(this.scene.key);
@@ -878,13 +905,13 @@ export class GameScene extends ResizableScene implements CenterListener {
this.cameras.main.setZoom(ZOOM_LEVEL);
}
- addLayer(Layer : Phaser.Tilemaps.TilemapLayer){
+ addLayer(Layer : Phaser.Tilemaps.StaticTilemapLayer){
this.Layers.push(Layer);
}
createCollisionWithPlayer() {
//add collision layer
- this.Layers.forEach((Layer: Phaser.Tilemaps.TilemapLayer) => {
+ this.Layers.forEach((Layer: Phaser.Tilemaps.StaticTilemapLayer) => {
this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => {
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
});
@@ -902,7 +929,7 @@ export class GameScene extends ResizableScene implements CenterListener {
createCurrentPlayer(){
//TODO create animation moving between exit and start
- const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, this.textures, this.characterLayers);
+ const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, this.characterLayers);
try {
this.CurrentPlayer = new Player(
this,
@@ -1096,7 +1123,7 @@ export class GameScene extends ResizableScene implements CenterListener {
return;
}
- const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, this.textures, addPlayerData.characterLayers);
+ const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, addPlayerData.characterLayers);
const player = new RemotePlayer(
addPlayerData.userId,
this,
@@ -1221,6 +1248,7 @@ export class GameScene extends ResizableScene implements CenterListener {
private reposition(): void {
this.presentationModeSprite.setY(this.game.renderer.height - 2);
this.chatModeSprite.setY(this.game.renderer.height - 2);
+ this.openChatIcon.setY(this.game.renderer.height - 2);
// Recompute camera offset if needed
this.updateCameraOffset();
@@ -1247,7 +1275,11 @@ export class GameScene extends ResizableScene implements CenterListener {
}
public startJitsi(roomName: string, jwt?: string): void {
- jitsiFactory.start(roomName, this.playerName, jwt);
+ const allProps = this.gameMap.getCurrentProperties();
+ const jitsiConfig = this.safeParseJSONstring(allProps.get("jitsiConfig") as string|undefined, 'jitsiConfig');
+ const jitsiInterfaceConfig = this.safeParseJSONstring(allProps.get("jitsiInterfaceConfig") as string|undefined, 'jitsiInterfaceConfig');
+
+ jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig);
this.connection.setSilent(true);
mediaManager.hideGameOverlay();
@@ -1265,5 +1297,14 @@ export class GameScene extends ResizableScene implements CenterListener {
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
}
+ private bannedUser(){
+ this.cleanupClosingScene();
+ this.userInputManager.clearAllKeys();
+ this.scene.start(ErrorSceneName, {
+ title: 'Banned',
+ subTitle: 'You was banned of WorkAdventure',
+ message: 'If you want more information, you can contact us: workadventure@thecodingmachine.com'
+ });
+ }
}
diff --git a/front/src/Phaser/Items/ActionableItem.ts b/front/src/Phaser/Items/ActionableItem.ts
index e32bad1a..fe4de385 100644
--- a/front/src/Phaser/Items/ActionableItem.ts
+++ b/front/src/Phaser/Items/ActionableItem.ts
@@ -43,8 +43,9 @@ export class ActionableItem {
}
this.isSelectable = true;
if (this.sprite.pipeline) {
- this.sprite.setPipeline(OutlinePipeline.KEY);
- this.sprite.pipeline.set2f('uTextureSize', this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);
+ // Commented out to try to fix MacOS issue
+ /*this.sprite.setPipeline(OutlinePipeline.KEY);
+ this.sprite.pipeline.set2f('uTextureSize', this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);*/
}
}
@@ -56,7 +57,8 @@ export class ActionableItem {
return;
}
this.isSelectable = false;
- this.sprite.resetPipeline();
+ // Commented out to try to fix MacOS issue
+ //this.sprite.resetPipeline();
}
/**
diff --git a/front/src/Phaser/Login/AbstractCharacterScene.ts b/front/src/Phaser/Login/AbstractCharacterScene.ts
new file mode 100644
index 00000000..dfc98539
--- /dev/null
+++ b/front/src/Phaser/Login/AbstractCharacterScene.ts
@@ -0,0 +1,41 @@
+import {ResizableScene} from "./ResizableScene";
+import {localUserStore} from "../../Connexion/LocalUserStore";
+import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
+import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
+import {CharacterTexture} from "../../Connexion/LocalUser";
+
+export abstract class AbstractCharacterScene extends ResizableScene {
+
+ loadCustomSceneSelectCharacters() : Promise {
+ const textures = this.getTextures();
+ const promises : Promise[] = [];
+ if (textures) {
+ for (const texture of textures) {
+ if (texture.level === -1) {
+ continue;
+ }
+ promises.push(loadCustomTexture(this.load, texture));
+ }
+ }
+ return Promise.all(promises)
+ }
+
+ loadSelectSceneCharacters() : Promise {
+ const textures = this.getTextures();
+ const promises: Promise[] = [];
+ if (textures) {
+ for (const texture of textures) {
+ if (texture.level !== -1) {
+ continue;
+ }
+ promises.push(loadCustomTexture(this.load, texture));
+ }
+ }
+ return Promise.all(promises)
+ }
+
+ private getTextures() : CharacterTexture[]|undefined{
+ const localUser = localUserStore.getLocalUser();
+ return localUser?.textures;
+ }
+}
\ No newline at end of file
diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts
index c6364ef6..4f5b2860 100644
--- a/front/src/Phaser/Login/CustomizeScene.ts
+++ b/front/src/Phaser/Login/CustomizeScene.ts
@@ -10,6 +10,7 @@ import {ResizableScene} from "./ResizableScene";
import {localUserStore} from "../../Connexion/LocalUserStore";
import {addLoader} from "../Components/Loader";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
+import {AbstractCharacterScene} from "./AbstractCharacterScene";
export const CustomizeSceneName = "CustomizeScene";
@@ -20,7 +21,7 @@ enum CustomizeTextures{
arrowUp = "arrow_up",
}
-export class CustomizeScene extends ResizableScene {
+export class CustomizeScene extends AbstractCharacterScene {
private textField!: TextField;
private enterField!: TextField;
@@ -48,29 +49,21 @@ export class CustomizeScene extends ResizableScene {
preload() {
addLoader(this);
+
+ this.layers = loadAllLayers(this.load);
+ this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
+ bodyResourceDescriptions.forEach((bodyResourceDescription) => {
+ if(!bodyResourceDescription.level){
+ throw 'Texture level is null';
+ }
+ this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
+ });
+ });
+
this.load.image(CustomizeTextures.arrowRight, "resources/objects/arrow_right.png");
this.load.image(CustomizeTextures.icon, "resources/logos/tcm_full.png");
this.load.image(CustomizeTextures.arrowUp, "resources/objects/arrow_up.png");
this.load.bitmapFont(CustomizeTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
-
- this.layers = loadAllLayers(this.load);
-
- const localUser = localUserStore.getLocalUser();
-
- const textures = localUser?.textures;
- if (textures) {
- for (const texture of textures) {
- if(texture.level === -1){
- continue;
- }
- loadCustomTexture(this.load, texture);
- const name = 'customCharacterTexture'+texture.id;
- this.layers[texture.level].unshift({
- name,
- img: texture.url
- });
- }
- }
}
create() {
diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts
index 8d7202a8..9ca6dcd2 100644
--- a/front/src/Phaser/Login/LoginScene.ts
+++ b/front/src/Phaser/Login/LoginScene.ts
@@ -2,7 +2,6 @@ import {gameManager} from "../Game/GameManager";
import {TextField} from "../Components/TextField";
import {TextInput} from "../Components/TextInput";
import Image = Phaser.GameObjects.Image;
-import {cypressAsserter} from "../../Cypress/CypressAsserter";
import {SelectCharacterSceneName} from "./SelectCharacterScene";
import {ResizableScene} from "./ResizableScene";
@@ -29,16 +28,13 @@ export class LoginScene extends ResizableScene {
}
preload() {
- cypressAsserter.preloadStarted();
//this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
- cypressAsserter.preloadFinished();
}
create() {
- cypressAsserter.initStarted();
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:');
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => {
@@ -59,8 +55,6 @@ export class LoginScene extends ResizableScene {
}
this.login(this.name);
});
-
- cypressAsserter.initFinished();
}
update(time: number, delta: number): void {
diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts
index 25af61c6..e47cf38a 100644
--- a/front/src/Phaser/Login/SelectCharacterScene.ts
+++ b/front/src/Phaser/Login/SelectCharacterScene.ts
@@ -9,6 +9,7 @@ import {localUserStore} from "../../Connexion/LocalUserStore";
import {loadAllDefaultModels, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
import {addLoader} from "../Components/Loader";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
+import {AbstractCharacterScene} from "./AbstractCharacterScene";
//todo: put this constants in a dedicated file
@@ -21,7 +22,7 @@ enum LoginTextures {
customizeButtonSelected = "customize_button_selected"
}
-export class SelectCharacterScene extends ResizableScene {
+export class SelectCharacterScene extends AbstractCharacterScene {
private readonly nbCharactersPerRow = 6;
private textField!: TextField;
private pressReturnField!: TextField;
@@ -44,6 +45,13 @@ export class SelectCharacterScene extends ResizableScene {
preload() {
addLoader(this);
+
+ this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => {
+ bodyResourceDescriptions.forEach((bodyResourceDescription) => {
+ this.playerModels.push(bodyResourceDescription);
+ });
+ })
+
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
@@ -52,17 +60,7 @@ export class SelectCharacterScene extends ResizableScene {
this.load.image(LoginTextures.customizeButton, 'resources/objects/customize.png');
this.load.image(LoginTextures.customizeButtonSelected, 'resources/objects/customize_selected.png');
- const localUser = localUserStore.getLocalUser();
- const textures = localUser?.textures;
- if (textures) {
- for (const texture of textures) {
- if(texture.level !== -1){
- continue;
- }
- const name = loadCustomTexture(this.load, texture);
- this.playerModels.push({name: name, img: texture.url});
- }
- }
+ addLoader(this);
}
create() {
@@ -127,7 +125,7 @@ export class SelectCharacterScene extends ResizableScene {
/*create user*/
this.createCurrentPlayer();
-
+
const playerNumber = localUserStore.getPlayerCharacterIndex();
if (playerNumber && playerNumber !== -1) {
this.selectedRectangleXPos = playerNumber % this.nbCharactersPerRow;
diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts
index 8bd64cd1..ab25c338 100644
--- a/front/src/Phaser/Menu/MenuScene.ts
+++ b/front/src/Phaser/Menu/MenuScene.ts
@@ -3,7 +3,9 @@ import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCha
import {gameManager} from "../Game/GameManager";
import {localUserStore} from "../../Connexion/LocalUserStore";
import {mediaManager} from "../../WebRtc/MediaManager";
-import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
+import {gameReportKey, gameReportRessource, ReportMenu} from "./ReportMenu";
+import {connectionManager} from "../../Connexion/ConnectionManager";
+import {GameConnexionTypes} from "../../Url/UrlManager";
export const MenuSceneName = 'MenuScene';
const gameMenuKey = 'gameMenu';
@@ -21,6 +23,7 @@ export class MenuScene extends Phaser.Scene {
private menuElement!: Phaser.GameObjects.DOMElement;
private gameQualityMenuElement!: Phaser.GameObjects.DOMElement;
private gameShareElement!: Phaser.GameObjects.DOMElement;
+ private gameReportElement!: ReportMenu;
private sideMenuOpened = false;
private settingsMenuOpened = false;
private gameShareOpened = false;
@@ -40,20 +43,21 @@ export class MenuScene extends Phaser.Scene {
this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html');
this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html');
this.load.html(gameShare, 'resources/html/gameShare.html');
+ this.load.html(gameReportKey, gameReportRessource);
}
create() {
this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey);
this.menuElement.setOrigin(0);
- this.revealMenusAfterInit(this.menuElement, 'gameMenu');
+ MenuScene.revealMenusAfterInit(this.menuElement, 'gameMenu');
const middleX = (window.innerWidth / 3) - 298;
this.gameQualityMenuElement = this.add.dom(middleX, -400).createFromCache(gameSettingsMenuKey);
- this.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality');
+ MenuScene.revealMenusAfterInit(this.gameQualityMenuElement, 'gameQuality');
this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare);
- this.revealMenusAfterInit(this.gameShareElement, gameShare);
+ MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare);
this.gameShareElement.addListener('click');
this.gameShareElement.on('click', (event:MouseEvent) => {
event.preventDefault();
@@ -64,6 +68,12 @@ export class MenuScene extends Phaser.Scene {
}
});
+ this.gameReportElement = new ReportMenu(this, connectionManager.getConnexionType === GameConnexionTypes.anonymous);
+ mediaManager.setShowReportModalCallBacks((userId, userName) => {
+ this.closeAll();
+ this.gameReportElement.open(parseInt(userId), userName);
+ });
+
this.input.keyboard.on('keyup-TAB', () => {
this.sideMenuOpened ? this.closeSideMenu() : this.openSideMenu();
});
@@ -77,7 +87,8 @@ export class MenuScene extends Phaser.Scene {
this.menuElement.on('click', this.onMenuClick.bind(this));
}
- private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
+ //todo put this method in a parent menuElement class
+ static revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
//Dom elements will appear inside the viewer screen when creating before being moved out of it, which create a flicker effect.
//To prevent this, we put a 'hidden' attribute on the root element, we remove it only after the init is done.
setTimeout(() => {
@@ -98,6 +109,11 @@ export class MenuScene extends Phaser.Scene {
const adminSection = this.menuElement.getChildByID('adminConsoleSection') as HTMLElement;
adminSection.hidden = false;
}
+ //TODO bind with future metadata of card
+ //if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){
+ const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement;
+ adminSection.hidden = false;
+ //}
this.tweens.add({
targets: this.menuElement,
x: openedSideMenuX,
@@ -222,6 +238,9 @@ export class MenuScene extends Phaser.Scene {
}
private onMenuClick(event:MouseEvent) {
+ if((event?.target as HTMLInputElement).classList.contains('not-button')){
+ return;
+ }
event.preventDefault();
switch ((event?.target as HTMLInputElement).id) {
@@ -280,5 +299,6 @@ export class MenuScene extends Phaser.Scene {
private closeAll(){
this.closeGameQualityMenu();
this.closeGameShare();
+ this.gameReportElement.close();
}
}
diff --git a/front/src/Phaser/Menu/ReportMenu.ts b/front/src/Phaser/Menu/ReportMenu.ts
new file mode 100644
index 00000000..bee86c35
--- /dev/null
+++ b/front/src/Phaser/Menu/ReportMenu.ts
@@ -0,0 +1,119 @@
+import {MenuScene} from "./MenuScene";
+import {gameManager} from "../Game/GameManager";
+import {blackListManager} from "../../WebRtc/BlackListManager";
+
+export const gameReportKey = 'gameReport';
+export const gameReportRessource = 'resources/html/gameReport.html';
+
+export class ReportMenu extends Phaser.GameObjects.DOMElement {
+ private opened: boolean = false;
+
+ private userId!: number;
+ private userName!: string|undefined;
+ private anonymous: boolean;
+
+ constructor(scene: Phaser.Scene, anonymous: boolean) {
+ super(scene, -2000, -2000);
+ this.anonymous = anonymous;
+ this.createFromCache(gameReportKey);
+
+ if (this.anonymous) {
+ const divToHide = this.getChildByID('reportSection') as HTMLElement;
+ divToHide.hidden = true;
+ const textToHide = this.getChildByID('askActionP') as HTMLElement;
+ textToHide.hidden = true;
+ }
+
+ scene.add.existing(this);
+ MenuScene.revealMenusAfterInit(this, gameReportKey);
+
+ this.addListener('click');
+ this.on('click', (event:MouseEvent) => {
+ event.preventDefault();
+ if ((event?.target as HTMLInputElement).id === 'gameReportFormSubmit') {
+ this.submitReport();
+ } else if((event?.target as HTMLInputElement).id === 'gameReportFormCancel') {
+ this.close();
+ } else if((event?.target as HTMLInputElement).id === 'toggleBlockButton') {
+ this.toggleBlock();
+ }
+ });
+ }
+
+ public open(userId: number, userName: string|undefined): void {
+ if (this.opened) {
+ this.close();
+ return;
+ }
+
+ this.userId = userId;
+ this.userName = userName;
+
+ const mainEl = this.getChildByID('gameReport') as HTMLElement;
+ this.x = this.getCenteredX(mainEl);
+ this.y = this.getHiddenY(mainEl);
+
+ const gameTitleReport = this.getChildByID('nameReported') as HTMLElement;
+ gameTitleReport.innerText = userName || '';
+
+ const blockButton = this.getChildByID('toggleBlockButton') as HTMLElement;
+ blockButton.innerText = blackListManager.isBlackListed(this.userId) ? 'Unblock this user' : 'Block this user';
+
+ this.opened = true;
+
+ gameManager.getCurrentGameScene(this.scene).userInputManager.clearAllKeys();
+
+ this.scene.tweens.add({
+ targets: this,
+ y: this.getCenteredY(mainEl),
+ duration: 1000,
+ ease: 'Power3'
+ });
+ }
+
+ public close(): void {
+ this.opened = false;
+ gameManager.getCurrentGameScene(this.scene).userInputManager.initKeyBoardEvent();
+ const mainEl = this.getChildByID('gameReport') as HTMLElement;
+ this.scene.tweens.add({
+ targets: this,
+ y: this.getHiddenY(mainEl),
+ duration: 1000,
+ ease: 'Power3'
+ });
+ }
+
+ //todo: into a parent class?
+ private getCenteredX(mainEl: HTMLElement): number {
+ return window.innerWidth / 4 - mainEl.clientWidth / 2;
+ }
+ private getHiddenY(mainEl: HTMLElement): number {
+ return - mainEl.clientHeight - 50;
+ }
+ private getCenteredY(mainEl: HTMLElement): number {
+ return window.innerHeight / 4 - mainEl.clientHeight / 2;
+ }
+
+ private toggleBlock(): void {
+ !blackListManager.isBlackListed(this.userId) ? blackListManager.blackList(this.userId) : blackListManager.cancelBlackList(this.userId);
+ this.close();
+ }
+
+ private submitReport(): void{
+ const gamePError = this.getChildByID('gameReportErr') as HTMLParagraphElement;
+ gamePError.innerText = '';
+ gamePError.style.display = 'none';
+ const gameTextArea = this.getChildByID('gameReportInput') as HTMLInputElement;
+ const gameIdUserReported = this.getChildByID('idUserReported') as HTMLInputElement;
+ if(!gameTextArea || !gameTextArea.value || !gameIdUserReported || !gameIdUserReported.value){
+ gamePError.innerText = 'Report message cannot to be empty.';
+ gamePError.style.display = 'block';
+ return;
+ }
+ gameManager.getCurrentGameScene(this.scene).connection.emitReportPlayerMessage(
+ parseInt(gameIdUserReported.value),
+ gameTextArea.value
+ );
+ this.close();
+ }
+}
\ No newline at end of file
diff --git a/front/src/Phaser/Shaders/OutlinePipeline.ts b/front/src/Phaser/Shaders/OutlinePipeline.ts
index 0d074bc3..9c123c48 100644
--- a/front/src/Phaser/Shaders/OutlinePipeline.ts
+++ b/front/src/Phaser/Shaders/OutlinePipeline.ts
@@ -1,4 +1,4 @@
-export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline {
+export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline {
// the unique id of this pipeline
public static readonly KEY = 'Outline';
diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts
index c8d91609..dd46a343 100644
--- a/front/src/Phaser/UserInput/UserInputManager.ts
+++ b/front/src/Phaser/UserInput/UserInputManager.ts
@@ -59,7 +59,11 @@ export class UserInputManager {
];
}
- clearAllInputKeyboard(){
+ clearAllListeners(){
+ this.Scene.input.keyboard.removeAllListeners();
+ }
+
+ clearAllKeys(){
this.Scene.input.keyboard.removeAllKeys();
}
diff --git a/front/src/WebRtc/BlackListManager.ts b/front/src/WebRtc/BlackListManager.ts
new file mode 100644
index 00000000..65efef3a
--- /dev/null
+++ b/front/src/WebRtc/BlackListManager.ts
@@ -0,0 +1,24 @@
+import {Subject} from 'rxjs';
+
+class BlackListManager {
+ private list: number[] = [];
+ public onBlockStream: Subject = new Subject();
+ public onUnBlockStream: Subject = new Subject();
+
+ isBlackListed(userId: number): boolean {
+ return this.list.find((data) => data === userId) !== undefined;
+ }
+
+ blackList(userId: number): void {
+ if (this.isBlackListed(userId)) return;
+ this.list.push(userId);
+ this.onBlockStream.next(userId);
+ }
+
+ cancelBlackList(userId: number): void {
+ this.list.splice(this.list.findIndex(data => data === userId), 1);
+ this.onUnBlockStream.next(userId);
+ }
+}
+
+export const blackListManager = new BlackListManager();
\ No newline at end of file
diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts
index e9564222..ef73ac1d 100644
--- a/front/src/WebRtc/CoWebsiteManager.ts
+++ b/front/src/WebRtc/CoWebsiteManager.ts
@@ -42,7 +42,7 @@ class CoWebsiteManager {
this.opened = iframeStates.opened;
}
- public loadCoWebsite(url: string): void {
+ public loadCoWebsite(url: string, allowPolicy?: string): void {
this.load();
this.cowebsiteDiv.innerHTML = `