Compare commits

..

No commits in common. "ca15c9f1e4606c151374fcc58fa8cc2f8e5c2366" and "aeb649b79bd9cb184f31a084c6a1f2fa798788e7" have entirely different histories.

321 changed files with 3843 additions and 18513 deletions

View File

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

164
.github/workflows/build-and-deploy.yml vendored Normal file
View File

@ -0,0 +1,164 @@
name: Build, push and deploy Docker image
on:
- push
# Enables BuildKit
env:
DOCKER_BUILDKIT: 1
jobs:
build-front:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Create a slugified value of the branch
- uses: rlespinasse/github-slug-action@3.1.0
- name: "Build and push front image"
uses: docker/build-push-action@v1
with:
dockerfile: front/Dockerfile
path: ./
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-front
tags: ${{ env.GITHUB_REF_SLUG }}
add_git_labels: true
build-back:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Create a slugified value of the branch
- uses: rlespinasse/github-slug-action@3.1.0
- name: "Build and push back image"
uses: docker/build-push-action@v1
with:
dockerfile: back/Dockerfile
path: ./
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-back
tags: ${{ env.GITHUB_REF_SLUG }}
add_git_labels: true
build-pusher:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Create a slugified value of the branch
- uses: rlespinasse/github-slug-action@3.1.0
- name: "Build and push back image"
uses: docker/build-push-action@v1
with:
dockerfile: pusher/Dockerfile
path: ./
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-pusher
tags: ${{ env.GITHUB_REF_SLUG }}
add_git_labels: true
build-uploader:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Create a slugified value of the branch
- uses: rlespinasse/github-slug-action@3.1.0
- name: "Build and push back image"
uses: docker/build-push-action@v1
with:
dockerfile: uploader/Dockerfile
path: ./
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-uploader
tags: ${{ env.GITHUB_REF_SLUG }}
add_git_labels: true
build-maps:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Create a slugified value of the branch
- uses: rlespinasse/github-slug-action@3.1.0
- name: "Build and push front image"
uses: docker/build-push-action@v1
with:
dockerfile: maps/Dockerfile
path: maps/
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: thecodingmachine/workadventure-maps
tags: ${{ env.GITHUB_REF_SLUG }}
add_git_labels: true
deeploy:
needs:
- build-front
- build-back
- build-pusher
- build-maps
- build-uploader
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Create a slugified value of the branch
- uses: rlespinasse/github-slug-action@3.1.0
- name: Deploy
uses: thecodingmachine/deeployer-action@master
env:
KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }}
ADMIN_API_TOKEN: ${{ secrets.ADMIN_API_TOKEN }}
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 }}
- name: Add a comment in PR
uses: unsplash/comment-on-pr@v1.2.0
if: ${{ env.GITHUB_REF_SLUG != 'master' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
msg: Environment deployed at https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
check_for_duplicate_msg: true

26
.github/workflows/cleanup.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Cleanup images and environments
on:
- delete
# Enables BuildKit
env:
DOCKER_BUILDKIT: 1
jobs:
delete_namespace:
runs-on: ubuntu-latest
steps:
# Create a slugified value of the branch
- uses: rlespinasse/github-slug-action@1.1.0
- name: Cleanup
uses: thecodingmachine/deeployer-cleanup-action@master
env:
KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }}
with:
# FIXME: we are not using ${{ env.GITHUB_REF_SLUG }} that resolves to master BUT! we are not using a slugified namespace
# so complex namespace names will not be treated correctly
namespace: workadventure-${{ github.event.ref }}

View File

@ -3,8 +3,8 @@
name: "Continuous Integration"
on:
push:
pull_request:
- "pull_request"
- "push"
jobs:
@ -46,7 +46,7 @@ jobs:
- name: "Build"
run: yarn run build
env:
PUSHER_URL: "//localhost:8080"
API_URL: "localhost:8080"
working-directory: "front"
- name: "Lint"

3
.gitignore vendored
View File

@ -6,5 +6,4 @@ docker-compose.override.yaml
*.DS_Store
maps/yarn.lock
maps/dist/computer.js
maps/dist/computer.js.map
contrib/docker/acme
maps/dist/computer.js.map

View File

@ -1,36 +0,0 @@
## Version 1.3.9 - in dev
### BREAKING CHANGES
- Scripting API:
- Changed function names: `restorePlayerControl` => `restorePlayerControls`, `disablePlayerControl` => `disablePlayerControls`.
Please keep in mind that the scripting API is still experimental. Some breaking changes can occur in it until we mark it as stable.
### Updates
- Added the emote feature to WorkAdventure. (@Kharhamel, @Tabascoeye)
- The emote menu can be opened by clicking on your character.
- Clicking on one of its element will close the menu and play an emote above your character.
- This emote can be seen by other players.
- Mobile support has been improved
- WorkAdventure automatically sets the zoom level based on the viewport size to ensure a sensible size of the map is visible, whatever the viewport used
- Mouse wheel support to zoom in / out
- Pinch support on mobile to zoom in / out
- Improved virtual joystick size (adapts to the zoom level)
- New scripting API features:
- Use `WA.loadSound(): Sound` to load / play / stop a sound
### Bug Fixes
- Pinch gesture does no longer move the character
## Version 1.3.0
### New Features
* Maps can now contain "group" layers (layers that contain other layers) - #899 #779 (@Lurkars @moufmouf)
### Updates
### Bug Fixes

View File

@ -20,7 +20,7 @@ Install Docker.
Run:
```
docker-compose up -d
docker-compose up
```
The environment will start.

View File

@ -11,7 +11,6 @@ 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 const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4');
export {
MINIMUM_DISTANCE,

View File

@ -1,3 +1,9 @@
import { Group } from "./Group";
import { PointInterface } from "./Websocket/PointInterface";
import {Zone} from "_Model/Zone";
import {Movable} from "_Model/Movable";
import {PositionNotifier} from "_Model/PositionNotifier";
import {ServerDuplexStream} from "grpc";
import {
BatchMessage,
PusherToBackMessage,
@ -5,6 +11,7 @@ import {
ServerToClientMessage,
SubMessage, UserJoinedRoomMessage, UserLeftRoomMessage
} from "../Messages/generated/messages_pb";
import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
import {AdminSocket} from "../RoomManager";

View File

@ -2,12 +2,12 @@ import {PointInterface} from "./Websocket/PointInterface";
import {Group} from "./Group";
import {User, UserSocket} from "./User";
import {PositionInterface} from "_Model/PositionInterface";
import {EmoteCallback, EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone";
import {EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone";
import {PositionNotifier} from "./PositionNotifier";
import {Movable} from "_Model/Movable";
import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier";
import {arrayIntersect} from "../Services/ArrayHelper";
import {EmoteEventMessage, JoinRoomMessage} from "../Messages/generated/messages_pb";
import {JoinRoomMessage} from "../Messages/generated/messages_pb";
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
import {ZoneSocket} from "src/RoomManager";
import {Admin} from "../Model/Admin";
@ -38,10 +38,12 @@ export class GameRoom {
private readonly positionNotifier: PositionNotifier;
public readonly roomId: string;
public readonly anonymous: boolean;
public tags: string[];
public policyType: GameRoomPolicyTypes;
public readonly roomSlug: string;
public readonly worldSlug: string = '';
public readonly organizationSlug: string = '';
private versionNumber:number = 1;
private nextUserId: number = 1;
constructor(roomId: string,
@ -51,12 +53,14 @@ export class GameRoom {
groupRadius: number,
onEnters: EntersCallback,
onMoves: MovesCallback,
onLeaves: LeavesCallback,
onEmote: EmoteCallback,
) {
onLeaves: LeavesCallback)
{
this.roomId = roomId;
this.anonymous = isRoomAnonymous(roomId);
this.tags = [];
this.policyType = GameRoomPolicyTypes.ANONYMOUS_POLICY;
if (isRoomAnonymous(roomId)) {
if (this.anonymous) {
this.roomSlug = extractRoomSlugPublicRoomId(this.roomId);
} else {
const {organizationSlug, worldSlug, roomSlug} = extractDataFromPrivateRoomId(this.roomId);
@ -75,7 +79,7 @@ export class GameRoom {
this.minDistance = minDistance;
this.groupRadius = groupRadius;
// A zone is 10 sprites wide.
this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves, onEmote);
this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves);
}
public getGroups(): Group[] {
@ -106,8 +110,7 @@ export class GameRoom {
socket,
joinRoomMessage.getTagList(),
joinRoomMessage.getName(),
ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()),
joinRoomMessage.getCompanion()
ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList())
);
this.nextUserId++;
this.users.set(user.id, user);
@ -301,6 +304,10 @@ export class GameRoom {
return this.itemsState;
}
public canAccess(userTags: string[]): boolean {
return arrayIntersect(userTags, this.tags);
}
public addZoneListener(call: ZoneSocket, x: number, y: number): Set<Movable> {
return this.positionNotifier.addZoneListener(call, x, y);
}
@ -321,13 +328,4 @@ export class GameRoom {
public adminLeave(admin: Admin): void {
this.admins.delete(admin);
}
public incrementVersion(): number {
this.versionNumber++
return this.versionNumber;
}
public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) {
this.positionNotifier.emitEmoteEvent(user, emoteEventMessage);
}
}

View File

@ -4,9 +4,9 @@ import {PositionInterface} from "_Model/PositionInterface";
import {Movable} from "_Model/Movable";
import {PositionNotifier} from "_Model/PositionNotifier";
import {gaugeManager} from "../Services/GaugeManager";
import {MAX_PER_GROUP} from "../Enum/EnvironmentVariable";
export class Group implements Movable {
static readonly MAX_PER_GROUP = 4;
private static nextId: number = 1;
@ -88,7 +88,7 @@ export class Group implements Movable {
}
isFull(): boolean {
return this.users.size >= MAX_PER_GROUP;
return this.users.size >= Group.MAX_PER_GROUP;
}
isEmpty(): boolean {

View File

@ -8,12 +8,10 @@
* The PositionNotifier is important for performance. It allows us to send the position of players only to a restricted
* number of players around the current player.
*/
import {EmoteCallback, EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone";
import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone";
import {Movable} from "_Model/Movable";
import {PositionInterface} from "_Model/PositionInterface";
import {ZoneSocket} from "../RoomManager";
import {User} from "_Model/User";
import {EmoteEventMessage} from "../Messages/generated/messages_pb";
interface ZoneDescriptor {
i: number;
@ -26,7 +24,7 @@ export class PositionNotifier {
private zones: Zone[][] = [];
constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback, private onEmote: EmoteCallback) {
constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback) {
}
private getZoneDescriptorFromCoordinates(x: number, y: number): ZoneDescriptor {
@ -79,7 +77,7 @@ export class PositionNotifier {
let zone = this.zones[j][i];
if (zone === undefined) {
zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, this.onEmote, i, j);
zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, i, j);
this.zones[j][i] = zone;
}
return zone;
@ -95,11 +93,4 @@ export class PositionNotifier {
const zone = this.getZone(x, y);
zone.removeListener(call);
}
public emitEmoteEvent(user: User, emoteEventMessage: EmoteEventMessage) {
const zoneDesc = this.getZoneDescriptorFromCoordinates(user.getPosition().x, user.getPosition().y);
const zone = this.getZone(zoneDesc.i, zoneDesc.j);
zone.emitEmoteEvent(emoteEventMessage);
}
}

View File

@ -4,7 +4,7 @@ import {Zone} from "_Model/Zone";
import {Movable} from "_Model/Movable";
import {PositionNotifier} from "_Model/PositionNotifier";
import {ServerDuplexStream} from "grpc";
import {BatchMessage, CompanionMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb";
import {BatchMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb";
import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
export type UserSocket = ServerDuplexStream<PusherToBackMessage, ServerToClientMessage>;
@ -23,8 +23,7 @@ export class User implements Movable {
public readonly socket: UserSocket,
public readonly tags: string[],
public readonly name: string,
public readonly characterLayers: CharacterLayer[],
public readonly companion?: CompanionMessage
public readonly characterLayers: CharacterLayer[]
) {
this.listenedZones = new Set<Zone>();

View File

@ -3,19 +3,21 @@ import {PositionInterface} from "_Model/PositionInterface";
import {Movable} from "./Movable";
import {Group} from "./Group";
import {ZoneSocket} from "../RoomManager";
import {EmoteEventMessage} from "../Messages/generated/messages_pb";
export type EntersCallback = (thing: Movable, fromZone: Zone|null, listener: ZoneSocket) => void;
export type MovesCallback = (thing: Movable, position: PositionInterface, listener: ZoneSocket) => void;
export type LeavesCallback = (thing: Movable, newZone: Zone|null, listener: ZoneSocket) => void;
export type EmoteCallback = (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => void;
export class Zone {
private things: Set<Movable> = new Set<Movable>();
private listeners: Set<ZoneSocket> = new Set<ZoneSocket>();
constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, private onEmote: EmoteCallback, public readonly x: number, public readonly y: number) { }
/**
* @param x For debugging purpose only
* @param y For debugging purpose only
*/
constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback, public readonly x: number, public readonly y: number) {
}
/**
* A user/thing leaves the zone
@ -39,7 +41,9 @@ export class Zone {
*/
private notifyLeft(thing: Movable, newZone: Zone|null) {
for (const listener of this.listeners) {
this.onLeaves(thing, newZone, listener);
//if (listener !== thing && (newZone === null || !listener.listenedZones.has(newZone))) {
this.onLeaves(thing, newZone, listener);
//}
}
}
@ -53,6 +57,15 @@ export class Zone {
*/
private notifyEnter(thing: Movable, oldZone: Zone|null, position: PositionInterface) {
for (const listener of this.listeners) {
/*if (listener === thing) {
continue;
}
if (oldZone === null || !listener.listenedZones.has(oldZone)) {
this.onEnters(thing, listener);
} else {
this.onMoves(thing, position, listener);
}*/
this.onEnters(thing, oldZone, listener);
}
}
@ -72,6 +85,28 @@ export class Zone {
}
}
/*public startListening(listener: User): void {
for (const thing of this.things) {
if (thing !== listener) {
this.onEnters(thing, listener);
}
}
this.listeners.add(listener);
listener.listenedZones.add(this);
}
public stopListening(listener: User): void {
for (const thing of this.things) {
if (thing !== listener) {
this.onLeaves(thing, listener);
}
}
this.listeners.delete(listener);
listener.listenedZones.delete(this);
}*/
public getThings(): Set<Movable> {
return this.things;
}
@ -84,11 +119,4 @@ export class Zone {
public removeListener(socket: ZoneSocket): void {
this.listeners.delete(socket);
}
public emitEmoteEvent(emoteEventMessage: EmoteEventMessage) {
for (const listener of this.listeners) {
this.onEmote(emoteEventMessage, listener);
}
}
}

View File

@ -5,13 +5,12 @@ import {
AdminPusherToBackMessage,
AdminRoomMessage,
BanMessage,
EmotePromptMessage,
EmptyMessage,
ItemEventMessage,
JoinRoomMessage,
PlayGlobalMessage,
PusherToBackMessage,
QueryJitsiJwtMessage, RefreshRoomPromptMessage,
QueryJitsiJwtMessage,
ServerToAdminClientMessage,
ServerToClientMessage,
SilentMessage,
@ -72,8 +71,6 @@ const roomManager: IRoomManagerServer = {
socketManager.emitPlayGlobalMessage(room, message.getPlayglobalmessage() as PlayGlobalMessage);
} else if (message.hasQueryjitsijwtmessage()){
socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
} else if (message.hasEmotepromptmessage()){
socketManager.handleEmoteEventMessage(room, user, message.getEmotepromptmessage() as EmotePromptMessage);
}else if (message.hasSendusermessage()) {
const sendUserMessage = message.getSendusermessage();
if(sendUserMessage !== undefined) {
@ -196,10 +193,6 @@ const roomManager: IRoomManagerServer = {
socketManager.dispatchWorlFullWarning(call.request.getRoomid());
callback(null, new EmptyMessage());
},
sendRefreshRoomPrompt(call: ServerUnaryCall<RefreshRoomPromptMessage>, callback: sendUnaryData<EmptyMessage>): void {
socketManager.dispatchRoomRefresh(call.request.getRoomid());
callback(null, new EmptyMessage());
},
};
export {roomManager};

View File

@ -0,0 +1,49 @@
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
import Axios from "axios";
export interface AdminApiData {
organizationSlug: string
worldSlug: string
roomSlug: string
mapUrlStart: string
tags: string[]
policy_type: number
userUuid: string
messages?: unknown[],
textures: CharacterTexture[]
}
export interface CharacterTexture {
id: number,
level: number,
url: string,
rights: string
}
class AdminApi {
async fetchMapDetails(organizationSlug: string, worldSlug: string, roomSlug: string|undefined): Promise<AdminApiData> {
if (!ADMIN_API_URL) {
return Promise.reject('No admin backoffice set!');
}
const params: { organizationSlug: string, worldSlug: string, roomSlug?: string } = {
organizationSlug,
worldSlug
};
if (roomSlug) {
params.roomSlug = roomSlug;
}
const res = await Axios.get(ADMIN_API_URL + '/api/map',
{
headers: {"Authorization": `${ADMIN_API_TOKEN}`},
params
}
)
return res.data;
}
}
export const adminApi = new AdminApi();

View File

@ -26,8 +26,7 @@ import {
GroupLeftZoneMessage,
WorldFullWarningMessage,
UserLeftZoneMessage,
EmoteEventMessage,
BanUserMessage, RefreshRoomMessage, EmotePromptMessage,
BanUserMessage,
} from "../Messages/generated/messages_pb";
import {User, UserSocket} from "../Model/User";
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
@ -42,6 +41,7 @@ import {
} from "../Enum/EnvironmentVariable";
import {Movable} from "../Model/Movable";
import {PositionInterface} from "../Model/PositionInterface";
import {adminApi, CharacterTexture} from "./AdminApi";
import Jwt from "jsonwebtoken";
import {JITSI_URL} from "../Enum/EnvironmentVariable";
import {clientEventsEmitter} from "./ClientEventsEmitter";
@ -68,7 +68,6 @@ export class SocketManager {
private rooms: Map<string, GameRoom> = new Map<string, GameRoom>();
constructor() {
clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
gaugeManager.incNbClientPerRoomGauge(roomId);
});
@ -130,7 +129,15 @@ export class SocketManager {
if (viewport === undefined) {
throw new Error('Viewport not found in message');
}
// sending to all clients in room except sender
/*client.position = {
x: position.x,
y: position.y,
direction,
moving: position.moving,
};
client.viewport = viewport;*/
// update position in the world
room.updatePosition(user, ProtobufUtils.toPointInterface(position));
@ -185,6 +192,21 @@ export class SocketManager {
}
}
// TODO: handle this message in pusher
/*async handleReportMessage(client: ExSocketInterface, reportPlayerMessage: ReportPlayerMessage) {
try {
const reportedSocket = this.sockets.get(reportPlayerMessage.getReporteduserid());
if (!reportedSocket) {
throw 'reported socket user not found';
}
//TODO report user on admin application
await adminApi.reportPlayer(reportedSocket.userUuid, reportPlayerMessage.getReportcomment(), client.userUuid)
} catch (e) {
console.error('An error occurred on "handleReportMessage"');
console.error(e);
}
}*/
emitVideo(room: GameRoom, user: User, data: WebRtcSignalToServerMessage): void {
//send only at user
const remoteUser = room.getUsers().get(data.getReceiverid());
@ -265,9 +287,13 @@ export class SocketManager {
GROUP_RADIUS,
(thing: Movable, fromZone: Zone|null, listener: ZoneSocket) => this.onZoneEnter(thing, fromZone, listener),
(thing: Movable, position:PositionInterface, listener: ZoneSocket) => this.onClientMove(thing, position, listener),
(thing: Movable, newZone: Zone|null, listener: ZoneSocket) => this.onClientLeave(thing, newZone, listener),
(emoteEventMessage:EmoteEventMessage, listener: ZoneSocket) => this.onEmote(emoteEventMessage, listener),
(thing: Movable, newZone: Zone|null, listener: ZoneSocket) => this.onClientLeave(thing, newZone, listener)
);
if (!world.anonymous) {
const data = await adminApi.fetchMapDetails(world.organizationSlug, world.worldSlug, world.roomSlug)
world.tags = data.tags
world.policyType = Number(data.policy_type)
}
gaugeManager.incNbRoomGauge();
this.rooms.set(roomId, world);
}
@ -299,7 +325,6 @@ export class SocketManager {
userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone));
userJoinedZoneMessage.setCompanion(thing.companion);
const subMessage = new SubToPusherMessage();
subMessage.setUserjoinedzonemessage(userJoinedZoneMessage);
@ -342,14 +367,6 @@ export class SocketManager {
}
}
private onEmote(emoteEventMessage: EmoteEventMessage, client: ZoneSocket) {
const subMessage = new SubToPusherMessage();
subMessage.setEmoteeventmessage(emoteEventMessage);
emitZoneMessage(subMessage, client);
}
private emitCreateUpdateGroupEvent(client: ZoneSocket, fromZone: Zone|null, group: Group): void {
const position = group.getPosition();
const pointMessage = new PointMessage();
@ -521,6 +538,19 @@ export class SocketManager {
return this.rooms;
}
/**
*
* @param token
*/
/*searchClientByUuid(uuid: string): ExSocketInterface | null {
for(const socket of this.sockets.values()){
if(socket.userUuid === uuid){
return socket;
}
}
return null;
}*/
public handleQueryJitsiJwtMessage(user: User, queryJitsiJwtMessage: QueryJitsiJwtMessage) {
const room = queryJitsiJwtMessage.getJitsiroom();
@ -604,7 +634,6 @@ export class SocketManager {
userJoinedMessage.setName(thing.name);
userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
userJoinedMessage.setCompanion(thing.companion);
const subMessage = new SubToPusherMessage();
subMessage.setUserjoinedzonemessage(userJoinedMessage);
@ -743,32 +772,6 @@ export class SocketManager {
recipient.socket.write(clientMessage);
});
}
dispatchRoomRefresh(roomId: string,): void {
const room = this.rooms.get(roomId);
if (!room) {
return;
}
const versionNumber = room.incrementVersion();
room.getUsers().forEach((recipient) => {
const worldFullMessage = new RefreshRoomMessage();
worldFullMessage.setRoomid(roomId)
worldFullMessage.setVersionnumber(versionNumber)
const clientMessage = new ServerToClientMessage();
clientMessage.setRefreshroommessage(worldFullMessage);
recipient.socket.write(clientMessage);
});
}
handleEmoteEventMessage(room: GameRoom, user: User, emotePromptMessage: EmotePromptMessage) {
const emoteEventMessage = new EmoteEventMessage();
emoteEventMessage.setEmote(emotePromptMessage.getEmote());
emoteEventMessage.setActoruserid(user.id);
room.emitEmoteEvent(user, emoteEventMessage);
}
}
export const socketManager = new SocketManager();

View File

@ -5,7 +5,6 @@ import {Group} from "../src/Model/Group";
import {User, UserSocket} from "_Model/User";
import {JoinRoomMessage, PositionMessage} from "../src/Messages/generated/messages_pb";
import Direction = PositionMessage.Direction;
import {EmoteCallback} from "_Model/Zone";
function createMockUser(userId: number): User {
return {
@ -34,8 +33,6 @@ function createJoinRoomMessage(uuid: string, x: number, y: number): JoinRoomMess
return joinRoomMessage;
}
const emote: EmoteCallback = (emoteEventMessage, listener): void => {}
describe("GameRoom", () => {
it("should connect user1 and user2", () => {
let connectCalledNumber: number = 0;
@ -46,8 +43,7 @@ describe("GameRoom", () => {
}
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote);
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {});
@ -76,7 +72,7 @@ describe("GameRoom", () => {
}
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote);
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {});
const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100));
@ -105,7 +101,7 @@ describe("GameRoom", () => {
disconnectCallNumber++;
}
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {}, emote);
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {});
const user1 = world.join(createMockUserSocket(), createJoinRoomMessage('1', 100, 100));

View File

@ -23,7 +23,7 @@ describe("PositionNotifier", () => {
moveTriggered = true;
}, (thing: Movable) => {
leaveTriggered = true;
}, () => {});
});
const user1 = new User(1, 'test', '10.0.0.2', {
x: 500,
@ -98,7 +98,7 @@ describe("PositionNotifier", () => {
moveTriggered = true;
}, (thing: Movable) => {
leaveTriggered = true;
}, () => {});
});
const user1 = new User(1, 'test', '10.0.0.2', {
x: 500,

View File

@ -1251,9 +1251,9 @@ has-values@^1.0.0:
kind-of "^4.0.0"
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
http-errors@1.7.2:
version "1.7.2"
@ -1704,9 +1704,9 @@ lodash.once@^4.0.0:
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
long@~3:
version "3.2.0"
@ -3032,9 +3032,9 @@ xtend@^4.0.0:
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
y18n@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
yallist@^3.0.0, yallist@^3.0.3:
version "3.1.1"

View File

@ -230,9 +230,9 @@
}
},
"hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
},
"indent-string": {
"version": "2.1.0",

View File

@ -169,8 +169,8 @@ graceful-fs@^4.1.2:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
indent-string@^2.1.0:
version "2.1.0"

View File

@ -1,41 +0,0 @@
## The single (sub)domain to use
BASE_DOMAIN=wa.example.com
DEBUG_MODE=false
## JITSI settings
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=
## ADMIN backend settings
ADMIN_API_TOKEN=2342
ADMIN_API_URL=
## TURN server settings
# URL of the TURN server (needed to "punch a hole" through some networks for P2P connections)
STUN_SERVER=
TURN_SERVER=
TURN_USER=
TURN_PASSWORD=
## Reverse proxy settings (note: these must also be manually activated in the docker-compose file for now)
TRAEFIK_BASICAUTH=testuser:$2y$05$L2t/Wx937mHhKH61mjPL7OvepvjcyUnzVUkpiMo.nKeWSdRd5oyUC
TRAEFIK_BASICAUTHFILE=/.htpasswd
# Maximum allowed number of people per "ring" group
MAX_PER_GROUP=5
# Maximum length allowed for nicknames
MAX_USERNAME_LENGTH=12
# The URL used by default, in the form: "/_/global/map/url.json" or with active admin backend as "/@/org/world/room"
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=
# Set to true to allow using this instance as a target for the apiUrl property
FEDERATE_PUSHER=false

View File

@ -18,6 +18,3 @@ 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=
# Set to true to allow using this instance as a target for the apiUrl property
FEDERATE_PUSHER=false

View File

@ -1,73 +0,0 @@
version: '3'
services:
reverse-proxy:
image: traefik:v2.4
depends_on:
- front
- pusher
command:
- --providers.docker
- --entryPoints.web.address=:80
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:z
privileged: true
front:
build:
context: ../..
dockerfile: front/Dockerfile
args:
START_ROOM_URL: "$START_ROOM_URL"
JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
JITSI_URL: "$JITSI_URL"
STUN_SERVER: "$STUN_SERVER"
TURN_SERVER: "$TURN_SERVER"
TURN_USER: "$TURN_USER"
TURN_PASSWORD: "$TURN_PASSWORD"
environment:
DEBUG_MODE: "$DEBUG_MODE"
labels:
- "traefik.http.routers.front.rule=PathPrefix(`/`)"
- "traefik.http.routers.front.entryPoints=web"
- "traefik.http.services.front.loadbalancer.server.port=8000"
pusher:
build:
context: ../..
dockerfile: pusher/Dockerfile
depends_on:
- back
environment:
SECRET_KEY: yourSecretKey
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
API_URL: back:50051
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
ports:
- "9378:8080"
labels:
- "traefik.http.middlewares.strip-pusher-prefix.stripprefix.prefixes=/pusher"
- "traefik.http.routers.pusher.rule=PathPrefix(`/pusher`)"
- "traefik.http.routers.pusher.middlewares=strip-pusher-prefix@docker"
- "traefik.http.routers.pusher.entryPoints=web"
- "traefik.http.services.pusher.loadbalancer.server.port=8080"
back:
build:
context: ../..
dockerfile: back/Dockerfile
environment:
SECRET_KEY: yourSecretKey
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
labels:
- "traefik.http.middlewares.strip-api-prefix.stripprefix.prefixes=/api"
- "traefik.http.routers.back.rule=PathPrefix(`/api`)"
- "traefik.http.routers.back.middlewares=strip-api-prefix@docker"
- "traefik.http.routers.back.entryPoints=web"
- "traefik.http.services.back.loadbalancer.server.port=8080"

View File

@ -1,145 +0,0 @@
version: "3"
services:
reverse-proxy:
image: traefik:v2.3.7
restart: unless-stopped
command:
- --providers.docker
- --entryPoints.web.address=:80
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
- --entryPoints.websecure.address=:443
- --providers.file.directory=/configs/
- --certificatesresolvers.myhttpchallenge.acme.tlschallenge=true
- --certificatesresolvers.myhttpchallenge.acme.email=$ACME_EMAIL
- --certificatesresolvers.myhttpchallenge.acme.storage=/acme/acme.json
# uncomment for debugging traefik
# - --log.level=DEBUG
ports:
- "80:80"
- "443:443"
depends_on:
- back
- front
- uploader
- pusher
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./acme:/acme
- ./traefik_tls.yaml:/configs/traefik_tls.yml
- ./.htpasswd:/.htpasswd
front:
restart: unless-stopped
build:
context: ../..
dockerfile: front/Dockerfile
args:
BASE_DOMAIN: ${BASE_DOMAIN:-workadventure.localhost}
# These should fall back to window.location.host
PUSHER_URL: /pusher
UPLOADER_URL: /uploader
ADMIN_URL: /admin
MAPS_URL: /maps
START_ROOM_URL: "$START_ROOM_URL"
JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
JITSI_URL: "$JITSI_URL"
START_ROOM_URL: "$START_ROOM_URL"
STUN_SERVER: "$STUN_SERVER"
TURN_PASSWORD: "$TURN_PASSWORD"
TURN_SERVER: "$TURN_SERVER"
TURN_USER: "$TURN_USER"
MAX_PER_GROUP: $MAX_PER_GROUP
MAX_USERNAME_LENGTH: $MAX_USERNAME_LENGTH
labels:
- "traefik.http.routers.front.rule=Host(`${BASE_DOMAIN}`) && PathPrefix(`/`)"
- "traefik.http.routers.front.entryPoints=web,websecure"
- "traefik.http.services.front.loadbalancer.server.port=8000"
- "traefik.http.routers.front.tls=true"
- "traefik.http.routers.front.service=front"
- "traefik.http.routers.front.tls.certresolver=myhttpchallenge"
# uncomment to enable user/pass basic auth
# - "traefik.http.routers.front.middlewares=auth"
# - "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_BASICAUTH}"
# - "traefik.http.middlewares.auth.basicauth.usersFile=${TRAEFIK_BASICAUTHFILE}"
# - "traefik.http.middlewares.auth.basicauth.headerField=X-WebAuth-User"
pusher:
restart: unless-stopped
build:
context: ../..
dockerfile: pusher/Dockerfile
environment:
SECRET_KEY: yourSecretKey
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
ADMIN_API_URL: "$ADMIN_API_URL"
API_URL: back:50051
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
FEDERATE_PUSHER: $FEDERATE_PUSHER
labels:
- "traefik.http.middlewares.strip-pusher-prefix.stripprefix.prefixes=/pusher"
- "traefik.http.routers.pusher.rule=Host(`${BASE_DOMAIN}`) && PathPrefix(`/pusher`)"
- "traefik.http.routers.pusher.middlewares=strip-pusher-prefix@docker"
- "traefik.http.routers.pusher.entryPoints=web,websecure"
- "traefik.http.services.pusher.loadbalancer.server.port=8080"
- "traefik.http.routers.pusher.tls=true"
- "traefik.http.routers.pusher.service=pusher"
back:
restart: unless-stopped
build:
context: ../..
dockerfile: back/Dockerfile
environment:
SECRET_KEY: yourSecretKey
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
MAX_PER_GROUP: $MAX_PER_GROUP
labels:
- "traefik.http.middlewares.strip-api-prefix.stripprefix.prefixes=/api"
- "traefik.http.routers.back.rule=Host(`${BASE_DOMAIN}`) && PathPrefix(`/api`)"
- "traefik.http.routers.back.middlewares=strip-api-prefix@docker"
- "traefik.http.routers.back.entryPoints=web,websecure"
- "traefik.http.services.back.loadbalancer.server.port=8080"
- "traefik.http.routers.back.tls=true"
- "traefik.http.routers.back.service=back"
uploader:
restart: unless-stopped
build:
context: ../..
dockerfile: uploader/Dockerfile
labels:
- "traefik.http.middlewares.strip-uploader-prefix.stripprefix.prefixes=/uploader"
- "traefik.http.routers.uploader.rule=Host(`${BASE_DOMAIN}`) && PathPrefix(`/uploader`)"
- "traefik.http.routers.uploader.middlewares=strip-uploader-prefix@docker"
- "traefik.http.routers.uploader.entryPoints=web,websecure"
- "traefik.http.services.uploader.loadbalancer.server.port=8080"
- "traefik.http.routers.uploader.tls=true"
- "traefik.http.routers.uploader.service=uploader"
maps:
restart: unless-stopped
build:
context: ../..
dockerfile: maps/Dockerfile
volumes:
- ../../maps:/var/www/html/
labels:
- "traefik.http.middlewares.strip-maps-prefix.stripprefix.prefixes=/maps"
- "traefik.http.routers.maps.rule=Host(`${BASE_DOMAIN}`) && PathPrefix(`/maps`)"
- "traefik.http.routers.maps.middlewares=strip-maps-prefix@docker"
- "traefik.http.routers.maps.entryPoints=web,websecure"
- "traefik.http.services.maps.loadbalancer.server.port=80"
- "traefik.http.routers.maps.tls=true"
- "traefik.http.routers.maps.service=maps"

View File

@ -37,7 +37,7 @@ services:
DEBUG_MODE: "$DEBUG_MODE"
JITSI_URL: $JITSI_URL
JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
PUSHER_URL: //pusher.${DOMAIN}
API_URL: pusher.${DOMAIN}
TURN_SERVER: "${TURN_SERVER}"
TURN_USER: "${TURN_USER}"
TURN_PASSWORD: "${TURN_PASSWORD}"
@ -65,7 +65,6 @@ services:
API_URL: back:50051
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
FEDERATE_PUSHER: $FEDERATE_PUSHER
labels:
- "traefik.http.routers.pusher.rule=Host(`pusher.${DOMAIN}`)"
- "traefik.http.routers.pusher.entryPoints=web,traefik"

View File

@ -1,13 +0,0 @@
tls:
options:
default:
sniStrict: true
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305

View File

@ -1,115 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
@font-face {
font-family: "Press Start 2P";
src: url(PressStart2P-Regular.ttf);
}
canvas {
float: left;
border: 1px solid black;
}
h1 {
font-family: "Press Start 2P";
font-size: 20px;
}
.setting {
font-family: "Press Start 2P";
font-size: 12px;
}
.generator input {
height: 64px;
display: inline-block;
float: left;
margin: 0 1em 0 1em;
}
.generator textarea {
width: 256px;
height: 512px;
display: inline-block;
float: left;
}
</style>
<script type="text/javascript" >
function drawText () {
var fontName = document.getElementById("fontName").value;
var fontColor = document.getElementById("fontColor").value;
var marginTop = Number(document.getElementById("marginTop").value);
var marginLeft = Number(document.getElementById("marginLeft").value);
var fontSize = document.getElementById("fontSize").value;
var fontInfo = fontSize + "px " + fontName;
var lines = document.getElementById("textToDraw").value.split('\n');
var canvas = document.getElementById("textCanvas");
var canvasContext = canvas.getContext("2d");
var tileHeight = 32
var maxRows = (canvas.height / tileHeight) - 1;
var row = 0;
var column = 0;
canvasContext.font = fontInfo;
canvasContext.fillStyle = fontColor;
canvasContext.textAlign = "left";
canvasContext.textBaseline = "hanging";
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
for(var n=0; n<lines.length; n++) {
var x = (column * 128) + marginLeft;
var y = (row * tileHeight) + marginTop;
canvasContext.fillText(lines[n], x, y, 128-marginLeft);
row++;
if(row > maxRows)
{
column++;
row = 0;
}
}
}
</script>
</head>
<body>
<h1>Text to Tiles</h1>
<p>Jede Zeile wird linksbündig in ein Feld von 128px x 32px geschrieben.</p>
<p>Das Bild kann per Rechtsklick mit "Bild speichern" direkt als Tileset verwendet oder mit "Bild kopieren" in ein Grafikprogramm übernommen werden.</p>
<form>
<div class="setting">
<label>Font</label> <input id="fontName" value="&quot;Press Start 2P&quot;" style="width:15em"; />
</div>
<div class="setting">
<label>Size</label> <input id="fontSize" value="8" style="width:3em"; /> px
</div>
<div class="setting">
<label>Margin top</label> <input id="marginTop" value="18" style="width:3em"; /> px
</div>
<div class="setting">
<label>Margin left</label> <input id="marginLeft" value="20" style="width:3em"; /> px
</div>
<div class="setting">
<label>Color</label> <input id="fontColor" value="#cccccc" style="width:7em"; />
</div>
</form>
<form class="generator">
<textarea id="textToDraw"></textarea>
<input type="button" value="Draw =>" onclick="drawText();" />
</form>
<div style="overflow:scroll;width:512px;height:512px">
<canvas width="1056" height="1056" id="textCanvas"></canvas>
</div>
</body>
</html>

View File

@ -1,69 +0,0 @@
#!/bin/sh -e
# vim: ts=4 sw=4 et
# --- V A R I A B L E S --- #
SCRIPT_NAME=$(basename "$0")
# --- S U B R O U T I N E S --- #
print_help_message_and_exit() {
cat << EOM
usage: $SCRIPT_NAME [-h]
$SCRIPT_NAME -f
$SCRIPT_NAME [-f] [-b] <cntnr>
$SCRIPT_NAME [-f] -r
Cleanly rebuilds the Docker image of a container and restarts all containers.
options:
-b do not restart containers; only rebuild Docker image
-f fetches new commits and resets (--hard !) to origin/xce
-h show this help message
-r do not rebuild Docker imags; only restart containers
positional arguments:
cntnr name of the container whose image should be rebuilt
EOM
exit
}
# --- M A I N S C R I P T --- #
# Parse commnand line
fetch=no
rebuild=yes
reboot=yes
while getopts "fbhr" option; do
case $option in
b) reboot=no ;;
f) fetch=yes ;;
r) rebuild=no ;;
*) print_help_message_and_exit ;;
esac
done
shift $((OPTIND - 1))
CONTAINER=$1
if [ -z "$CONTAINER" ] && [ "$rebuild" = 'yes' ]; then
print_help_message_and_exit
fi
# Fetch
if [ "$fetch" = 'yes' ]; then
git fetch
git reset --hard origin/xce
fi
# Rebuild
if [ -n "$CONTAINER" ] && [ "$rebuild" = 'yes' ]; then
docker system prune -f
docker-compose build --no-cache "$CONTAINER"
fi
if [ "$reboot" = 'yes' ]; then
docker-compose down
docker-compose up -d
fi

View File

@ -1,8 +1,8 @@
{
local env = std.extVar("env"),
local namespace = env.DEPLOY_REF,
local namespace = env.GITHUB_REF_SLUG,
local tag = namespace,
local url = namespace+".test.workadventu.re",
local url = if namespace == "master" then "workadventu.re" else namespace+".workadventure.test.thecodingmachine.com",
// 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",
@ -25,7 +25,10 @@
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {})
} else {}) + if namespace != "master" then {
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
},
"back2": {
"image": "thecodingmachine/workadventure-back:"+tag,
@ -44,7 +47,10 @@
"TURN_STATIC_AUTH_SECRET": env.TURN_STATIC_AUTH_SECRET,
} + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {})
} else {}) + if namespace != "master" then {
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
},
"pusher": {
"replicas": 2,
@ -63,7 +69,10 @@
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
} + (if adminUrl != null then {
"ADMIN_API_URL": adminUrl,
} else {})
} else {}) + if namespace != "master" then {
// Absolutely ugly WorkAround to circumvent broken certificates on the K8S test cluster. Don't do this in production kids!
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
},
"front": {
"image": "thecodingmachine/workadventure-front:"+tag,
@ -73,12 +82,14 @@
},
"ports": [80],
"env": {
"PUSHER_URL": "//pusher."+url,
"UPLOADER_URL": "//uploader."+url,
"ADMIN_URL": "//"+url,
"API_URL": "pusher."+url,
"UPLOADER_URL": "uploader."+url,
"ADMIN_URL": url,
"JITSI_URL": env.JITSI_URL,
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
"TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443",
"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"

View File

@ -1,83 +0,0 @@
version: "3"
services:
# the frontend is only built here - it is served directly via nginx
front:
image: thecodingmachine/nodejs:14
environment:
DEBUG_MODE: "$DEBUG_MODE"
JITSI_URL: "$JITSI_URL"
JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
NODE_ENV: development
PUSHER_URL: "//pusher.$DOMAIN"
UPLOADER_URL: "//uploader.$DOMAIN"
STARTUP_COMMAND_1: ./templater.sh
STARTUP_COMMAND_2: yarn install
STUN_SERVER: "stun:stun.l.google.com:19302"
TURN_SERVER: "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443"
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
# 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"
MAX_PER_GROUP: "$MAX_PER_GROUP"
MAX_USERNAME_LENGTH: "$MAX_USERNAME_LENGTH"
command: yarn run build
volumes:
- ./front:/usr/src/app
pusher:
image: thecodingmachine/nodejs:12
environment:
DEBUG: "socket:*"
STARTUP_COMMAND_1: yarn install
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
SECRET_KEY: yourSecretKey
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
API_URL: back:50051
JITSI_URL: "$JITSI_URL"
JITSI_ISS: "$JITSI_ISS"
FEDERATE_PUSHER: $FEDERATE_PUSHER
command: yarn run prod
ports:
- "127.0.0.1:8002:8080"
volumes:
- ./pusher:/usr/src/app
back:
image: thecodingmachine/nodejs:12
environment:
DEBUG: "*"
STARTUP_COMMAND_1: yarn install
SECRET_KEY: yourSecretKey
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
ALLOW_ARTILLERY: "true"
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
JITSI_URL: "$JITSI_URL"
JITSI_ISS: "$JITSI_ISS"
TURN_STATIC_AUTH_SECRET: SomeStaticAuthSecret
MAX_PER_GROUP: "MAX_PER_GROUP"
command: yarn run prod
volumes:
- ./back:/usr/src/app
uploader:
image: thecodingmachine/nodejs:12
environment:
DEBUG: "*"
STARTUP_COMMAND_1: yarn install
command: yarn run prod
ports:
- "127.0.0.1:8005:8080"
volumes:
- ./uploader:/usr/src/app
messages:
#image: thecodingmachine/nodejs:14
image: thecodingmachine/workadventure-back-base:latest
environment:
#STARTUP_COMMAND_0: sudo apt-get install -y inotify-tools
STARTUP_COMMAND_1: yarn install
STARTUP_COMMAND_2: yarn run proto:watch
volumes:
- ./messages:/usr/src/app
- ./back:/usr/src/back
- ./front:/usr/src/front
- ./pusher:/usr/src/pusher

View File

@ -1,194 +0,0 @@
version: "3"
services:
reverse-proxy:
image: traefik:v2.0
command:
- --api.insecure=true
- --providers.docker
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
ports:
- "80:80"
- "443:443"
# The Web UI (enabled by --api.insecure=true)
- "8080:8080"
depends_on:
- back
- front
volumes:
- /var/run/docker.sock:/var/run/docker.sock
front:
image: thecodingmachine/nodejs:14
environment:
DEBUG_MODE: "$DEBUG_MODE"
JITSI_URL: $JITSI_URL
JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
HOST: "0.0.0.0"
NODE_ENV: development
PUSHER_URL: /pusher
UPLOADER_URL: /uploader
ADMIN_URL: /admin
MAPS_URL: /maps
STARTUP_COMMAND_1: ./templater.sh
STARTUP_COMMAND_2: yarn install
TURN_SERVER: "turn:localhost:3478,turns:localhost:5349"
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
# 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:
- ./front:/usr/src/app
labels:
- "traefik.http.routers.front.rule=PathPrefix(`/`)"
- "traefik.http.routers.front.entryPoints=web,traefik"
- "traefik.http.services.front.loadbalancer.server.port=8080"
- "traefik.http.routers.front-ssl.rule=PathPrefix(`/`)"
- "traefik.http.routers.front-ssl.entryPoints=websecure"
- "traefik.http.routers.front-ssl.tls=true"
- "traefik.http.routers.front-ssl.service=front"
pusher:
image: thecodingmachine/nodejs:12
command: yarn dev
#command: yarn run prod
#command: yarn run profile
environment:
DEBUG: "*"
STARTUP_COMMAND_1: yarn install
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
SECRET_KEY: yourSecretKey
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
API_URL: back:50051
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
volumes:
- ./pusher:/usr/src/app
labels:
- "traefik.http.middlewares.strip-pusher-prefix.stripprefix.prefixes=/pusher"
- "traefik.http.routers.pusher.rule=PathPrefix(`/pusher`)"
- "traefik.http.routers.pusher.middlewares=strip-pusher-prefix@docker"
- "traefik.http.routers.pusher.entryPoints=web"
- "traefik.http.services.pusher.loadbalancer.server.port=8080"
- "traefik.http.routers.pusher-ssl.rule=PathPrefix(`/pusher`)"
- "traefik.http.routers.pusher-ssl.middlewares=strip-pusher-prefix@docker"
- "traefik.http.routers.pusher-ssl.entryPoints=websecure"
- "traefik.http.routers.pusher-ssl.tls=true"
- "traefik.http.routers.pusher-ssl.service=pusher"
maps:
image: thecodingmachine/nodejs:12-apache
environment:
DEBUG_MODE: "$DEBUG_MODE"
HOST: "0.0.0.0"
NODE_ENV: development
#APACHE_DOCUMENT_ROOT: dist/
#APACHE_EXTENSIONS: headers
#APACHE_EXTENSION_HEADERS: 1
STARTUP_COMMAND_0: sudo a2enmod headers
STARTUP_COMMAND_1: yarn install
STARTUP_COMMAND_2: yarn run dev &
volumes:
- ./maps:/var/www/html
labels:
- "traefik.http.middlewares.strip-maps-prefix.stripprefix.prefixes=/maps"
- "traefik.http.routers.maps.rule=PathPrefix(`/maps`)"
- "traefik.http.routers.maps.middlewares=strip-maps-prefix@docker"
- "traefik.http.routers.maps.entryPoints=web,traefik"
- "traefik.http.services.maps.loadbalancer.server.port=80"
- "traefik.http.routers.maps-ssl.rule=PathPrefix(`/maps`)"
- "traefik.http.routers.maps-ssl.middlewares=strip-maps-prefix@docker"
- "traefik.http.routers.maps-ssl.entryPoints=websecure"
- "traefik.http.routers.maps-ssl.tls=true"
- "traefik.http.routers.maps-ssl.service=maps"
back:
image: thecodingmachine/nodejs:12
command: yarn dev
#command: yarn run profile
environment:
DEBUG: "*"
STARTUP_COMMAND_1: yarn install
SECRET_KEY: yourSecretKey
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
ALLOW_ARTILLERY: "true"
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
MAX_PER_GROUP: "$MAX_PER_GROUP"
volumes:
- ./back:/usr/src/app
labels:
- "traefik.http.middlewares.strip-api-prefix.stripprefix.prefixes=/api"
- "traefik.http.routers.back.rule=PathPrefix(`/api`)"
- "traefik.http.routers.back.middlewares=strip-api-prefix@docker"
- "traefik.http.routers.back.entryPoints=web"
- "traefik.http.services.back.loadbalancer.server.port=8080"
- "traefik.http.routers.back-ssl.rule=PathPrefix(`/api`)"
- "traefik.http.routers.back-ssl.middlewares=strip-api-prefix@docker"
- "traefik.http.routers.back-ssl.entryPoints=websecure"
- "traefik.http.routers.back-ssl.tls=true"
- "traefik.http.routers.back-ssl.service=back"
uploader:
image: thecodingmachine/nodejs:12
command: yarn dev
#command: yarn run profile
environment:
DEBUG: "*"
STARTUP_COMMAND_1: yarn install
volumes:
- ./uploader:/usr/src/app
labels:
- "traefik.http.middlewares.strip-uploader-prefix.stripprefix.prefixes=/uploader"
- "traefik.http.routers.uploader.rule=PathPrefix(`/uploader`)"
- "traefik.http.routers.uploader.middlewares=strip-uploader-prefix@docker"
- "traefik.http.routers.uploader.entryPoints=web"
- "traefik.http.services.uploader.loadbalancer.server.port=8080"
- "traefik.http.routers.uploader-ssl.rule=PathPrefix(`/uploader`)"
- "traefik.http.routers.uploader-ssl.middlewares=strip-uploader-prefix@docker"
- "traefik.http.routers.uploader-ssl.entryPoints=websecure"
- "traefik.http.routers.uploader-ssl.tls=true"
- "traefik.http.routers.uploader-ssl.service=uploader"
messages:
#image: thecodingmachine/nodejs:14
image: thecodingmachine/workadventure-back-base:latest
environment:
#STARTUP_COMMAND_0: sudo apt-get install -y inotify-tools
STARTUP_COMMAND_1: yarn install
STARTUP_COMMAND_2: yarn run proto:watch
volumes:
- ./messages:/usr/src/app
- ./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=localhost
# - --server-name=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

View File

@ -26,28 +26,24 @@ services:
JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
HOST: "0.0.0.0"
NODE_ENV: development
PUSHER_URL: //pusher.workadventure.localhost
UPLOADER_URL: //uploader.workadventure.localhost
ADMIN_URL: //workadventure.localhost
API_URL: pusher.workadventure.localhost
UPLOADER_URL: uploader.workadventure.localhost
ADMIN_URL: workadventure.localhost
STARTUP_COMMAND_1: ./templater.sh
STARTUP_COMMAND_2: yarn install
STUN_SERVER: "stun:stun.l.google.com:19302"
TURN_SERVER: "turn:coturn.workadventure.localhost:3478,turns:coturn.workadventure.localhost:5349"
DISABLE_NOTIFICATIONS: "$DISABLE_NOTIFICATIONS"
SKIP_RENDER_OPTIMIZATIONS: "$SKIP_RENDER_OPTIMIZATIONS"
# Use TURN_USER/TURN_PASSWORD if your Coturn server is secured via hard coded credentials.
# 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"
MAX_PER_GROUP: "$MAX_PER_GROUP"
MAX_USERNAME_LENGTH: "$MAX_USERNAME_LENGTH"
command: yarn run start
volumes:
- ./front:/usr/src/app
labels:
- "traefik.http.routers.front.rule=Host(`play.workadventure.localhost`)"
- "traefik.http.routers.front.entryPoints=web"
- "traefik.http.routers.front.entryPoints=web,traefik"
- "traefik.http.services.front.loadbalancer.server.port=8080"
- "traefik.http.routers.front-ssl.rule=Host(`play.workadventure.localhost`)"
- "traefik.http.routers.front-ssl.entryPoints=websecure"
@ -66,7 +62,6 @@ services:
API_URL: back:50051
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
FEDERATE_PUSHER: $FEDERATE_PUSHER
volumes:
- ./pusher:/usr/src/app
labels:
@ -115,7 +110,6 @@ services:
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
TURN_STATIC_AUTH_SECRET: SomeStaticAuthSecret
MAX_PER_GROUP: "MAX_PER_GROUP"
volumes:
- ./back:/usr/src/app
labels:

View File

@ -1,237 +0,0 @@
{.section-title.accent.text-primary}
# API Reference
### Sending a message in the chat
```
sendChatMessage(message: string, author: string): void
```
Sends a message in the chat. The message is only visible in the browser of the current user.
* **message**: the message to be displayed in the chat
* **author**: the name displayed for the author of the message. It does not have to be a real user.
Example:
```javascript
WA.sendChatMessage('Hello world', 'Mr Robot');
```
### Listening to messages from the chat
```javascript
onChatMessage(callback: (message: string) => void): void
```
Listens to messages typed by the current user and calls the callback. Messages from other users in the chat cannot be listened to.
* **callback**: the function that will be called when a message is received. It contains the message typed by the user.
Example:
```javascript
WA.onChatMessage((message => {
console.log('The user typed a message', message);
}));
```
### Detecting when the user enters/leaves a zone
```
onEnterZone(name: string, callback: () => void): void
onLeaveZone(name: string, callback: () => void): void
```
Listens to the position of the current user. The event is triggered when the user enters or leaves a given zone. The name of the zone is stored in the map, on a dedicated layer with the `zone` property.
<div>
<figure class="figure">
<img src="https://workadventu.re/img/docs/trigger_event.png" class="figure-img img-fluid rounded" alt="" />
<figcaption class="figure-caption">The `zone` property, applied on a layer</figcaption>
</figure>
</div>
* **name**: the name of the zone, as defined in the `zone` property.
* **callback**: the function that will be called when a user enters or leaves the zone.
Example:
```javascript
WA.onEnterZone('myZone', () => {
WA.sendChatMessage("Hello!", 'Mr Robot');
})
WA.onLeaveZone('myZone', () => {
WA.sendChatMessage("Goodbye!", 'Mr Robot');
})
```
### Opening a popup
In order to open a popup window, you must first define the position of the popup on your map.
You can position this popup by using a "rectangle" object in Tiled that you will place on an "object" layer.
<div class="row">
<div class="col">
<img src="https://workadventu.re/img/docs/screen_popup_tiled.png" class="figure-img img-fluid rounded" alt="" />
</div>
<div class="col">
<img src="https://workadventu.re/img/docs/screen_popup_in_game.png" class="figure-img img-fluid rounded" alt="" />
</div>
</div>
```
openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup
```
* **targetObject**: the name of the rectangle object defined in Tiled.
* **message**: the message to display in the popup.
* **buttons**: an array of action buttons defined underneath the popup.
Action buttons are `ButtonDescriptor` objects containing these properties.
* **label (_string_)**: The label of the button.
* **className (_string_)**: The visual type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled".
* **callback (_(popup: Popup)=>void_)**: Callback called when the button is pressed.
Please note that `openPopup` returns an object of the `Popup` class. Also, the callback called when a button is clicked is passed a `Popup` object.
The `Popup` class that represents an open popup contains a single method: `close()`. This will obviously close the popup when called.
```javascript
class Popup {
/**
* Closes the popup
*/
close() {};
}
```
Example:
```javascript
let helloWorldPopup;
// Open the popup when we enter a given zone
helloWorldPopup = WA.onEnterZone('myZone', () => {
WA.openPopup("popupRectangle", 'Hello world!', [{
label: "Close",
className: "primary",
callback: (popup) => {
// Close the popup when the "Close" button is pressed.
popup.close();
}
});
}]);
// Close the popup when we leave the zone.
WA.onLeaveZone('myZone', () => {
helloWorldPopup.close();
});
```
### Disabling / restoring controls
```
disablePlayerControls(): void
restorePlayerControls(): void
```
These 2 methods can be used to completely disable player controls and to enable them again.
When controls are disabled, the user cannot move anymore using keyboard input. This can be useful in a "First Time User Experience" part, to display an important message to a user before letting him/her move again.
Example:
```javascript
WA.onEnterZone('myZone', () => {
WA.disablePlayerControls();
WA.openPopup("popupRectangle", 'This is an imporant message!', [{
label: "Got it!",
className: "primary",
callback: (popup) => {
WA.restorePlayerControls();
popup.close();
}
}]);
});
```
### Opening a web page in a new tab
```
openTab(url: string): void
```
Opens the webpage at "url" in your browser, in a new tab.
Example:
```javascript
WA.openTab('https://www.wikipedia.org/');
```
### Opening a web page in the current tab
```
goToPage(url: string): void
```
Opens the webpage at "url" in your browser in place of WorkAdventure. WorkAdventure will be completely unloaded.
Example:
```javascript
WA.goToPage('https://www.wikipedia.org/');
```
### Opening/closing a web page in an iFrame
```
openCoWebSite(url: string): void
closeCoWebSite(): void
```
Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame.
Example:
```javascript
WA.openCoWebSite('https://www.wikipedia.org/');
// ...
WA.closeCoWebSite();
```
### Load a sound from an url
```
loadSound(url: string): Sound
```
Load a sound from an url
Please note that `loadSound` returns an object of the `Sound` class
The `Sound` class that represents a loaded sound contains two methods: `play(soundConfig : SoundConfig|undefined)` and `stop()`
The parameter soundConfig is optional, if you call play without a Sound config the sound will be played with the basic configuration.
Example:
```javascript
var mySound = WA.loadSound("Sound.ogg");
var config = {
volume : 0.5,
loop : false,
rate : 1,
detune : 1,
delay : 0,
seek : 0,
mute : false
}
mySound.play(config);
// ...
mySound.stop();
```

View File

@ -1,117 +0,0 @@
{.alert.alert-danger style="width:80%"}
This feature is "_experimental_". We may apply changes in the near future to the way it works when we gather some feedback.
{.section-title.accent.text-primary}
# Scripting WorkAdventure maps
Do you want to add a bit of intelligence to your map? Scripts allow you to create maps with special features.
You can for instance:
* Create FTUE (First Time User Experience) scenarios where a first-time user will be displayed a notification popup.
* Create NPC (non playing characters) and interact with those characters using the chat.
* Organize interactions between an iframe and your map (for instance, walking on a special zone might add a product in the cart of an eCommerce website...)
* etc...
Please note that scripting in WorkAdventure is at an early stage of development and that more features might be added in the future. You can actually voice your opinion about useful features by adding [an issue on Github](https://github.com/thecodingmachine/workadventure/issues).
{.alert.alert-warning}
**Beware:** Scripts are executed in the browser of the current user only. Generally speaking, scripts cannot be used to trigger a change that will be displayed on other users screen.
## Scripting language
Client-side scripting is done in **Javascript** (or any language that transpiles to Javascript like _Typescript_).
There are 2 ways you can use the scripting language:
* **In the map**: By directly referring a Javascript file inside your map, in the `script` property of your map.
* **In an iFrame**: By placing your Javascript script into an iFrame, your script can communicate with the WorkAdventure game
## Adding a script in the map
Create a `script` property in your map.
In Tiled, in order to access your map properties, you can click on _"Map > Map properties"_.
<div>
<figure class="figure">
<img src="https://workadventu.re/img/docs/admin/map_properties.png" class="figure-img img-fluid rounded" alt="" />
<figcaption class="figure-caption">The Map properties menu</figcaption>
</figure>
</div>
Create a `script` property (a "string"), and put the URL of your script.
You can put relative URLs. If your script file is next to your map, you can simply write the name of the script file here.
<div>
<figure class="figure">
<img src="https://workadventu.re/img/docs/script_property.png" class="figure-img img-fluid rounded" alt="" />
<figcaption class="figure-caption">The script property</figcaption>
</figure>
</div>
Start by testing this with a simple message sent to the chat.
**script.js**
```javascript
WA.sendChatMessage('Hello world', 'Mr Robot');
```
The `WA` objects contains a number of useful methods enabling you to interact with the WorkAdventure game. For instance, `WA.sendChatMessage` opens the chat and adds a message in it.
In your browser console, when you open the map, the chat message should be displayed right away.
## Adding a script in an iFrame
In WorkAdventure, you can easily [open an iFrame using the `openWebsite` property on a layer](special-zones). However, by default, the iFrame is not allowed to communicate with WorkAdventure.
This is done to improve security. In order to be able to execute a script that communicates with WorkAdventure inside an iFrame, you have to **explicitly allow the iFrame to use the "iFrame API"**.
In order to allow communication with WorkAdventure, you need to add an additional property: `openWebsiteAllowApi`. This property must be _boolean_ and you must set it to "true".
<div>
<figure class="figure">
<img src="https://workadventu.re/img/docs/open_website_allow_api.png" class="figure-img img-fluid rounded" alt="" />
<figcaption class="figure-caption">The `openWebsiteAllowApi` property</figcaption>
</figure>
</div>
In your iFrame HTML page, you now need to import the _WorkAdventure client API Javascript library_. This library contains the `WA` object that you can use to communicate with WorkAdventure.
The library is available at `https://play.workadventu.re/iframe_api.js`.
_Note:_ if you are using a self-hosted version of WorkAdventure, use `https://[front_domain]/iframe_api.js`
**iframe.html**
```html
<!doctype html>
<html lang="en">
<head>
<script src="https://play.workadventu.re/iframe_api.js"></script>
</head>
<body>
</body>
</html>
```
You can now start by testing this with a simple message sent to the chat.
**iframe.html**
```html
...
<script>
WA.sendChatMessage('Hello world', 'Mr Robot');
</script>
...
```
Let's now review the complete list of methods available in this `WA` object.
## Using Typescript
View the dedicated page about [using Typescript with the scripting API](using-typescript).
## Available features in the client API
The list of available functions and features is [available in the API Reference page, with examples](api-reference).

View File

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

View File

@ -4,7 +4,6 @@ WORKDIR /usr/src
COPY messages .
RUN yarn install && yarn proto
# webpack build
FROM node:14-buster-slim as builder2
WORKDIR /usr/src
@ -15,28 +14,6 @@ COPY front .
COPY --from=builder /usr/src/generated src/Messages/generated
ENV NODE_ENV=production
# Removing the iframe.html file from the final image as this adds a XSS attack.
# iframe.html is only in dev mode to circumvent a limitation
RUN rm dist/iframe.html
ARG PUSHER_URL=/pusher
ARG ADMIN_URL=/admin
ARG MAPS_URL=/maps
ARG UPLOADER_URL=/uploader
ARG START_ROOM_URL
ARG JITSI_PRIVATE_MODE
ARG JITSI_URL
ARG STUN_SERVER
ARG TURN_SERVER
ARG TURN_USER
ARG TURN_PASSWORD
ARG MAX_USERNAME_LENGTH
ARG MAX_PER_GROUP
COPY front .
COPY --from=builder /usr/src/generated src/Messages/generated
ENV NODE_ENV=production
ARG ADMIN_URL=admin.$BASE_DOMAIN
ARG API_URL=pusher.$BASE_DOMAIN
ARG BASE_DOMAIN=workadventure.localhost
@ -52,8 +29,7 @@ ARG UPLOADER_URL=uploader.$BASE_DOMAIN
RUN ./templater.sh
RUN API_URL=$API_URL UPLOADER_URL=$UPLOADER_URL ADMIN_URL=$ADMIN_URL START_ROOM_URL=$START_ROOM_URL \
JITSI_URL=$JITSI_URL JITSI_PRIVATE_MODE=$JITSI_PRIVATE_MODE \
STUN_SERVER=$STUN_SERVER TURN_SERVER=$TURN_SERVER TURN_USER=$TURN_USER \
TURN_PASSWORD=$TURN_PASSWORD MAX_USERNAME_LENGTH=$MAX_USERNAME_LENGTH MAX_PER_GROUP=$MAX_PER_GROUP \
STUN_SERVER=$STUN_SERVER TURN_SERVER=$TURN_SERVER TURN_USER=$TURN_USER TURN_PASSWORD=$TURN_PASSWORD \
yarn run build
# final production image

View File

@ -1,4 +1,3 @@
index.html
index.tmpl.html.tmp
/js/
style.*.css
style.*.css

View File

@ -1,17 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<script src="/iframe_api.js" ></script>
<script>
// Note: this is a huge XSS flow as we allow anyone to load a Javascript file in our domain.
// This file must ABSOLUTELY be removed from the Docker images/deployments and is only here
// for development purpose (because dynamically generated iframes are not working with
// webpack hot reload due to an issue with rights)
const urlParams = new URLSearchParams(window.location.search);
const scriptUrl = urlParams.get('script');
const script = document.createElement('script');
script.src = scriptUrl;
document.head.append(script);
</script>
</head>
</html>

View File

@ -29,17 +29,12 @@
<base href="/">
<link href="https://fonts.googleapis.com/css?family=Press+Start+2P" rel="stylesheet">
<link href="https://unpkg.com/nes.css@2.3.0/css/nes.min.css" rel="stylesheet" />
<title>Binary Kitchen World</title>
</head>
<body id="body" style="margin: 0; background-color: #000">
<div class="main-container" id="main-container">
<!-- Create the editor container -->
<div id="game" class="game">
<div id="svelte-overlay">
</div>
<div id="game-overlay" class="game-overlay">
<div id="main-section" class="main-section">
</div>
@ -56,29 +51,23 @@
<div id="activeCam" class="activeCam">
<div id="div-myCamVideo" class="video-container">
<video id="myCamVideo" autoplay muted></video>
<div id="mySoundMeter" class="sound-progress">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
<div class="btn-cam-action">
<div id="btn-monitor" class="btn-monitor">
<img id="monitor" src="resources/logos/monitor.svg">
<img id="monitor-close" src="resources/logos/monitor-close.svg">
<div id="btn-micro" class="btn-micro">
<img id="microphone" src="resources/logos/microphone.svg">
<img id="microphone-close" src="resources/logos/microphone-close.svg">
</div>
<div id="btn-video" class="btn-video">
<img id="cinema" src="resources/logos/cinema.svg">
<img id="cinema-close" src="resources/logos/cinema-close.svg">
</div>
<div id="btn-micro" class="btn-micro">
<img id="microphone" src="resources/logos/microphone.svg">
<img id="microphone-close" src="resources/logos/microphone-close.svg">
<div id="btn-monitor" class="btn-monitor">
<img id="monitor" src="resources/logos/monitor.svg">
<img id="monitor-close" src="resources/logos/monitor-close.svg">
</div>
</div>
</div>
</div>
<div id="cowebsite" class="cowebsite hidden">
@ -87,11 +76,11 @@
</aside>
<main id="cowebsite-main">
</main>
<button class="top-right-btn" id="cowebsite-fullscreen" alt="fullscreen mode">
<img id="cowebsite-fullscreen-open" src="resources/logos/fullscreen.svg"/>
<img id="cowebsite-fullscreen-close" style="display: none;" src="resources/logos/fullscreen-exit.svg"/>
<button class="top-right-btn" id="cowebsite-fullscreen">
<img id="cowebsite-fullscreen-open" src="resources/logos/monitor.svg"/>
<img id="cowebsite-fullscreen-close" style="display: none;" src="resources/logos/monitor-close.svg"/>
</button>
<button class="top-right-btn" id="cowebsite-close" alt="close the iframe">
<button class="top-right-btn" id="cowebsite-close">
<img src="resources/logos/close.svg"/>
</button>
</div>
@ -102,15 +91,9 @@
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-volume-up" fill="white" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06zM6 5.04L4.312 6.39A.5.5 0 0 1 4 6.5H2v3h2a.5.5 0 0 1 .312.11L6 10.96V5.04z" />
<g id="audioplayer_volume_icon_playing">
<g id="audioplayer_volume_icon_playing_high">
<path d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303l.708.707z" />
</g>
<g id="audioplayer_volume_icon_playing_mid">
<path d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89l.706.706z" />
</g>
<g id="audioplayer_volume_icon_playing_low">
<path d="M8.707 11.182A4.486 4.486 0 0 0 10.025 8a4.486 4.486 0 0 0-1.318-3.182L8 5.525A3.489 3.489 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475l.707.707z" />
</g>
<path d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303l.708.707z" />
<path d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89l.706.706z" />
<path d="M8.707 11.182A4.486 4.486 0 0 0 10.025 8a4.486 4.486 0 0 0-1.318-3.182L8 5.525A3.489 3.489 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475l.707.707z" />
</g>
</svg>
</button>
@ -119,22 +102,13 @@
</div>
</div>
<div class="audioplayer">
<label id="label-audioplayer_decrease_while_talking" for="audioplayer_decrease_while_talking" title="decrease background volume by 50% when entering conversations">
<label id="label-audioplayer_decrease_while_talking" for="audiooplayer_decrease_while_talking" title="decrease background volume by 50% when entering conversations">
reduce in conversations
<input type="checkbox" id="audioplayer_decrease_while_talking" checked />
</label>
<div id="audioplayer" style="visibility: hidden"></div>
</div>
</div>
<div id="videoplayerctrl" class="hidden">
<div class="videoplayer">
<div id="videoplayer"></div>
<label id="label-videoplayer_decrease_while_talking" for="videoplayer_decrease_while_talking" title="decrease background volume by 50% when entering conversations">
autoreduce
<input type="checkbox" id="videoplayer_decrease_while_talking" checked />
</label>
</div>
</div>
<div class="audio-playing">
<img src="/resources/logos/megaphone.svg" />
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -1,160 +0,0 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#customizeScene {
background: #0000;
/*border: 1px solid #ebeeee;*/
border-radius: 6px;
margin: 10px auto 0;
color: #ebeeee;
width: 42vw;
height: 48vh;
/*max-width: 300px;
max-height: 48vh;*/
overflow: hidden;
}
#customizeScene h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#customizeScene input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
text-align: center;
}
#customizeScene section {
margin: 10px;
}
#customizeScene section.action {
text-align: center;
position: sticky;
bottom: 0;
top: 100%;
}
#customizeScene section.action.action-move {
top: 55%;
}
#customizeScene button {
margin: 2px 10px;
background-color: black;;
color: #ebeeee;
border-radius: 7px;
padding-bottom: 4px;
}
#customizeScene button#customizeSceneFormCancel {
background-color: #aca6a600;
color: #292929;
}
#customizeScene section h6,
#customizeScene section h5{
margin: 1px;
}
#customizeScene section.text-center{
text-align: center;
}
#customizeScene section a{
font-size: 14px;
text-decoration: underline;
color: #ebeeee;
}
#customizeScene section a:hover{
font-weight: 700;
}
#customizeScene section p{
text-align: left;
font-size: 8px;
margin: 10px 10px;
}
#customizeScene section p.err{
color: red;
text-align: center;
}
#customizeScene section p.info{
display: none;
text-align: center;
}
#customizeScene section input#customizeSceneLink{
background-color: #a1a3a3;
}
#customizeScene section button.customizeSceneButton{
position: absolute;
margin: 0;
top: -8vh;
font-size: 10px;
padding: 2px 4px;
}
#customizeScene section button.customizeSceneButton#customizeSceneButtonLeft{
left: 0vw;
}
#customizeScene section button.customizeSceneButton#customizeSceneButtonRight{
right: 0vw;
}
#customizeScene section button.customizeSceneButton#customizeSceneButtonUp{
left: calc(2vw + 40px);
transform: rotate(90deg);
margin-top: -20px;
}
#customizeScene section button.customizeSceneButton#customizeSceneButtonDown{
right: calc(2vw + 40px);
transform: rotate(90deg);
margin-top: 20px;
}
#customizeScene section.action img{
width: 8px;
height: 8px;
}
#customizeScene section.action a#customizeSceneFormBack img{
margin-top: -2px;
}
#customizeScene section.action button#customizeSceneFormSubmit img{
transform: rotate(180deg);
}
@media only screen and (max-width: 600px) {
#customizeScene {
max-width: 160px;
overflow-y: scroll;
}
}
@media only screen and (max-height: 400px) {
#customizeScene section.action {
top: 92%;
}
#customizeScene section.action.action-move {
top: 80%;
}
}
</style>
<form id="customizeScene" hidden>
<section class="text-center">
<h5>Custom your WOKA</h3>
</section>
<section class="action action-move">
<button class="customizeSceneButton" id="customizeSceneButtonLeft"> < </button>
<button class="customizeSceneButton" id="customizeSceneButtonRight"> > </button>
<!--<button class="customizeSceneButton" id="customizeSceneButtonUp"> < </button>
<button class="customizeSceneButton" id="customizeSceneButtonDown"> > </button>-->
</section>
<section class="action">
<a type="submit" id="customizeSceneFormBack">Back <img src="resources/objects/arrow_up.png"/></a>
<button type="submit" id="customizeSceneFormSubmit">Next <img src="resources/objects/arrow_up.png"/></button>
</section>
</form>

View File

@ -1,129 +0,0 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#enableCameraScene {
background: #000000;
/*border: 1px solid #ebeeee;*/
border-radius: 6px;
margin: 20px auto 0;
color: #ebeeee;
max-height: 48vh;
width: 42vw;
max-width: 300px;
overflow: hidden;
}
#enableCameraScene h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#enableCameraScene input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
text-align: center;
}
#enableCameraScene section.title {
position: absolute;
top: 1vh;
width: 100%;
}
#enableCameraScene section.action{
text-align: center;
margin: 0;
position: absolute;
top: 40vh;
width: 100%;
}
#enableCameraScene button {
margin: 10px;
background-color: black;;
color: #ebeeee;
border-radius: 7px;
padding-bottom: 4px;
}
#enableCameraScene button#enableCameraSceneFormCancel {
background-color: #c7c7c700;
color: #292929;
}
#enableCameraScene section h6,
#enableCameraScene section h5{
margin: 1px;
}
#enableCameraScene section.text-center{
text-align: center;
}
#enableCameraScene section a{
font-size: 8px;
text-decoration: underline;
color: #ebeeee;
}
#enableCameraScene section a:hover{
font-weight: 700;
}
#enableCameraScene section p{
text-align: left;
font-size: 8px;
margin: 10px 10px;
}
#enableCameraScene section p.err{
color: red;
text-align: center;
}
#enableCameraScene section p.info{
display: none;
text-align: center;
}
#enableCameraScene section input#enableCameraSceneLink{
background-color: #a1a3a3;
}
#enableCameraScene section img{
width: 160px;
margin: 20px 0;
}
/*@media only screen and (max-width: 800px),
only screen and (max-height: 600px) {
#enableCameraScene{
overflow-y: scroll;
}
}*/
</style>
<form id="enableCameraScene" hidden>
<!-- FIX me -->
<section class="title text-center">
<h5>Turn on your camera and microphone</h5>
</section>
<!--<section class="text-center">
<video id="myCamVideoSetup" autoplay muted></video>
</section>
<section class="text-center">
<h5>Select your camera</h3>
<select id="camera">
</select>
</section>
<section class="text-center">
<h5>Select your michrophone</h3>
<select id="michrophone">
</select>
</section>-->
<section class="action">
<button type="submit" id="enableCameraSceneFormSubmit">Let's go!</button>
</section>
</form>

View File

@ -1,134 +0,0 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#selectCompanionScene {
background: #0000;
/*border: 1px solid #ebeeee;*/
border-radius: 6px;
margin: 10px auto 0;
color: #ebeeee;
max-height: 40vh;
max-width: 300px;
width: 40vw;
overflow: hidden;
}
#selectCompanionScene h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#selectCompanionScene input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
text-align: center;
}
#selectCompanionScene section {
margin: 10px;
}
#selectCompanionScene section.action {
text-align: center;
margin: 0;
margin-top: 20vh;
}
#selectCompanionScene button {
margin: 10px 4px;
background-color: black;;
color: #ebeeee;
border-radius: 7px;
padding-bottom: 4px;
width: 100px;
}
#selectCompanionScene button#selectCompanionSceneFormCancel {
background-color: #aca6a600;
color: #292929;
}
#selectCompanionScene section h6,
#selectCompanionScene section h5{
margin: 1px;
}
#selectCompanionScene section.text-center{
text-align: center;
}
#selectCompanionScene section a{
font-size: 14px;
text-decoration: underline;
color: #ebeeee;
}
#selectCompanionScene section a:hover{
font-weight: 700;
}
#selectCompanionScene section p{
text-align: left;
font-size: 8px;
margin: 10px 10px;
}
#selectCompanionScene section p.err{
color: red;
text-align: center;
}
#selectCompanionScene section p.info{
display: none;
text-align: center;
}
#selectCompanionScene section input#selectCompanionSceneLink{
background-color: #a1a3a3;
}
#selectCompanionScene section img{
width: 160px;
margin: 20px 0;
}
#selectCompanionScene section button.selectCharacterButton{
position: absolute;
top: 20vh;
margin: 0;
width: 25px;
}
#selectCompanionScene section button.selectCharacterButton#selectCharacterButtonLeft{
left: 1vw;
}
#selectCompanionScene section button.selectCharacterButton#selectCharacterButtonRight{
right: 1vw;
}
#selectCompanionScene section button#selectCompanionSceneFormCustomYourOwnSubmit{
font-size: 14px;
width: auto;
margin-top: -2px;
background-color: #ffd700;
color: black;
}
@media only screen and (max-width: 800px),
only screen and (max-height: 600px) {
#selectCompanionScene{
overflow-y: scroll;
}
}
</style>
<form id="selectCompanionScene" hidden>
<section class="text-center">
<h5>Select your WOKA</h5>
<button class="selectCharacterButton" id="selectCharacterButtonLeft"> < </button>
<button class="selectCharacterButton" id="selectCharacterButtonRight"> > </button>
</section>
<section class="action">
<a herf="#" id="selectCompanionSceneFormBack">No companion</a>
<button type="submit" id="selectCompanionSceneFormSubmit">Continue</button>
</section>
</form>

View File

@ -1,9 +1,6 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#gameMenu main{
margin-top: 15px;
#gameMenu {
pointer-events: auto;
}
#gameMenu button {
background-color: black;
@ -14,17 +11,13 @@
#gameMenu section {
margin: 10px;
}
@media only screen and (max-height: 700px) {
#gameMenu main {
display: flex;
flex-direction: row;
align-items: flex-end;
flex-wrap: wrap;
margin-top: 0;
}
#gameMenu section{
margin: 2px;
}
section#socialLinks{
position: absolute;
margin-bottom: 0;
}
section#socialLinks img{
width: 32px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
</style>
@ -40,17 +33,18 @@
<section>
<button id="changeSkinButton">Edit skin</button>
</section>
<section>
<button id="changeCompanionButton">Edit companion</button>
</section>
<section>
<button id="editGameSettingsButton">Settings</button>
</section>
<section>
<button id="toggleFullscreen">Toggle fullscreen</button>
<section hidden>
<button id="sparkButton">Create map</button>
</section>
<section id="adminConsoleSection" hidden>
<button id="adminConsoleButton">Admin console</button>
</section>
<section id="socialLinks" hidden>
<a class="not-button" href="https://www.facebook.com/workadventurebytcm" target="_blank"><img class="not-button" src="/resources/objects/facebook-icon.png"/></a>
<a class="not-button" href="https://twitter.com/Workadventure_" target="_blank"><img class="not-button" src="/resources/objects/twitter-icon.png"/></a>
</section>
</main>
</div>

View File

@ -1,7 +1,4 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#menuIcon {
pointer-events: auto;
}
@ -9,21 +6,17 @@
background-color: black;
color: white;
border-radius: 7px;
padding: 2px 8px;
height: 28px;
width: 34px;
}
#menuIcon button img{
width: 14px;
padding-top: 0;
padding-top: 3px;
cursor: pointer;
}
#menuIcon section {
margin: 10px;
}
@media only screen and (max-height: 700px) {
#menuIcon section {
margin: 2px;
}
}
</style>
<main id="menuIcon" hidden>
<section>

View File

@ -1,14 +1,12 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#gameQuality {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
height: 257px;
margin: 20px auto 0;
width: 50vw;
max-width: 300px;
width: 298px;
pointer-events: auto;
}
#gameQuality .cautiousText {
font-size: 50%;
@ -36,7 +34,7 @@
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
width: 240px;
}
#gameQuality section {
margin: 10px;
@ -45,11 +43,12 @@
text-align: center;
}
#gameQuality button {
margin: 10px;
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
padding-bottom: 4px;
width: 60px;
}
#gameQuality button#gameQualityFormCancel {
background-color: #c7c7c700;
@ -59,7 +58,7 @@
<form id="gameQuality" hidden>
<section>
<h5>Game quality</h3>
<h3>Game quality</h3>
<p class="cautiousText">(Editing these settings will restart the game)</p>
<select id="select-game-quality">
<option value="120">High video quality (120 fps)</option>
@ -69,7 +68,7 @@
</select>
</section>
<section>
<h5>Video quality</h3>
<h3>Video quality</h3>
<select id="select-video-quality">
<option value="30">High video quality (30 fps)</option>
<option value="20">Medium video quality (20 fps, recommended)</option>

View File

@ -1,7 +1,4 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#gameReport {
background: #eceeee;
border: 1px solid #42464b;

View File

@ -1,14 +1,12 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#gameShare {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
margin: 20px auto 0;
width: 50vw;
max-width: 400px;
width: 298px;
height: 150px;
pointer-events: auto;
}
#gameShare h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
@ -43,7 +41,7 @@
margin: 0;
}
#gameShare button {
margin: 10px;
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
@ -69,7 +67,7 @@
}
#gameShare section p{
font-size: 8px;
margin: 0;
margin: 0px 70px;
}
#gameShare section p.err{
color: red;

View File

@ -1,17 +1,12 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#helpCameraSettings {
background: #eceeee;
border: 1px solid #42464b;
border-radius: 6px;
margin: 25px auto 0;
margin: 10px auto 0;
width: 400px;
max-height: calc(48vh - 50px);
max-width: 48vw;
overflow: hidden;
overflow-y: scroll;
height: 370px;
pointer-events: auto;
}
#helpCameraSettings h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
@ -26,6 +21,18 @@
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#helpCameraSettings input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
}
#helpCameraSettings section {
margin: 10px;
}
@ -34,7 +41,7 @@
margin: 0;
}
#helpCameraSettings button {
margin: 10px 4px;
margin-top: 10px;
background-color: black;
color: white;
border-radius: 7px;
@ -45,8 +52,9 @@
color: #292929;
}
#helpCameraSettings section a{
text-align: center;
font-size: 12px;
text-decoration: underline;
margin: 0 6px;
color: black;
}
#helpCameraSettings section h6,
@ -60,9 +68,6 @@
font-size: 8px;
margin: 0px 20px;
}
#helpCameraSettings section p a{
font-size: 8px;
}
#helpCameraSettings section p.err{
color: #ff0000;
}
@ -77,12 +82,6 @@
width: 200px;
margin-top: 10px;
}
@media only screen and (max-width: 800px),
only screen and (max-height: 600px) {
#helpCameraSettings{
overflow-y: scroll;
}
}
</style>
<form id="helpCameraSettings" hidden>
@ -98,12 +97,8 @@
<p>If you prefer to continue without allowing camera and microphone access, click on Continue</p>
<p id='browserHelpSetting'></p>
</section>
<!--<section class="text-center">
<p>If your problem persist, please contact us: <a id="mailto" href="mailto:workadventure@thecodingmachine.com?subject=Support camera and microphone settings" target="_blank"> workadventure@thecodingmachine.com</a>.</p>
</section>-->
</section>
<section class="action">
<a href="#" id="helpCameraSettingsFormRefresh">Refresh</a>
<button type="submit" id="helpCameraSettingsFormRefresh">Refresh</button>
<button type="submit" id="helpCameraSettingsFormContinue">Continue</button>
</section>
</form>

View File

@ -1,120 +0,0 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#loginScene {
background: #000000;
/*border: 1px solid #ebeeee;*/
border-radius: 6px;
margin: 20px auto 0;
width: 90%;
max-width: 200px;
color: #ebeeee;
max-height: 40vh;
overflow: hidden;
}
#loginScene h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#loginScene input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
text-align: center;
}
#loginScene section {
margin: 10px;
}
#loginScene section.action{
text-align: center;
margin: 0;
}
#loginScene button {
margin: 10px;
background-color: black;;
color: #ebeeee;
border-radius: 7px;
padding-bottom: 4px;
width: 100px;
}
#loginScene button#loginSceneFormCancel {
background-color: #c7c7c700;
color: #292929;
}
#loginScene section h6,
#loginScene section h5{
margin: 1px;
}
#loginScene section.text-center{
text-align: center;
}
#loginScene section a{
font-size: 8px;
text-decoration: underline;
color: #ebeeee;
}
#loginScene section a:hover{
font-weight: 700;
}
#loginScene section p{
text-align: left;
font-size: 8px;
margin: 10px 10px;
}
#loginScene section p.err{
color: red;
text-align: center;
}
#loginScene section p.info{
display: none;
text-align: center;
}
#loginScene section input#loginSceneLink{
background-color: #a1a3a3;
}
#loginScene section img{
width: 160px;
margin: 20px 0;
}
@media only screen and (max-width: 800px),
only screen and (max-height: 600px) {
#loginScene{
overflow-y: scroll;
}
}
</style>
<form id="loginScene" hidden>
<section class="text-center">
<img src="resources/logos/logo.png">
</section>
<section class="text-center">
<h5>Enter your name</h5>
<p class="info">9 chars maximum</p>
<p class="err" id="errorLoginScene"></p>
</section>
<section>
<input type="text" name="email" id="loginSceneName">
<p>By continuing, you are agreeing our <a href="https://workadventu.re/terms-of-use" target="_blank">terms of use</a>, <a href="https://workadventu.re/privacy-policy" target="_blank">privacy policy</a> and <a href="https://workadventu.re/cookie-policy" target="_blank">cookie policy</a>.</p>
</section>
<section class="action">
<button type="submit" id="loginSceneFormSubmit">Continue</button>
</section>
</form>

View File

@ -1,142 +0,0 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#selectCharacterScene {
background: #0000;
/*border: 1px solid #ebeeee;*/
border-radius: 6px;
margin: 10px auto 0;
color: #ebeeee;
max-height: 48vh;
max-width: 300px;
width: 40vw;
overflow: hidden;
}
#selectCharacterScene h1 {
background-image: linear-gradient(top, #f1f3f3, #d4dae0);
border-bottom: 1px solid #a6abaf;
border-radius: 6px 6px 0 0;
box-sizing: border-box;
color: #727678;
display: block;
height: 43px;
padding-top: 10px;
margin: 0;
text-align: center;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2), 0 1px 0 #fff;
}
#selectCharacterScene input {
font-size: 70%;
background: linear-gradient(top, #d6d7d7, #dee0e0);
border: 1px solid #a1a3a3;
border-radius: 4px;
box-shadow: 0 1px #fff;
box-sizing: border-box;
color: #696969;
height: 30px;
transition: box-shadow 0.3s;
width: 100%;
text-align: center;
}
#selectCharacterScene section {
margin: 10px;
}
#selectCharacterScene section.action {
text-align: center;
margin: 0;
margin-top: 28vh;
}
#selectCharacterScene button {
margin: 10px 0px;
background-color: black;;
color: #ebeeee;
border-radius: 7px;
padding-bottom: 4px;
width: 100px;
}
#selectCharacterScene button#selectCharacterSceneFormCancel {
background-color: #aca6a600;
color: #292929;
}
#selectCharacterScene section h6,
#selectCharacterScene section h5{
margin: 1px;
}
#selectCharacterScene section.text-center{
text-align: center;
}
#selectCharacterScene section a{
font-size: 8px;
text-decoration: underline;
color: #ebeeee;
}
#selectCharacterScene section a:hover{
font-weight: 700;
}
#selectCharacterScene section p{
text-align: left;
font-size: 8px;
margin: 10px 10px;
}
#selectCharacterScene section p.err{
color: red;
text-align: center;
}
#selectCharacterScene section p.info{
display: none;
text-align: center;
}
#selectCharacterScene section input#selectCharacterSceneLink{
background-color: #a1a3a3;
}
#selectCharacterScene section img{
width: 160px;
margin: 20px 0;
}
#selectCharacterScene section button.selectCharacterButton{
position: absolute;
top: 20vh;
margin: 0;
width: 25px;
}
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonLeft{
display: none;
left: 1vw;
}
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonRight{
display: none;
right: 1vw;
}
#selectCharacterScene section button#selectCharacterSceneFormCustomYourOwnSubmit{
font-size: 14px;
width: auto;
margin-top: -2px;
background-color: #ffd700;
color: black;
}
@media only screen and (max-width: 800px),
only screen and (max-height: 600px) {
#selectCharacterScene{
overflow-y: scroll;
}
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonRight{
display: block;
}
#selectCharacterScene section button.selectCharacterButton#selectCharacterButtonLeft{
display: block;
}
}
</style>
<form id="selectCharacterScene" hidden>
<section class="text-center">
<h5>Select your WOKA</h5>
<button class="selectCharacterButton" id="selectCharacterButtonLeft"> < </button>
<button class="selectCharacterButton" id="selectCharacterButtonRight"> > </button>
</section>
<section class="action">
<button type="submit" id="selectCharacterSceneFormSubmit">Continue</button>
<button type="submit" id="selectCharacterSceneFormCustomYourOwnSubmit">Custom your WOKA</button>
</section>
</form>

View File

@ -1,7 +1,4 @@
<style>
*{
font-family: PixelFont-7,monospace!important;
}
#warningMain {
border-radius: 5px;
height: 100px;

View File

Before

Width:  |  Height:  |  Size: 979 B

After

Width:  |  Height:  |  Size: 979 B

View File

Before

Width:  |  Height:  |  Size: 937 B

After

Width:  |  Height:  |  Size: 937 B

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg id="i-fullscreen-exit" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none" stroke="#FFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M4 12 L12 12 12 4 M20 4 L20 12 28 12 M4 20 L12 20 12 28 M28 20 L20 20 20 28" />
</svg>

Before

Width:  |  Height:  |  Size: 329 B

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg id="i-fullscreen" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none" stroke="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M4 12 L4 4 12 4 M20 4 L28 4 28 12 M4 20 L4 28 12 28 M28 20 L28 28 20 28" />
</svg>

Before

Width:  |  Height:  |  Size: 322 B

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="196" height="196" style="enable-background:new 0 0 196 196;">
<circle cx="98" cy="98" r="98" fill="#808080"/>
<circle cx="98" cy="98" r="78" fill="#fff"/>
<circle cx="98" cy="98" r="55" fill="#808080"/>
<circle cx="98" cy="98" r="30" fill="#fff"/>
<rect width="31" height="25" x="123" y="85" fill="#fff"/>
<rect width="20.5" height="100" transform="matrix(1,1,-1,1,137.75,37.75)" fill="#fff"/>
<rect width="17.5" height="120" transform="matrix(1,1,-1,1,149.25,29.25)" fill="#808080"/>
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="196" height="196" style="enable-background:new 0 0 196 196;">
<circle cx="98" cy="98" r="98" fill="#808080"/>
<circle cx="98" cy="98" r="78" fill="#fff"/>
<circle cx="98" cy="98" r="55" fill="#808080"/>
<circle cx="98" cy="98" r="30" fill="#fff"/>
<rect width="31" height="25" x="123" y="85" fill="#fff"/>
<rect width="20.5" height="100" transform="matrix(1,1,-1,1,137.75,37.75)" fill="#fff"/>
<rect width="17.5" height="120" transform="matrix(1,1,-1,1,149.25,29.25)" fill="#808080"/>
</svg>

Before

Width:  |  Height:  |  Size: 577 B

After

Width:  |  Height:  |  Size: 586 B

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="197" height="197" style="enable-background:new 0 0 197 197;">
<circle cx="98" cy="98" r="88" fill="#fff" stroke="#0FAD2C" stroke-width="20"/>
<path d="M152.5,111.4a55.3,55.3 0 1 1 0-25h-26a30,30 0 1 0 0,25z" fill="#0FAD2C"/>
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="197" height="197" style="enable-background:new 0 0 197 197;">
<circle cx="98" cy="98" r="88" fill="#fff" stroke="#0FAD2C" stroke-width="20"/>
<path d="M152.5,111.4a55.3,55.3 0 1 1 0-25h-26a30,30 0 1 0 0,25z" fill="#0FAD2C"/>
</svg>

Before

Width:  |  Height:  |  Size: 317 B

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1 +1,44 @@
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 469.33 426.67"><defs><style>.cls-1{fill:#fff;}</style></defs><path class="cls-1" d="M426.67,21.33h-384A42.66,42.66,0,0,0,0,64V320a42.66,42.66,0,0,0,42.67,42.67H192v42.66H149.33V448H320V405.33H277.33V362.67H426.67A42.66,42.66,0,0,0,469.33,320V64A42.66,42.66,0,0,0,426.67,21.33Zm0,298.67h-384V64h384Z" transform="translate(0 -21.33)"/><path class="cls-1" d="M267.2,127.15V86.26a8.14,8.14,0,0,1,14.18-5.44l73.2,81.34a8.12,8.12,0,0,1,.25,10.57l-73.2,89.47a8.14,8.14,0,0,1-14.43-5.13V216.54c-64.25,2.09-104.35,29.55-122.42,83.77a8.13,8.13,0,0,1-15.84-2.58C128.94,202,186.59,131.59,267.2,127.15Zm8.14,73a8.13,8.13,0,0,1,8.13,8.14v26l54.36-66.44-54.36-60.39v27.6a8.13,8.13,0,0,1-8.13,8.14c-63.93,0-111.77,44.24-125.87,111.73C175.65,218.53,217.8,200.13,275.34,200.13Z" transform="translate(0 -21.33)"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 480" style="enable-background:new 0 0 512 480;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M411.1,384.2c-12.2,0-24.3,0-36.5,0C259.6,257.6,144.5,130.9,29.5,4.3c11.4-0.8,22.9-1.6,34.3-2.4
C179.6,129.3,295.3,256.8,411.1,384.2z"/>
<g>
<path class="st0" d="M352,152.5c-8.8-8.7-34.2-31.6-74.5-38.1c-32.3-5.2-58.1,2.7-70,7.3C170.9,81.5,134.3,41.3,97.8,1
C220.4,1,343,1,465.6,1C427.7,51.5,389.8,102,352,152.5z"/>
<path class="st0" d="M511.5,338.3c0,4.7-0.8,12.2-4.7,20.2c-1.2,2.4-3.4,6.3-7.1,10.5c-4,4.4-7.9,7.1-10.2,8.5
c-5.6,3.5-10.7,4.9-13.5,5.6c-3.8,0.9-6.7,1-10.2,1.2c-3.6,0.2-5.3,0-13.1,0c-3,0-5.4,0-7,0C414.5,307,383.2,229.8,352,152.5
C402.9,62.7,448.7,1,465.6,1c7.5,0,14.3,2.3,14.3,2.3c14.5,4.8,22.3,15.8,23.6,17.8c7.4,10.8,8,21.7,8,25.9
C511.5,144.1,511.5,241.2,511.5,338.3z"/>
<path class="st0" d="M312.5,192c-5.2-5.2-15.6-14.1-31.4-19.4c-12.8-4.2-24-4.3-30.9-3.8c-6.2-7.1-12.4-14.2-18.6-21.3
c10.3-2.4,36.5-6.8,65.1,5.3c15.3,6.5,26.1,15.5,32.7,22.2C323.7,180.8,318.1,186.4,312.5,192z"/>
<path class="st0" d="M329.4,175.1c38.8,69.7,77.6,139.4,116.4,209.1c-50.3-55.4-100.6-110.8-151-166.2c6.9,2.9,14.9,0.7,19.2-5.2
c4.6-6.2,4-15.1-1.6-20.8C318.1,186.4,323.7,180.8,329.4,175.1z"/>
<path class="st0" d="M445.8,384.2L445.8,384.2c-38.8-69.7-77.6-139.4-116.4-209.1c5.3,4.9,12.9,6,18.9,2.7
c7.8-4.2,8.3-13.4,8.3-13.8C386.4,237.4,416.1,310.8,445.8,384.2z"/>
</g>
<path class="st0" d="M162.2,150.4C108.3,213,54.4,275.7,0.5,338.3c0-97.1,0-194.3,0-291.4c0-4,0.6-15.1,8.1-26
C16,10.2,25.7,5.8,29.5,4.3C73.7,53,118,101.7,162.2,150.4z"/>
<path class="st0" d="M199.5,192c-5.3-6-10.6-12-15.8-18C122.6,228.8,61.6,283.6,0.5,338.3c0,4.1,0.6,15.5,8.6,26.7
c1.7,2.4,9.6,12.9,24.1,17.2c5.3,1.6,10,1.9,13.1,1.9C97.5,320.2,148.5,256.1,199.5,192z"/>
<path class="st0" d="M84.7,384.2c-12.7,0-25.5,0-38.2,0c58.2-56.2,116.5-112.5,174.7-168.7c8.3,9.1,16.6,18.2,24.9,27.2
c-2.2,1.1-5.5,3-8.4,6.4c-3.1,3.7-4.2,7.3-4.4,7.8C231.3,262.4,194.5,295.2,84.7,384.2z"/>
<path class="st0" d="M46.4,384.2c-15.3-15.3-30.6-30.6-45.9-45.9C52.8,277.5,105.1,216.8,157.4,156c-3.7,6.7-2.2,15.1,3.5,20
c5.4,4.6,13.3,5.1,19.4,1C135.7,246.1,91.1,315.1,46.4,384.2z"/>
<path class="st0" d="M49.6,384.3c-1.1,0-2.1,0-3.2-0.1c50.1-62.9,100.2-125.8,150.3-188.7c-3.5,6.6-2.1,14.8,3.4,19.7
c5.8,5.2,14.8,5.3,21,0.3C164,271.7,106.8,328,49.6,384.3z"/>
<path class="st0" d="M374.6,384.2c-96.6,0-193.3,0-289.9,0C16.5,308,1.3,223,28.5,194.5C57,164.7,150.6,177,233.3,256.9
c-3.9,11.8,2,24.8,13.3,29.6c11,4.7,24,0.4,30.2-10C309.3,312.4,342,348.3,374.6,384.2z"/>
<path class="st0" d="M219.7,226.7"/>
<path class="st0" d="M368.9,480c-74.9,0-149.8,0-224.7,0c-8.8,0-16-7.2-16-16c0-8.8,7.2-16,16-16c74.9,0,149.9,0,224.8,0.1
c8.3,0.7,14.7,7.6,14.7,15.9C383.7,472.3,377.2,479.3,368.9,480z"/>
<rect x="208.1" y="384.2" class="st0" width="31.9" height="63.9"/>
<rect x="272" y="384.2" class="st0" width="32" height="63.9"/>
<path class="st0" d="M410.3,395.5"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 884 B

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -1,24 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 469.3 469.3" style="enable-background:new 0 0 469.3 469.3;" xml:space="preserve">
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 480" style="enable-background:new 0 0 512 480;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{opacity:0.9;fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;stroke-miterlimit:10;enable-background:new ;}
</style>
<g>
<g>
<path class="st0" d="M426.7,21.3h-384C19.1,21.3,0,40.4,0,64v256c0,23.6,19.1,42.7,42.7,42.7H192v42.7h-42.7V448H320v-42.7h-42.7
v-42.7h149.3c23.6,0,42.7-19.1,42.7-42.7V64C469.3,40.4,450.2,21.3,426.7,21.3z M426.7,320h-384V64h384V320z"/>
</g>
</g>
<g>
<g>
<path class="st0" d="M267.2,127.2V86.3c0-4.5,3.6-8.1,8.1-8.1c2.3,0,4.5,1,6,2.7l73.2,81.3c2.7,3,2.8,7.5,0.3,10.6l-73.2,89.5
c-2.8,3.5-8,4-11.4,1.1c-1.9-1.5-3-3.8-3-6.3v-40.5c-64.3,2.1-104.4,29.6-122.4,83.8c-1.1,3.3-4.2,5.6-7.7,5.6
c-0.4,0-0.9,0-1.3-0.1c-3.9-0.6-6.8-4-6.8-8C128.9,202,186.6,131.6,267.2,127.2z M275.3,200.1c4.5,0,8.1,3.6,8.1,8.1v26l54.4-66.4
l-54.4-60.4V135c0,4.5-3.6,8.1-8.1,8.1c-63.9,0-111.8,44.2-125.9,111.7C175.6,218.5,217.8,200.1,275.3,200.1z"/>
</g>
</g>
<path class="st1" d="M13.4,42.7C153.6,142.3,293.8,241.9,434,341.5"/>
<path class="st0" d="M466,0H46C20.6,0,0,20.6,0,46v292c0,25.4,20.6,46,46,46h162v64h-64c-8.8,0-16,7.2-16,16s7.2,16,16,16h224
c8.8,0,16-7.2,16-16s-7.2-16-16-16h-64v-64h162c25.4,0,46-20.6,46-46V46C512,20.6,491.4,0,466,0z M232,264c0-13.3,10.7-24,24-24
c13.3,0,24,10.7,24,24s-10.7,24-24,24C242.7,288,232,277.3,232,264z M272,448h-32v-64h32V448z M312.6,214.1
c-6.2,6.2-16.4,6.2-22.6,0c-18.7-18.8-49.1-18.8-67.9,0c0,0,0,0,0,0c-6.4,6.1-16.5,5.8-22.6-0.6c-5.9-6.2-5.9-15.9,0-22
c31.2-31.2,81.9-31.2,113.1,0c0,0,0,0,0,0C318.8,197.7,318.8,207.8,312.6,214.1z M352.2,174.5c-6.2,6.2-16.4,6.3-22.6,0c0,0,0,0,0,0
c-40.6-40.6-106.4-40.6-147.1,0c-6.2,6.3-16.4,6.3-22.6,0c-6.3-6.2-6.3-16.4,0-22.6c53.1-53.1,139.2-53.1,192.3,0c0,0,0,0,0,0
C358.4,158.1,358.4,168.2,352.2,174.5C352.2,174.5,352.2,174.5,352.2,174.5L352.2,174.5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,170 @@
/* A potentially shared website could appear in an iframe in the cowebsite space. */
#cowebsite {
position: fixed;
transition: transform 0.5s;
background-color: white;
&.loading {
background-color: gray;
}
main {
iframe {
width: 100%;
height: 100%;
}
}
aside {
background: gray;
align-items: center;
display: flex;
img {
margin: 3px;
pointer-events: none;
height: 20px;
}
}
.top-right-btn{
position: absolute;
background: none;
border: none;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
img {
height: 20px;
background-color: rgba(0,0.0,0,0.3);
padding: 5px;
border-radius: 3px;
}
img:hover {
background-color: rgba(0,0,0,0.4);
}
}
}
@media (min-aspect-ratio: 1/1) {
#cowebsite {
right: 0;
top: 0;
width: 50%;
height: 100vh;
display: flex;
&.loading {
transform: translateX(90%);
}
&.hidden {
transform: translateX(100%);
}
main {
width: 100%;
}
aside {
width: 30px;
cursor: ew-resize;
img {
cursor: ew-resize;
transform: rotate(90deg);
}
}
.top-right-btn{
top: 10px;
right: -100px;
animation: right .2s ease;
img {
right: 15px;
}
}
#cowebsite-close {
right: -140px;
}
#cowebsite-fullscreen {
right: -100px;
}
}
#cowebsite:hover {
#cowebsite-close{
right: 10px;
}
#cowebsite-fullscreen{
right: 45px;
}
}
}
@media (max-aspect-ratio: 1/1) {
#cowebsite {
left: 0;
bottom: 0;
width: 100%;
height: 50%;
display: flex;
flex-direction: column;
&.loading {
transform: translateY(90%);
}
&.hidden {
transform: translateY(100%);
}
main {
height: 100%;
}
aside {
height: 30px;
cursor: ns-resize;
flex-direction: column;
img {
cursor: ns-resize;
}
}
.top-right-btn{
top: 10px;
right: -100px;
animation: right .2s ease;
img {
right: 15px;
}
}
#cowebsite-close {
right: -140px;
}
#cowebsite-fullscreen {
right: -100px;
}
}
#cowebsite:hover {
#cowebsite-close{
right: 10px;
}
#cowebsite-fullscreen{
right: 45px;
}
}
}

2
front/dist/resources/style/index.scss vendored Normal file
View File

@ -0,0 +1,2 @@
@import "cowebsite.scss";
@import "style.css";

View File

@ -36,32 +36,30 @@ body .message-info.warning{
background: #ffa500d6;
}
.video-container{
pointer-events: all;
position: relative;
transition: all 0.2s ease;
background-color: #00000099;
cursor: url('./images/cursor_pointer.png'), pointer;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
.video-container i{
position: absolute;
width: 100px;
height: 100px;
height: 65px;
left: calc(50% - 50px);
top: calc(50% - 50px);
background-color: black;
border-radius: 50%;
text-align: center;
padding-top: 32px;
padding-top: 35px;
font-size: 28px;
color: white;
overflow: hidden;
}
.video-container img{
position: absolute;
display: none;
width: 40px;
height: 40px;
width: 25px;
height: 25px;
left: 5px;
bottom: 5px;
padding: 10px;
@ -76,7 +74,7 @@ body .message-info.warning{
.video-container button.report{
display: block;
cursor: url('./images/cursor_pointer.png'), pointer;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
background: none;
background-color: rgba(0, 0, 0, 0);
border: none;
@ -99,7 +97,7 @@ body .message-info.warning{
}
.video-container button.report:hover {
width: 160px;
width: 150px;
}
.video-container button.report img{
@ -109,17 +107,15 @@ body .message-info.warning{
left: 5px;
margin: 0;
padding: 0;
cursor: url('./images/cursor_pointer.png'), pointer;
width: 25px;
height: 25px;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
.video-container button.report span{
position: absolute;
bottom: 6px;
bottom: 8px;
left: 36px;
color: white;
font-size: 16px;
cursor: url('./images/cursor_pointer.png'), pointer;
cursor: url('/resources/logos/cursor_pointer.png'), pointer;
}
.video-container img.active {
display: block !important;
@ -127,7 +123,6 @@ body .message-info.warning{
.video-container video{
height: 100%;
cursor: url('./images/cursor_pointer.png'), pointer;
}
.video-container video:focus{
@ -141,14 +136,8 @@ body .message-info.warning{
#div-myCamVideo {
position: absolute;
right: 15px;
bottom: 30px;
bottom: 15px;
border-radius: 15px 15px 15px 15px;
max-height: 20%;
transition: right 350ms;
}
#div-myCamVideo.hide {
right: -20vw;
}
video#myCamVideo{
@ -160,56 +149,14 @@ video#myCamVideo{
/*height: 113px;*/
}
.sound-progress{
display: none;
position: absolute;
right: 14px;
top: calc(50% - 5px);
}
.sound-progress.active{
display: table-column;
}
.sound-progress span{
position: absolute;
color: black;
background-color: #00000020;
width: 5px;
height: 5px;
border-radius: 50%;
}
.sound-progress span.active{
background-color: #00c3ff66
}
.sound-progress span:nth-child(1){
top: calc(50% + 20px);
}
.sound-progress span:nth-child(2){
top: calc(50% + 10px);
}
.sound-progress span:nth-child(3){
top: calc(50% - 0px);
}
.sound-progress span:nth-child(4){
top: calc(50% - 10px);
}
.sound-progress span:nth-child(5){
top: calc(50% - 20px);
}
.btn-cam-action {
z-index: 1;
pointer-events: all;
position: absolute;
display: inline-flex;
bottom: 10px;
right: 15px;
width: 180px;
height: 40px;
text-align: center;
align-content: center;
align-items: center;
justify-content: flex-end;
justify-items: center;
bottom: 0px;
right: 0px;
width: 450px;
height: 150px;
}
/* override copyright btn*/
@ -218,16 +165,7 @@ video#myCamVideo{
bottom: 0px;
left: 350px;
width: 150px;
height: 50px;
}
.btn-cam-action#btn-copy-action div img{
height: 30px;
width: 30px;
top: calc(48px - 42px);
left: calc(48px - 47px);
position: relative;
cursor: pointer;
height: 150px;
}
.copyright {
@ -239,16 +177,16 @@ video#myCamVideo{
/*btn animation*/
.btn-cam-action div{
cursor: pointer;
/*position: absolute;*/
position: absolute;
border: solid 0px black;
width: 44px;
height: 44px;
background: #666;
box-shadow: 2px 2px 24px #444;
border-radius: 48px;
transform: translateY(20px);
transform: translateY(40px);
transition-timing-function: ease-in-out;
margin: 0 4%;
bottom: 20px;
}
.btn-cam-action div.disabled {
background: #d75555;
@ -262,34 +200,19 @@ video#myCamVideo{
.btn-cam-action div:hover{
background: #407cf7;
box-shadow: 4px 4px 48px #666;
transition: 120ms;
transition: 280ms;
}
.btn-micro{
pointer-events: auto;
transition: all .3s;
/*right: 44px;*/
}
.btn-video{
pointer-events: auto;
transition: all .25s;
/*right: 134px;*/
}
.btn-monitor{
pointer-events: auto;
transition: all .2s;
/*right: 224px;*/
}
.btn-monitor.hide {
transform: translateY(60px);
}
.btn-cam-action:hover .btn-monitor.hide{
transform: translateY(60px);
}
.btn-copy{
pointer-events: auto;
transition: all .3s;
right: 44px;
opacity: 1;
}
.btn-video{
transition: all .25s;
right: 134px;
}
.btn-monitor{
transition: all .2s;
right: 224px;
}
.btn-copy{
transition: all .3s;
@ -309,8 +232,8 @@ video#myCamVideo{
.connecting-spinner {
/*display: inline-block;*/
position: absolute;
left: calc(50% - 62px);
top: calc(50% - 62px);
left: calc(50% - 68px);
top: calc(50% - 68px);
width: 130px;
@ -388,8 +311,6 @@ video#myCamVideo{
#myCamVideoSetup {
width: 100%;
height: 100%;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.webrtcsetup.active{
display: block;
@ -425,7 +346,25 @@ body {
max-height: 25%;
}
}
@media (max-aspect-ratio: 1/1) {
.game-overlay {
flex-direction: column;
}
.sidebar {
flex-direction: row;
align-items: flex-end;
}
.sidebar > div {
max-width: 21%;
}
.sidebar > div:hover {
max-width: 25%;
}
}
#game {
@ -474,19 +413,6 @@ body {
visibility: hidden;
display: none;
}
#audioplayer_volume_icon_playing.low #audioplayer_volume_icon_playing_high {
visibility: hidden;
display: none;
}
#audioplayer_volume_icon_playing.low #audioplayer_volume_icon_playing_mid {
visibility: hidden;
display: none;
}
#audioplayer_volume_icon_playing.mid #audioplayer_volume_icon_playing_high {
visibility: hidden;
display: none;
}
#audioplayerctrl > #audioplayer_volume {
width: 100%;
background-color: rgba(0,0,0,0.5);
@ -591,54 +517,21 @@ input[type=range]:focus::-ms-fill-upper {
background: #FFFFFF;
}
#videoplayerctrl {
position: fixed;
width: 25%;
top: 0;
right: 75%;
transform: translate(50%, 0);
padding: 0.3rem 0.5rem;
color: white;
transition: transform 0.5s;
}
/*
* sollte eigentlich in den aspect-ratio teil ..
*/
#videoplayerctrl.loading {
transform: translate(50%, -90%);
}
#videoplayerctrl.hidden {
transform: translate(50%, -100%);
}
#videoplayerelem {
width: 100%;
}
.game-overlay {
display: none;
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
}
.game-overlay + div {
pointer-events: none;
}
.game-overlay + div > div {
pointer-events: auto;
}
.game-overlay.active {
display: flex;
}
.game-overlay video {
width: 100%;
width: 100%
}
.main-section {
@ -654,7 +547,6 @@ input[type=range]:focus::-ms-fill-upper {
flex-basis: 96%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s;
cursor: pointer;
pointer-events: auto;
/*flex-shrink: 2;*/
}
@ -673,20 +565,14 @@ input[type=range]:focus::-ms-fill-upper {
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s;
cursor: pointer;
border-radius: 15px 15px 15px 15px;
pointer-events: auto;
}
.sidebar > div:hover {
margin: 0%;
}
.sidebar > div video {
cursor: url('./images/cursor_pointer.png'), pointer;
}
/* Let's make sure videos are vertically centered if they need to be cropped */
.media-container {
pointer-events: all;
display: flex;
justify-content: center;
flex-direction: column;
@ -695,7 +581,7 @@ input[type=range]:focus::-ms-fill-upper {
}
.chat-mode {
display: grid;
display: inline-grid;
width: 100%;
padding: 1%;
align-items: flex-start;
@ -713,19 +599,19 @@ input[type=range]:focus::-ms-fill-upper {
}
.chat-mode.one-col {
grid-template-columns: repeat(1, 1fr);
grid-template-columns: auto;
}
.chat-mode.two-col {
grid-template-columns: repeat(2, 1fr);
grid-template-columns: auto auto;
}
.chat-mode.three-col {
grid-template-columns: repeat(3, 1fr);
grid-template-columns: auto auto auto;
}
.chat-mode.four-col {
grid-template-columns: repeat(4, 1fr);
grid-template-columns: auto auto auto auto;
}
/*CONSOLE*/
@ -757,7 +643,7 @@ input[type=range]:focus::-ms-fill-upper {
position: relative;
padding: 20px;
margin: 20px;
overflow: auto;
overflow: scroll;
max-height: 400px;
}
@ -927,7 +813,7 @@ input[type=range]:focus::-ms-fill-upper {
right: 0;
}
.main-container .audio-playing img{
/*width: 30px;*/
width: 30px;
border-radius: 50%;
background-color: #ffda01;
padding: 10px;
@ -935,7 +821,6 @@ input[type=range]:focus::-ms-fill-upper {
.main-container .audio-playing p{
color: white;
margin-left: 10px;
margin-top: 14px;
}
/* VIDEO QUALITY */
@ -985,10 +870,10 @@ div.modal-report-user{
.modal-report-user img{
position: absolute;
height: 24px;
width: 24px;
height: 50px;
width: 50px;
z-index: 999;
left: calc(50% - 12px);
left: calc(50% - 25px);
top: 10px;
}
@ -1152,7 +1037,7 @@ div.modal-report-user{
background-color: #2d2d2dba;
right: 34px;
margin: 0px;
padding: 2px 0px;
padding: 6px 0px;
border-radius: 15px;
border: none;
color: white;
@ -1167,7 +1052,7 @@ div.modal-report-user{
.discussion .messages{
position: absolute;
height: calc(100% - 390px);
height: calc(100% - 360px);
overflow-x: hidden;
overflow-y: auto;
max-width: calc(100% - 40px);
@ -1208,18 +1093,18 @@ div.modal-report-user{
.discussion .send-message{
position: absolute;
bottom: 45px;
width: 200px;
width: 220px;
height: 26px;
margin-bottom: 10px;
}
.discussion .send-message input{
position: absolute;
width: calc(100% - 20px);
height: 30px;
width: calc(100% - 10px);
height: 20px;
background-color: #171717;
color: white;
border-radius: 10px;
border-radius: 15px;
border: none;
padding: 6px;
}
@ -1248,69 +1133,18 @@ div.action{
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
div.action.info,
div.action.warning,
div.action.danger{
transition: all 1s ease;
animation: mymove 1s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
div.action p.action-body{
cursor: url('./images/cursor_pointer.png'), pointer;
padding: 10px;
background-color: #2d2d2dba;
color: #fff;
font-size: 20px;
font-weight: 500;
font-size: 12px;
text-align: center;
max-width: 350px;
margin-left: calc(50% - 175px);
max-width: 150px;
margin-left: calc(50% - 75px);
border-radius: 15px;
}
div.action.warning p.action-body{
background-color: #ff9800eb;
color: #000;
}
div.action.danger p.action-body{
background-color: #da0000e3;
color: #000;
}
.popUpElement{
font-family: 'Press Start 2P';
text-align: left;
color: white;
}
.popUpElement div {
font-family: 'Press Start 2P';
font-size: 10px;
background-color: #727678;
}
.popUpElement button {
position: relative;
font-size: 10px;
border-image-repeat: revert;
margin-right: 5px;
}
.popUpElement .buttonContainer {
float: right;
background-color: inherit;
}
@keyframes mymove {
0% {bottom: 40px;}
50% {bottom: 30px;}
100% {bottom: 40px;}
}
#svelte-overlay {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}

View File

@ -1,149 +1,41 @@
{
"short_name": "WA",
"name": "WorkAdventure",
"icons": [
{
"src": "/static/images/favicons/apple-icon-57x57.png",
"sizes": "57x57",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-60x60.png",
"sizes": "60x60",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-76x76.png",
"sizes": "76x76",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-114x114.png",
"sizes": "114x114",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-120x120.png",
"sizes": "120x120",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-152x152.png",
"sizes": "152x152",
"type": "image\/png"
},
{
"src": "/static/images/favicons/apple-icon-180x180.png",
"sizes": "180x180",
"type": "image\/png"
},
{
"src": "/static/images/favicons/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "/static/images/favicons/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "/static/images/favicons/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "/static/images/favicons/favicon-16x16.png",
"sizes": "16x16",
"type": "image\/png",
"density": "1"
},
{
"src": "/static/images/favicons/favicon-32x32.png",
"sizes": "32x32",
"type": "image\/png",
"density": "1.5"
},
{
"src": "/static/images/favicons/favicon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "/static/images/favicons/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "1"
},
{
"src": "/static/images/favicons/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1"
},
{
"src": "/static/images/favicons/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "/static/images/favicons/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "/static/images/favicons/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "/static/images/favicons/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
],
"start_url": "/",
"background_color": "#000000",
"display_override": ["window-control-overlay", "minimal-ui"],
"display": "standalone",
"scope": "/",
"theme_color": "#000000",
"shortcuts": [
{
"name": "WorkAdventures",
"short_name": "WA",
"description": "WorkAdventure application",
"url": "/",
"icons": [{ "src": "/static/images/favicons/android-icon-192x192.png", "sizes": "192x192" }]
}
],
"description": "WorkAdventure application",
"screenshots": [],
"related_applications": [{
"platform": "web",
"url": "https://workadventu.re"
}, {
"platform": "play",
"url": "https://play.workadventu.re"
}]
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

View File

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

View File

@ -1 +0,0 @@
iframe_api.d.ts

View File

@ -1,27 +0,0 @@
<h1 align="center">WorkAdventure - IFrame API typings for Typescript</h1>
<p align="center">This package contains Typescript typings for <a href="https://workadventu.re/map-building/scripting">WorkAdventure's map scripting API</a></p>
<hr/>
[WorkAdventure](https://workadventu.re) comes with a scripting API. Using this API, you can add some intelligence to your map.
You use this API by loading an external script directly from WorkAdventure (at https://play.workadventu.re/iframe_api.js), or this script is loaded
for you if you are using the "script" property of a map.
This project contains Typescript typings for the `WA` object provided by this script.
## Usage
This package is only useful if you are using Typescript to script your WorkAdventure maps.
## Download & Installation
```shell
$ npm install @workadventure/iframe-api-typings
```
or
```shell
$ yarn add @workadventure/iframe-api-typings
```

View File

@ -1 +0,0 @@
// This file is voluntarily empty.

View File

@ -1,13 +0,0 @@
{
"name": "@workadventure/iframe-api-typings",
"version": "VERSION_PLACEHOLDER",
"description": "Typescript typings for WorkAdventure iFrame API",
"main": "iframe_api.js",
"types": "iframe_api.d.ts",
"repository": "https://github.com/thecodingmachine/workadventure/",
"author": "David Négrier <d.negrier@thecodingmachine.com>",
"license": "MIT",
"publishConfig": {
"access": "public"
}
}

View File

@ -1,7 +1,8 @@
import {HtmlUtils} from "../WebRtc/HtmlUtils";
import type {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import type {RoomConnection} from "../Connexion/RoomConnection";
import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
import {RoomConnection} from "../Connexion/RoomConnection";
import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
import {ADMIN_URL} from "../Enum/EnvironmentVariable";
import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService";
export const CLASS_CONSOLE_MESSAGE = 'main-console';
@ -161,42 +162,42 @@ export class ConsoleGlobalMessageManager {
this.divMessageConsole.appendChild(section);
(async () => {
try{
// Import quill
const {default: Quill}:any = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any
// Start loading CSS
const cssPromise = ConsoleGlobalMessageManager.loadCss();
// Import quill
const Quill:any = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any
// Wait for CSS to be loaded
await cssPromise;
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{'header': 1}, {'header': 2}], // custom button values
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
[{'direction': 'rtl'}], // text direction
[{'header': 1}, {'header': 2}], // custom button values
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
[{'direction': 'rtl'}], // text direction
[{'size': ['small', false, 'large', 'huge']}], // custom dropdown
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'size': ['small', false, 'large', 'huge']}], // custom dropdown
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'color': []}, {'background': []}], // dropdown with defaults from theme
[{'font': []}],
[{'align': []}],
[{'color': []}, {'background': []}], // dropdown with defaults from theme
[{'font': []}],
[{'align': []}],
['clean'],
['clean'],
['link', 'image', 'video']
// remove formatting button
];
['link', 'image', 'video']
// remove formatting button
];
new Quill(`#${INPUT_CONSOLE_MESSAGE}`, {
theme: 'snow',
modules: {
toolbar: toolbarOptions
},
});
}catch(err){
console.error(err);
}
new Quill(`#${INPUT_CONSOLE_MESSAGE}`, {
theme: 'snow',
modules: {
toolbar: toolbarOptions
},
});
})();
}
@ -262,6 +263,27 @@ export class ConsoleGlobalMessageManager {
this.divMessageConsole.appendChild(section);
}
private static loadCss(): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (ConsoleGlobalMessageManager.cssLoaded) {
resolve();
return;
}
const fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", "https://cdn.quilljs.com/1.3.7/quill.snow.css");
document.getElementsByTagName("head")[0].appendChild(fileref);
ConsoleGlobalMessageManager.cssLoaded = true;
fileref.onload = () => {
resolve();
}
fileref.onerror = () => {
reject();
}
});
}
sendMessage(){
const inputType = HtmlUtils.getElementByIdOrFail<HTMLInputElement>(INPUT_TYPE_CONSOLE);
if(AUDIO_TYPE !== inputType.value && MESSAGE_TYPE !== inputType.value){
@ -314,7 +336,7 @@ export class ConsoleGlobalMessageManager {
}
active(){
this.userInputManager.disableControls();
this.userInputManager.clearAllKeys();
this.divMainConsole.style.top = '0';
this.activeConsole = true;
}

View File

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

View File

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

View File

@ -1,11 +0,0 @@
import * as tg from "generic-type-guard";
export const isButtonClickedEvent =
new tg.IsInterface().withProperties({
popupId: tg.isNumber,
buttonId: tg.isNumber,
}).get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property.
*/
export type ButtonClickedEvent = tg.GuardedType<typeof isButtonClickedEvent>;

View File

@ -1,11 +0,0 @@
import * as tg from "generic-type-guard";
export const isChatEvent =
new tg.IsInterface().withProperties({
message: tg.isString,
author: tg.isString,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type ChatEvent = tg.GuardedType<typeof isChatEvent>;

View File

@ -1,11 +0,0 @@
import * as tg from "generic-type-guard";
export const isClosePopupEvent =
new tg.IsInterface().withProperties({
popupId: tg.isNumber,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type ClosePopupEvent = tg.GuardedType<typeof isClosePopupEvent>;

View File

@ -1,10 +0,0 @@
import * as tg from "generic-type-guard";
export const isEnterLeaveEvent =
new tg.IsInterface().withProperties({
name: tg.isString,
}).get();
/**
* A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property.
*/
export type EnterLeaveEvent = tg.GuardedType<typeof isEnterLeaveEvent>;

View File

@ -1,13 +0,0 @@
import * as tg from "generic-type-guard";
export const isGoToPageEvent =
new tg.IsInterface().withProperties({
url: tg.isString,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type GoToPageEvent = tg.GuardedType<typeof isGoToPageEvent>;

View File

@ -1,60 +0,0 @@
import type { ButtonClickedEvent } from './ButtonClickedEvent';
import type { ChatEvent } from './ChatEvent';
import type { ClosePopupEvent } from './ClosePopupEvent';
import type { EnterLeaveEvent } from './EnterLeaveEvent';
import type { GoToPageEvent } from './GoToPageEvent';
import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
import type { OpenPopupEvent } from './OpenPopupEvent';
import type { OpenTabEvent } from './OpenTabEvent';
import type { UserInputChatEvent } from './UserInputChatEvent';
import type {LoadSoundEvent} from "./LoadSoundEvent";
import type {PlaySoundEvent} from "./PlaySoundEvent";
export interface TypedMessageEvent<T> extends MessageEvent {
data: T
}
export type IframeEventMap = {
//getState: GameStateEvent,
// updateTile: UpdateTileEvent
chat: ChatEvent,
openPopup: OpenPopupEvent
closePopup: ClosePopupEvent
openTab: OpenTabEvent
goToPage: GoToPageEvent
openCoWebSite: OpenCoWebSiteEvent
closeCoWebSite: null
disablePlayerControls: null
restorePlayerControls: null
displayBubble: null
removeBubble: null
loadSound: LoadSoundEvent
playSound: PlaySoundEvent
stopSound: null
}
export interface IframeEvent<T extends keyof IframeEventMap> {
type: T;
data: IframeEventMap[T];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeEventWrapper = (event: any): event is IframeEvent<keyof IframeEventMap> => typeof event.type === 'string';
export interface IframeResponseEventMap {
userInputChat: UserInputChatEvent
enterEvent: EnterLeaveEvent
leaveEvent: EnterLeaveEvent
buttonClickedEvent: ButtonClickedEvent
// gameState: GameStateEvent
}
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
type: T;
data: IframeResponseEventMap[T];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeResponseEventWrapper = (event: { type?: string }): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === 'string';

View File

@ -1,11 +0,0 @@
import * as tg from "generic-type-guard";
export const isLoadSoundEvent =
new tg.IsInterface().withProperties({
url: tg.isString,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type LoadSoundEvent = tg.GuardedType<typeof isLoadSoundEvent>;

View File

@ -1,13 +0,0 @@
import * as tg from "generic-type-guard";
export const isOpenCoWebsite =
new tg.IsInterface().withProperties({
url: tg.isString,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenCoWebSiteEvent = tg.GuardedType<typeof isOpenCoWebsite>;

View File

@ -1,20 +0,0 @@
import * as tg from "generic-type-guard";
const isButtonDescriptor =
new tg.IsInterface().withProperties({
label: tg.isString,
className: tg.isOptional(tg.isString)
}).get();
export const isOpenPopupEvent =
new tg.IsInterface().withProperties({
popupId: tg.isNumber,
targetObject: tg.isString,
message: tg.isString,
buttons: tg.isArray(isButtonDescriptor)
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenPopupEvent = tg.GuardedType<typeof isOpenPopupEvent>;

View File

@ -1,13 +0,0 @@
import * as tg from "generic-type-guard";
export const isOpenTabEvent =
new tg.IsInterface().withProperties({
url: tg.isString,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type OpenTabEvent = tg.GuardedType<typeof isOpenTabEvent>;

View File

@ -1,24 +0,0 @@
import * as tg from "generic-type-guard";
const isSoundConfig =
new tg.IsInterface().withProperties({
volume: tg.isOptional(tg.isNumber),
loop: tg.isOptional(tg.isBoolean),
mute: tg.isOptional(tg.isBoolean),
rate: tg.isOptional(tg.isNumber),
detune: tg.isOptional(tg.isNumber),
seek: tg.isOptional(tg.isNumber),
delay: tg.isOptional(tg.isNumber)
}).get();
export const isPlaySoundEvent =
new tg.IsInterface().withProperties({
url: tg.isString,
config : tg.isOptional(isSoundConfig),
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type PlaySoundEvent = tg.GuardedType<typeof isPlaySoundEvent>;

View File

@ -1,11 +0,0 @@
import * as tg from "generic-type-guard";
export const isStopSoundEvent =
new tg.IsInterface().withProperties({
url: tg.isString,
}).get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type StopSoundEvent = tg.GuardedType<typeof isStopSoundEvent>;

View File

@ -1,10 +0,0 @@
import * as tg from "generic-type-guard";
export const isUserInputChatEvent =
new tg.IsInterface().withProperties({
message: tg.isString,
}).get();
/**
* A message sent from the game to the iFrame when a user types a message in the chat.
*/
export type UserInputChatEvent = tg.GuardedType<typeof isUserInputChatEvent>;

Some files were not shown because too many files have changed in this diff Show More