Merge branch 'develop' of github.com:thecodingmachine/workadventure into fix/deploy-cleanup
# Conflicts: # front/Dockerfile
@ -5,3 +5,4 @@ JITSI_PRIVATE_MODE=false
|
|||||||
JITSI_ISS=
|
JITSI_ISS=
|
||||||
SECRET_JITSI_KEY=
|
SECRET_JITSI_KEY=
|
||||||
ADMIN_API_TOKEN=123
|
ADMIN_API_TOKEN=123
|
||||||
|
START_ROOM_URL=/_/global/maps.workadventure.localhost/Floor0/floor0.json
|
||||||
|
53
.github/workflows/build-and-deploy.yml
vendored
@ -102,29 +102,6 @@ jobs:
|
|||||||
tags: ${{ env.GITHUB_REF_SLUG }}
|
tags: ${{ env.GITHUB_REF_SLUG }}
|
||||||
add_git_labels: true
|
add_git_labels: true
|
||||||
|
|
||||||
build-website:
|
|
||||||
|
|
||||||
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: website/Dockerfile
|
|
||||||
path: website/
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
repository: thecodingmachine/workadventure-website
|
|
||||||
tags: ${{ env.GITHUB_REF_SLUG }}
|
|
||||||
add_git_labels: true
|
|
||||||
|
|
||||||
build-maps:
|
build-maps:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -156,7 +133,6 @@ jobs:
|
|||||||
- build-pusher
|
- build-pusher
|
||||||
- build-maps
|
- build-maps
|
||||||
- build-uploader
|
- build-uploader
|
||||||
- build-website
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -185,32 +161,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
msg: Environment deployed at https://${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
|
msg: Environment deployed at https://${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
|
||||||
check_for_duplicate_msg: true
|
check_for_duplicate_msg: true
|
||||||
|
|
||||||
- name: Run Cypress tests
|
|
||||||
uses: cypress-io/github-action@v2
|
|
||||||
if: ${{ env.GITHUB_REF_SLUG != 'master' }}
|
|
||||||
env:
|
|
||||||
CYPRESS_BASE_URL: https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
|
|
||||||
with:
|
|
||||||
env: host=play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com,port=80
|
|
||||||
spec: cypress/integration/spec.js
|
|
||||||
wait-on: https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com
|
|
||||||
working-directory: e2e
|
|
||||||
|
|
||||||
- name: Run Cypress tests in prod
|
|
||||||
uses: cypress-io/github-action@v2
|
|
||||||
if: ${{ env.GITHUB_REF_SLUG == 'master' }}
|
|
||||||
env:
|
|
||||||
CYPRESS_BASE_URL: https://play.workadventu.re
|
|
||||||
with:
|
|
||||||
env: host=play.workadventu.re
|
|
||||||
spec: cypress/integration/spec.js
|
|
||||||
wait-on: https://workadventu.re
|
|
||||||
working-directory: e2e
|
|
||||||
|
|
||||||
- name: "Upload the screenshot on test failure"
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
name: "screenshot"
|
|
||||||
path: "./e2e/cypress/screenshots/spec.js/WorkAdventureGame -- loads (failed).png"
|
|
||||||
|
47
.github/workflows/continuous_integration.yml
vendored
@ -39,6 +39,10 @@ jobs:
|
|||||||
run: yarn run proto && yarn run copy-to-front
|
run: yarn run proto && yarn run copy-to-front
|
||||||
working-directory: "messages"
|
working-directory: "messages"
|
||||||
|
|
||||||
|
- name: "Create index.html"
|
||||||
|
run: ./templater.sh
|
||||||
|
working-directory: "front"
|
||||||
|
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
run: yarn run build
|
run: yarn run build
|
||||||
env:
|
env:
|
||||||
@ -53,6 +57,49 @@ jobs:
|
|||||||
run: yarn test
|
run: yarn test
|
||||||
working-directory: "front"
|
working-directory: "front"
|
||||||
|
|
||||||
|
continuous-integration-pusher:
|
||||||
|
name: "Continuous Integration Pusher"
|
||||||
|
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v2.0.0"
|
||||||
|
|
||||||
|
- name: "Setup NodeJS"
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '12.x'
|
||||||
|
|
||||||
|
- name: Install Protoc
|
||||||
|
uses: arduino/setup-protoc@v1
|
||||||
|
with:
|
||||||
|
version: '3.x'
|
||||||
|
|
||||||
|
- name: "Install dependencies"
|
||||||
|
run: yarn install
|
||||||
|
working-directory: "pusher"
|
||||||
|
|
||||||
|
- name: "Install messages dependencies"
|
||||||
|
run: yarn install
|
||||||
|
working-directory: "messages"
|
||||||
|
|
||||||
|
- name: "Build proto messages"
|
||||||
|
run: yarn run proto && yarn run copy-to-pusher
|
||||||
|
working-directory: "messages"
|
||||||
|
|
||||||
|
- name: "Build"
|
||||||
|
run: yarn run tsc
|
||||||
|
working-directory: "pusher"
|
||||||
|
|
||||||
|
- name: "Lint"
|
||||||
|
run: yarn run lint
|
||||||
|
working-directory: "pusher"
|
||||||
|
|
||||||
|
- name: "Jasmine"
|
||||||
|
run: yarn test
|
||||||
|
working-directory: "pusher"
|
||||||
|
|
||||||
continuous-integration-back:
|
continuous-integration-back:
|
||||||
name: "Continuous Integration Back"
|
name: "Continuous Integration Back"
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
||||||
const URL_ROOM_STARTED = "/Floor0/floor0.json";
|
|
||||||
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
|
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
|
||||||
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
|
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
|
||||||
const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false;
|
const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false;
|
||||||
@ -16,7 +15,6 @@ export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as strin
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
SECRET_KEY,
|
SECRET_KEY,
|
||||||
URL_ROOM_STARTED,
|
|
||||||
MINIMUM_DISTANCE,
|
MINIMUM_DISTANCE,
|
||||||
ADMIN_API_URL,
|
ADMIN_API_URL,
|
||||||
ADMIN_API_TOKEN,
|
ADMIN_API_TOKEN,
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
PusherToBackMessage,
|
PusherToBackMessage,
|
||||||
ServerToAdminClientMessage,
|
ServerToAdminClientMessage,
|
||||||
ServerToClientMessage,
|
ServerToClientMessage,
|
||||||
SubMessage
|
SubMessage, UserJoinedRoomMessage, UserLeftRoomMessage
|
||||||
} from "../Messages/generated/messages_pb";
|
} from "../Messages/generated/messages_pb";
|
||||||
import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
|
import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
|
||||||
import {AdminSocket} from "../RoomManager";
|
import {AdminSocket} from "../RoomManager";
|
||||||
@ -21,16 +21,26 @@ export class Admin {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendUserJoin(uuid: string): void {
|
public sendUserJoin(uuid: string, name: string, ip: string): void {
|
||||||
const serverToAdminClientMessage = new ServerToAdminClientMessage();
|
const serverToAdminClientMessage = new ServerToAdminClientMessage();
|
||||||
serverToAdminClientMessage.setUseruuidjoinedroom(uuid);
|
|
||||||
|
const userJoinedRoomMessage = new UserJoinedRoomMessage();
|
||||||
|
userJoinedRoomMessage.setUuid(uuid);
|
||||||
|
userJoinedRoomMessage.setName(name);
|
||||||
|
userJoinedRoomMessage.setIpaddress(ip);
|
||||||
|
|
||||||
|
serverToAdminClientMessage.setUserjoinedroom(userJoinedRoomMessage);
|
||||||
|
|
||||||
this.socket.write(serverToAdminClientMessage);
|
this.socket.write(serverToAdminClientMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendUserLeft(uuid: string): void {
|
public sendUserLeft(uuid: string/*, name: string, ip: string*/): void {
|
||||||
const serverToAdminClientMessage = new ServerToAdminClientMessage();
|
const serverToAdminClientMessage = new ServerToAdminClientMessage();
|
||||||
serverToAdminClientMessage.setUseruuidleftroom(uuid);
|
|
||||||
|
const userLeftRoomMessage = new UserLeftRoomMessage();
|
||||||
|
userLeftRoomMessage.setUuid(uuid);
|
||||||
|
|
||||||
|
serverToAdminClientMessage.setUserleftroom(userLeftRoomMessage);
|
||||||
|
|
||||||
this.socket.write(serverToAdminClientMessage);
|
this.socket.write(serverToAdminClientMessage);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,17 @@ export class GameRoom {
|
|||||||
}
|
}
|
||||||
const position = ProtobufUtils.toPointInterface(positionMessage);
|
const position = ProtobufUtils.toPointInterface(positionMessage);
|
||||||
|
|
||||||
const user = new User(this.nextUserId, joinRoomMessage.getUseruuid(), position, false, this.positionNotifier, socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()));
|
const user = new User(this.nextUserId,
|
||||||
|
joinRoomMessage.getUseruuid(),
|
||||||
|
joinRoomMessage.getIpaddress(),
|
||||||
|
position,
|
||||||
|
false,
|
||||||
|
this.positionNotifier,
|
||||||
|
socket,
|
||||||
|
joinRoomMessage.getTagList(),
|
||||||
|
joinRoomMessage.getName(),
|
||||||
|
ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList())
|
||||||
|
);
|
||||||
this.nextUserId++;
|
this.nextUserId++;
|
||||||
this.users.set(user.id, user);
|
this.users.set(user.id, user);
|
||||||
this.usersByUuid.set(user.uuid, user);
|
this.usersByUuid.set(user.uuid, user);
|
||||||
@ -112,7 +122,7 @@ export class GameRoom {
|
|||||||
|
|
||||||
// Notify admins
|
// Notify admins
|
||||||
for (const admin of this.admins) {
|
for (const admin of this.admins) {
|
||||||
admin.sendUserJoin(user.uuid);
|
admin.sendUserJoin(user.uuid, user.name, user.IPAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
@ -135,7 +145,7 @@ export class GameRoom {
|
|||||||
|
|
||||||
// Notify admins
|
// Notify admins
|
||||||
for (const admin of this.admins) {
|
for (const admin of this.admins) {
|
||||||
admin.sendUserLeft(user.uuid);
|
admin.sendUserLeft(user.uuid/*, user.name, user.IPAddress*/);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +328,7 @@ export class GameRoom {
|
|||||||
|
|
||||||
// Let's send all connected users
|
// Let's send all connected users
|
||||||
for (const user of this.users.values()) {
|
for (const user of this.users.values()) {
|
||||||
admin.sendUserJoin(user.uuid);
|
admin.sendUserJoin(user.uuid, user.name, user.IPAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ export class User implements Movable {
|
|||||||
public constructor(
|
public constructor(
|
||||||
public id: number,
|
public id: number,
|
||||||
public readonly uuid: string,
|
public readonly uuid: string,
|
||||||
|
public readonly IPAddress: string,
|
||||||
private position: PointInterface,
|
private position: PointInterface,
|
||||||
public silent: boolean,
|
public silent: boolean,
|
||||||
private positionNotifier: PositionNotifier,
|
private positionNotifier: PositionNotifier,
|
||||||
|
@ -2,25 +2,22 @@ import {IRoomManagerServer} from "./Messages/generated/messages_grpc_pb";
|
|||||||
import {
|
import {
|
||||||
AdminGlobalMessage,
|
AdminGlobalMessage,
|
||||||
AdminMessage,
|
AdminMessage,
|
||||||
AdminPusherToBackMessage, BanMessage,
|
AdminPusherToBackMessage,
|
||||||
ClientToServerMessage, EmptyMessage,
|
BanMessage,
|
||||||
|
EmptyMessage,
|
||||||
ItemEventMessage,
|
ItemEventMessage,
|
||||||
JoinRoomMessage,
|
JoinRoomMessage,
|
||||||
PlayGlobalMessage,
|
PlayGlobalMessage,
|
||||||
PusherToBackMessage,
|
PusherToBackMessage,
|
||||||
QueryJitsiJwtMessage,
|
QueryJitsiJwtMessage,
|
||||||
ReportPlayerMessage,
|
|
||||||
RoomJoinedMessage,
|
|
||||||
ServerToAdminClientMessage,
|
ServerToAdminClientMessage,
|
||||||
ServerToClientMessage,
|
ServerToClientMessage,
|
||||||
SilentMessage,
|
SilentMessage,
|
||||||
UserMovesMessage,
|
UserMovesMessage,
|
||||||
ViewportMessage,
|
|
||||||
WebRtcSignalToServerMessage,
|
WebRtcSignalToServerMessage,
|
||||||
ZoneMessage
|
ZoneMessage
|
||||||
} from "./Messages/generated/messages_pb";
|
} from "./Messages/generated/messages_pb";
|
||||||
import grpc, {sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream} from "grpc";
|
import {sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream} from "grpc";
|
||||||
import {Empty} from "google-protobuf/google/protobuf/empty_pb";
|
|
||||||
import {socketManager} from "./Services/SocketManager";
|
import {socketManager} from "./Services/SocketManager";
|
||||||
import {emitError} from "./Services/MessageHelpers";
|
import {emitError} from "./Services/MessageHelpers";
|
||||||
import {User, UserSocket} from "./Model/User";
|
import {User, UserSocket} from "./Model/User";
|
||||||
@ -74,6 +71,16 @@ const roomManager: IRoomManagerServer = {
|
|||||||
socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage);*/
|
socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage);*/
|
||||||
} else if (message.hasQueryjitsijwtmessage()){
|
} else if (message.hasQueryjitsijwtmessage()){
|
||||||
socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
|
socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
|
||||||
|
}else if (message.hasSendusermessage()) {
|
||||||
|
const sendUserMessage = message.getSendusermessage();
|
||||||
|
if(sendUserMessage !== undefined) {
|
||||||
|
socketManager.handlerSendUserMessage(user, sendUserMessage);
|
||||||
|
}
|
||||||
|
}else if (message.hasBanusermessage()) {
|
||||||
|
const banUserMessage = message.getBanusermessage();
|
||||||
|
if(banUserMessage !== undefined) {
|
||||||
|
socketManager.handlerBanUserMessage(room, user, banUserMessage);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unhandled message type');
|
throw new Error('Unhandled message type');
|
||||||
}
|
}
|
||||||
@ -196,8 +203,8 @@ const roomManager: IRoomManagerServer = {
|
|||||||
callback(null, new EmptyMessage());
|
callback(null, new EmptyMessage());
|
||||||
},
|
},
|
||||||
ban(call: ServerUnaryCall<BanMessage>, callback: sendUnaryData<EmptyMessage>): void {
|
ban(call: ServerUnaryCall<BanMessage>, callback: sendUnaryData<EmptyMessage>): void {
|
||||||
|
// FIXME Work in progress
|
||||||
socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid());
|
socketManager.banUser(call.request.getRoomid(), call.request.getRecipientuuid(), 'foo bar TODO change this');
|
||||||
|
|
||||||
callback(null, new EmptyMessage());
|
callback(null, new EmptyMessage());
|
||||||
},
|
},
|
||||||
|
@ -1,24 +1,16 @@
|
|||||||
import {GameRoom} from "../Model/GameRoom";
|
import {GameRoom} from "../Model/GameRoom";
|
||||||
import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
|
import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
|
||||||
import {
|
import {
|
||||||
GroupDeleteMessage,
|
|
||||||
GroupUpdateMessage,
|
|
||||||
ItemEventMessage,
|
ItemEventMessage,
|
||||||
ItemStateMessage,
|
ItemStateMessage,
|
||||||
PlayGlobalMessage,
|
PlayGlobalMessage,
|
||||||
PointMessage,
|
PointMessage,
|
||||||
PositionMessage,
|
|
||||||
RoomJoinedMessage,
|
RoomJoinedMessage,
|
||||||
ServerToClientMessage,
|
ServerToClientMessage,
|
||||||
SetPlayerDetailsMessage,
|
|
||||||
SilentMessage,
|
SilentMessage,
|
||||||
SubMessage,
|
SubMessage,
|
||||||
ReportPlayerMessage,
|
|
||||||
UserJoinedMessage,
|
|
||||||
UserLeftMessage,
|
|
||||||
UserMovedMessage,
|
UserMovedMessage,
|
||||||
UserMovesMessage,
|
UserMovesMessage,
|
||||||
ViewportMessage,
|
|
||||||
WebRtcDisconnectMessage,
|
WebRtcDisconnectMessage,
|
||||||
WebRtcSignalToClientMessage,
|
WebRtcSignalToClientMessage,
|
||||||
WebRtcSignalToServerMessage,
|
WebRtcSignalToServerMessage,
|
||||||
@ -28,24 +20,23 @@ import {
|
|||||||
SendUserMessage,
|
SendUserMessage,
|
||||||
JoinRoomMessage,
|
JoinRoomMessage,
|
||||||
Zone as ProtoZone,
|
Zone as ProtoZone,
|
||||||
BatchMessage,
|
|
||||||
BatchToPusherMessage,
|
BatchToPusherMessage,
|
||||||
SubToPusherMessage,
|
SubToPusherMessage,
|
||||||
UserJoinedZoneMessage, GroupUpdateZoneMessage, GroupLeftZoneMessage, UserLeftZoneMessage, AdminMessage, BanMessage
|
UserJoinedZoneMessage, GroupUpdateZoneMessage, GroupLeftZoneMessage, UserLeftZoneMessage, BanUserMessage
|
||||||
} from "../Messages/generated/messages_pb";
|
} from "../Messages/generated/messages_pb";
|
||||||
import {User, UserSocket} from "../Model/User";
|
import {User, UserSocket} from "../Model/User";
|
||||||
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
||||||
import {Group} from "../Model/Group";
|
import {Group} from "../Model/Group";
|
||||||
import {cpuTracker} from "./CpuTracker";
|
import {cpuTracker} from "./CpuTracker";
|
||||||
import {ADMIN_API_URL, GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable";
|
import {GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable";
|
||||||
import {Movable} from "../Model/Movable";
|
import {Movable} from "../Model/Movable";
|
||||||
import {PositionInterface} from "../Model/PositionInterface";
|
import {PositionInterface} from "../Model/PositionInterface";
|
||||||
import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "./AdminApi";
|
import {adminApi, CharacterTexture} from "./AdminApi";
|
||||||
import Jwt from "jsonwebtoken";
|
import Jwt from "jsonwebtoken";
|
||||||
import {JITSI_URL} from "../Enum/EnvironmentVariable";
|
import {JITSI_URL} from "../Enum/EnvironmentVariable";
|
||||||
import {clientEventsEmitter} from "./ClientEventsEmitter";
|
import {clientEventsEmitter} from "./ClientEventsEmitter";
|
||||||
import {gaugeManager} from "./GaugeManager";
|
import {gaugeManager} from "./GaugeManager";
|
||||||
import {AdminSocket, ZoneSocket} from "../RoomManager";
|
import {ZoneSocket} from "../RoomManager";
|
||||||
import {Zone} from "_Model/Zone";
|
import {Zone} from "_Model/Zone";
|
||||||
import Debug from "debug";
|
import Debug from "debug";
|
||||||
import {Admin} from "_Model/Admin";
|
import {Admin} from "_Model/Admin";
|
||||||
@ -119,7 +110,7 @@ export class SocketManager {
|
|||||||
//const things = room.setViewport(client, viewport);
|
//const things = room.setViewport(client, viewport);
|
||||||
|
|
||||||
const roomJoinedMessage = new RoomJoinedMessage();
|
const roomJoinedMessage = new RoomJoinedMessage();
|
||||||
|
roomJoinedMessage.setTagList(joinRoomMessage.getTagList());
|
||||||
/*for (const thing of things) {
|
/*for (const thing of things) {
|
||||||
if (thing instanceof User) {
|
if (thing instanceof User) {
|
||||||
const player: ExSocketInterface|undefined = this.sockets.get(thing.id);
|
const player: ExSocketInterface|undefined = this.sockets.get(thing.id);
|
||||||
@ -626,6 +617,33 @@ export class SocketManager {
|
|||||||
user.socket.write(serverToClientMessage);
|
user.socket.write(serverToClientMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public handlerSendUserMessage(user: User, sendUserMessageToSend: SendUserMessage){
|
||||||
|
const sendUserMessage = new SendUserMessage();
|
||||||
|
sendUserMessage.setMessage(sendUserMessageToSend.getMessage());
|
||||||
|
sendUserMessage.setType(sendUserMessageToSend.getType());
|
||||||
|
|
||||||
|
const serverToClientMessage = new ServerToClientMessage();
|
||||||
|
serverToClientMessage.setSendusermessage(sendUserMessage);
|
||||||
|
user.socket.write(serverToClientMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public handlerBanUserMessage(room: GameRoom, user: User, banUserMessageToSend: BanUserMessage){
|
||||||
|
const banUserMessage = new BanUserMessage();
|
||||||
|
banUserMessage.setMessage(banUserMessageToSend.getMessage());
|
||||||
|
banUserMessage.setType(banUserMessageToSend.getType());
|
||||||
|
|
||||||
|
const serverToClientMessage = new ServerToClientMessage();
|
||||||
|
serverToClientMessage.setSendusermessage(banUserMessage);
|
||||||
|
user.socket.write(serverToClientMessage);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// Let's leave the room now.
|
||||||
|
room.leave(user);
|
||||||
|
// Let's close the connection when the user is banned.
|
||||||
|
user.socket.end();
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the characterLayers received from the front (as an array of string) with the custom textures from the back.
|
* Merges the characterLayers received from the front (as an array of string) with the custom textures from the back.
|
||||||
*/
|
*/
|
||||||
@ -748,7 +766,7 @@ export class SocketManager {
|
|||||||
recipient.socket.write(subToPusherMessage);
|
recipient.socket.write(subToPusherMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public banUser(roomId: string, recipientUuid: string): void {
|
public banUser(roomId: string, recipientUuid: string, message: string): void {
|
||||||
const room = this.rooms.get(roomId);
|
const room = this.rooms.get(roomId);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
console.error("In banUser, could not find room with id '" + roomId + "'. Maybe the room was closed a few milliseconds ago and there was a race condition?");
|
console.error("In banUser, could not find room with id '" + roomId + "'. Maybe the room was closed a few milliseconds ago and there was a race condition?");
|
||||||
@ -765,6 +783,7 @@ export class SocketManager {
|
|||||||
room.leave(recipient);
|
room.leave(recipient);
|
||||||
|
|
||||||
const sendUserMessage = new SendUserMessage();
|
const sendUserMessage = new SendUserMessage();
|
||||||
|
sendUserMessage.setMessage(message);
|
||||||
sendUserMessage.setType('banned');
|
sendUserMessage.setType('banned');
|
||||||
|
|
||||||
const subToPusherMessage = new SubToPusherMessage();
|
const subToPusherMessage = new SubToPusherMessage();
|
||||||
|
@ -26,6 +26,7 @@ function createJoinRoomMessage(uuid: string, x: number, y: number): JoinRoomMess
|
|||||||
positionMessage.setMoving(false);
|
positionMessage.setMoving(false);
|
||||||
const joinRoomMessage = new JoinRoomMessage();
|
const joinRoomMessage = new JoinRoomMessage();
|
||||||
joinRoomMessage.setUseruuid('1');
|
joinRoomMessage.setUseruuid('1');
|
||||||
|
joinRoomMessage.setIpaddress('10.0.0.2');
|
||||||
joinRoomMessage.setName('foo');
|
joinRoomMessage.setName('foo');
|
||||||
joinRoomMessage.setRoomid('_/global/test.json');
|
joinRoomMessage.setRoomid('_/global/test.json');
|
||||||
joinRoomMessage.setPositionmessage(positionMessage);
|
joinRoomMessage.setPositionmessage(positionMessage);
|
||||||
|
@ -25,14 +25,14 @@ describe("PositionNotifier", () => {
|
|||||||
leaveTriggered = true;
|
leaveTriggered = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const user1 = new User(1, 'test', {
|
const user1 = new User(1, 'test', '10.0.0.2', {
|
||||||
x: 500,
|
x: 500,
|
||||||
y: 500,
|
y: 500,
|
||||||
moving: false,
|
moving: false,
|
||||||
direction: 'down'
|
direction: 'down'
|
||||||
}, false, positionNotifier, {} as UserSocket, [], 'foo', []);
|
}, false, positionNotifier, {} as UserSocket, [], 'foo', []);
|
||||||
|
|
||||||
const user2 = new User(2, 'test', {
|
const user2 = new User(2, 'test', '10.0.0.2', {
|
||||||
x: -9999,
|
x: -9999,
|
||||||
y: -9999,
|
y: -9999,
|
||||||
moving: false,
|
moving: false,
|
||||||
@ -100,14 +100,14 @@ describe("PositionNotifier", () => {
|
|||||||
leaveTriggered = true;
|
leaveTriggered = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const user1 = new User(1, 'test', {
|
const user1 = new User(1, 'test', '10.0.0.2', {
|
||||||
x: 500,
|
x: 500,
|
||||||
y: 500,
|
y: 500,
|
||||||
moving: false,
|
moving: false,
|
||||||
direction: 'down'
|
direction: 'down'
|
||||||
}, false, positionNotifier, {} as UserSocket, [], 'foo', []);
|
}, false, positionNotifier, {} as UserSocket, [], 'foo', []);
|
||||||
|
|
||||||
const user2 = new User(2, 'test', {
|
const user2 = new User(2, 'test', '10.0.0.2', {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
moving: false,
|
moving: false,
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
local namespace = env.GITHUB_REF_SLUG,
|
local namespace = env.GITHUB_REF_SLUG,
|
||||||
local tag = namespace,
|
local tag = namespace,
|
||||||
local url = if namespace == "master" then "workadventu.re" else namespace+".workadventure.test.thecodingmachine.com",
|
local url = if namespace == "master" then "workadventu.re" else namespace+".workadventure.test.thecodingmachine.com",
|
||||||
local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://admin."+url else null,
|
// develop branch does not use admin because of issue with SSL certificate of admin as of now.
|
||||||
|
local adminUrl = if namespace == "master" /*|| namespace == "develop"*/ || std.startsWith(namespace, "admin") then "https://"+url else null,
|
||||||
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
|
"$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"containers": {
|
"containers": {
|
||||||
@ -72,13 +73,15 @@
|
|||||||
"env": {
|
"env": {
|
||||||
"API_URL": "pusher."+url,
|
"API_URL": "pusher."+url,
|
||||||
"UPLOADER_URL": "uploader."+url,
|
"UPLOADER_URL": "uploader."+url,
|
||||||
"ADMIN_URL": "admin."+url,
|
"ADMIN_URL": url,
|
||||||
"JITSI_URL": env.JITSI_URL,
|
"JITSI_URL": env.JITSI_URL,
|
||||||
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
|
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
|
||||||
"TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443",
|
"TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443",
|
||||||
"TURN_USER": "workadventure",
|
"TURN_USER": "workadventure",
|
||||||
"TURN_PASSWORD": "WorkAdventure123",
|
"TURN_PASSWORD": "WorkAdventure123",
|
||||||
"JITSI_PRIVATE_MODE": if env.SECRET_JITSI_KEY != '' then "true" else "false"
|
"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"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uploader": {
|
"uploader": {
|
||||||
@ -100,17 +103,6 @@
|
|||||||
},
|
},
|
||||||
"ports": [80]
|
"ports": [80]
|
||||||
},
|
},
|
||||||
"website": {
|
|
||||||
"image": "thecodingmachine/workadventure-website:"+tag,
|
|
||||||
"host": {
|
|
||||||
"url": url,
|
|
||||||
"https": "enable"
|
|
||||||
},
|
|
||||||
"ports": [80],
|
|
||||||
"env": {
|
|
||||||
"GAME_URL": "https://play."+url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"https": {
|
"https": {
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
wait_app:
|
|
||||||
image: dadarek/wait-for-dependencies
|
|
||||||
depends_on:
|
|
||||||
- reverse-proxy
|
|
||||||
command: front:8080
|
|
||||||
cypress:
|
|
||||||
# the Docker image to use from https://github.com/cypress-io/cypress-docker-images
|
|
||||||
image: "cypress/included:3.8.3"
|
|
||||||
depends_on:
|
|
||||||
- reverse-proxy
|
|
||||||
environment:
|
|
||||||
# pass base url to test pointing at the web application
|
|
||||||
- CYPRESS_baseUrl=http://front:8080
|
|
||||||
working_dir: /e2e
|
|
||||||
volumes:
|
|
||||||
- ./e2e/:/e2e
|
|
@ -28,11 +28,13 @@ services:
|
|||||||
NODE_ENV: development
|
NODE_ENV: development
|
||||||
API_URL: pusher.workadventure.localhost
|
API_URL: pusher.workadventure.localhost
|
||||||
UPLOADER_URL: uploader.workadventure.localhost
|
UPLOADER_URL: uploader.workadventure.localhost
|
||||||
ADMIN_URL: admin.workadventure.localhost
|
ADMIN_URL: workadventure.localhost
|
||||||
STARTUP_COMMAND_1: yarn install
|
STARTUP_COMMAND_1: ./templater.sh
|
||||||
|
STARTUP_COMMAND_2: yarn install
|
||||||
TURN_SERVER: "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443"
|
TURN_SERVER: "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443"
|
||||||
TURN_USER: workadventure
|
TURN_USER: workadventure
|
||||||
TURN_PASSWORD: WorkAdventure123
|
TURN_PASSWORD: WorkAdventure123
|
||||||
|
START_ROOM_URL: "$START_ROOM_URL"
|
||||||
command: yarn run start
|
command: yarn run start
|
||||||
volumes:
|
volumes:
|
||||||
- ./front:/usr/src/app
|
- ./front:/usr/src/app
|
||||||
@ -135,23 +137,6 @@ services:
|
|||||||
- "traefik.http.routers.uploader-ssl.tls=true"
|
- "traefik.http.routers.uploader-ssl.tls=true"
|
||||||
- "traefik.http.routers.uploader-ssl.service=uploader"
|
- "traefik.http.routers.uploader-ssl.service=uploader"
|
||||||
|
|
||||||
website:
|
|
||||||
image: thecodingmachine/nodejs:12-apache
|
|
||||||
environment:
|
|
||||||
STARTUP_COMMAND_1: npm install
|
|
||||||
STARTUP_COMMAND_2: npm run watch &
|
|
||||||
APACHE_DOCUMENT_ROOT: dist/
|
|
||||||
volumes:
|
|
||||||
- ./website:/var/www/html
|
|
||||||
labels:
|
|
||||||
- "traefik.http.routers.website.rule=Host(`workadventure.localhost`)"
|
|
||||||
- "traefik.http.routers.website.entryPoints=web"
|
|
||||||
- "traefik.http.services.website.loadbalancer.server.port=80"
|
|
||||||
- "traefik.http.routers.website-ssl.rule=Host(`workadventure.localhost`)"
|
|
||||||
- "traefik.http.routers.website-ssl.entryPoints=websecure"
|
|
||||||
- "traefik.http.routers.website-ssl.tls=true"
|
|
||||||
- "traefik.http.routers.website-ssl.service=website"
|
|
||||||
|
|
||||||
messages:
|
messages:
|
||||||
#image: thecodingmachine/nodejs:14
|
#image: thecodingmachine/nodejs:14
|
||||||
image: thecodingmachine/workadventure-back-base:latest
|
image: thecodingmachine/workadventure-back-base:latest
|
||||||
|
3
e2e/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
screenshots/
|
|
||||||
videos/
|
|
||||||
node_modules/
|
|
@ -1,36 +0,0 @@
|
|||||||
# Testing with cypress
|
|
||||||
|
|
||||||
This project use [cypress](https://www.cypress.io/) to do functional testing of the website.
|
|
||||||
Unfortunately we cannot integrate it with docker-compose for the moment, so you will need to install some packages locally on your pc.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
You will need to install theses dependancies on linux (don't know about mac):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install libgtk2.0-0 libgtk-3-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
|
|
||||||
```
|
|
||||||
|
|
||||||
Cypress can be installed locally in the e2e directory
|
|
||||||
```bash
|
|
||||||
cd e2e
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
How to use:
|
|
||||||
```bash
|
|
||||||
npm run cy:run
|
|
||||||
npm run cy:open
|
|
||||||
```
|
|
||||||
|
|
||||||
The first command will run all tests in the terminal, while the second will open the interactive task runner which allow you to easily manage the test workflow
|
|
||||||
|
|
||||||
[More details here](https://docs.cypress.io/guides/getting-started/testing-your-app.html#Step-1-Start-your-server)
|
|
||||||
|
|
||||||
## How to test a game
|
|
||||||
|
|
||||||
Cypress cannot "see" and so cannot directly manipulate the canva created by Phaser.
|
|
||||||
|
|
||||||
This means we have to do workarounds such as exposing core objects in the window so that cypress can manipulate them or doing console that cypress can catch.
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"baseUrl": "http://workadventure.localhost",
|
|
||||||
"video": false,
|
|
||||||
"defaultCommandTimeout": 20000,
|
|
||||||
"pluginsFile": false,
|
|
||||||
"supportFile": false
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
Cypress.on('window:before:load', (win) => {
|
|
||||||
// because this is called before any scripts
|
|
||||||
// have loaded - the ga function is undefined
|
|
||||||
// so we need to create it.
|
|
||||||
win.cypressAsserter = cy.stub().as('ca')
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('WorkAdventureGame', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.visit('/', {
|
|
||||||
onBeforeLoad (win) {
|
|
||||||
cy.spy(win.console, 'log').as('console.log')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads', () => {
|
|
||||||
cy.get('@console.log').should('be.calledWith', 'Started the game')
|
|
||||||
cy.get('@console.log').should('be.calledWith', 'Preloading')
|
|
||||||
cy.get('@console.log').should('be.calledWith', 'Preloading done')
|
|
||||||
cy.get('@console.log').should('be.calledWith', 'startInit')
|
|
||||||
cy.get('@console.log').should('be.calledWith', 'startInit done')
|
|
||||||
});
|
|
||||||
});
|
|
1406
e2e/package-lock.json
generated
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"cypress": "^3.8.3"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"cy:run": "cypress run",
|
|
||||||
"cy:open": "cypress open"
|
|
||||||
}
|
|
||||||
}
|
|
1
front/.gitignore
vendored
@ -6,3 +6,4 @@
|
|||||||
/dist/webpack.config.js.map
|
/dist/webpack.config.js.map
|
||||||
/dist/src
|
/dist/src
|
||||||
*.sh
|
*.sh
|
||||||
|
!templater.sh
|
||||||
|
@ -25,4 +25,5 @@ RUN API_URL=$API_URL UPLOADER_URL=$UPLOADER_URL ADMIN_URL=$ADMIN_URL \
|
|||||||
# final production image
|
# final production image
|
||||||
FROM nginx:1.19.6-alpine@sha256:01747306a7247dbe928db991eab42e4002118bf636dd85b4ffea05dd907e5b66
|
FROM nginx:1.19.6-alpine@sha256:01747306a7247dbe928db991eab42e4002118bf636dd85b4ffea05dd907e5b66
|
||||||
COPY front/nginx-vhost.conf /etc/nginx/conf.d/default.conf
|
COPY front/nginx-vhost.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY front/templater.sh /docker-entrypoint.d/templater.sh
|
||||||
COPY --from=builder2 /usr/src/dist /usr/share/nginx/html
|
COPY --from=builder2 /usr/src/dist /usr/share/nginx/html
|
||||||
|
1
front/dist/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
index.html
|
9
front/dist/ga.html.tmpl
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=<!-- TRACKING NUMBER -->"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', '<!-- TRACKING NUMBER -->');
|
||||||
|
</script>
|
@ -6,15 +6,8 @@
|
|||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
<!-- TRACK CODE -->
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-10196481-11"></script>
|
<!-- END TRACK CODE -->
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
|
|
||||||
gtag('config', 'UA-10196481-11');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="static/images/favicons/apple-icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="static/images/favicons/apple-icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="static/images/favicons/apple-icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="static/images/favicons/apple-icon-60x60.png">
|
5
front/dist/resources/style/style.css
vendored
@ -1072,17 +1072,22 @@ div.modal-report-user{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.discussion .messages .message p.body{
|
.discussion .messages .message p.body{
|
||||||
|
color: white;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
.discussion .messages .message p.a{
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.discussion .send-message{
|
.discussion .send-message{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 45px;
|
bottom: 45px;
|
||||||
width: 220px;
|
width: 220px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discussion .send-message input{
|
.discussion .send-message input{
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"generic-type-guard": "^3.2.0",
|
"generic-type-guard": "^3.2.0",
|
||||||
"google-protobuf": "^3.13.0",
|
"google-protobuf": "^3.13.0",
|
||||||
"phaser": "^3.22.0",
|
"phaser": "3.24.1",
|
||||||
"queue-typescript": "^1.0.1",
|
"queue-typescript": "^1.0.1",
|
||||||
"quill": "^1.3.7",
|
"quill": "^1.3.7",
|
||||||
"simple-peer": "^9.6.2",
|
"simple-peer": "^9.6.2",
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import {RoomConnection} from "../Connexion/RoomConnection";
|
import {RoomConnection} from "../Connexion/RoomConnection";
|
||||||
import * as TypeMessages from "./TypeMessage";
|
import * as TypeMessages from "./TypeMessage";
|
||||||
|
import List = Phaser.Structs.List;
|
||||||
|
import {UpdatedLocalStreamCallback} from "../WebRtc/MediaManager";
|
||||||
|
import {Banned} from "./TypeMessage";
|
||||||
|
|
||||||
export interface TypeMessageInterface {
|
export interface TypeMessageInterface {
|
||||||
showMessage(message: string): void;
|
showMessage(message: string): void;
|
||||||
@ -8,6 +11,7 @@ export interface TypeMessageInterface {
|
|||||||
export class UserMessageManager {
|
export class UserMessageManager {
|
||||||
|
|
||||||
typeMessages: Map<string, TypeMessageInterface> = new Map<string, TypeMessageInterface>();
|
typeMessages: Map<string, TypeMessageInterface> = new Map<string, TypeMessageInterface>();
|
||||||
|
receiveBannedMessageListener: Set<Function> = new Set<UpdatedLocalStreamCallback>();
|
||||||
|
|
||||||
constructor(private Connection: RoomConnection) {
|
constructor(private Connection: RoomConnection) {
|
||||||
const valueTypeMessageTab = Object.values(TypeMessages);
|
const valueTypeMessageTab = Object.values(TypeMessages);
|
||||||
@ -21,7 +25,14 @@ export class UserMessageManager {
|
|||||||
initialise() {
|
initialise() {
|
||||||
//receive signal to show message
|
//receive signal to show message
|
||||||
this.Connection.receiveUserMessage((type: string, message: string) => {
|
this.Connection.receiveUserMessage((type: string, message: string) => {
|
||||||
this.showMessage(type, message);
|
const typeMessage = this.showMessage(type, message);
|
||||||
|
|
||||||
|
//listener on banned receive message
|
||||||
|
if(typeMessage instanceof Banned) {
|
||||||
|
for (const callback of this.receiveBannedMessageListener) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,5 +43,10 @@ export class UserMessageManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
classTypeMessage.showMessage(message);
|
classTypeMessage.showMessage(message);
|
||||||
|
return classTypeMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
setReceiveBanListener(callback: Function){
|
||||||
|
this.receiveBannedMessageListener.add(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import {API_URL} from "../Enum/EnvironmentVariable";
|
import {API_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable";
|
||||||
import {RoomConnection} from "./RoomConnection";
|
import {RoomConnection} from "./RoomConnection";
|
||||||
import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
|
import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
|
||||||
import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
|
import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
|
||||||
@ -7,8 +7,6 @@ import {localUserStore} from "./LocalUserStore";
|
|||||||
import {LocalUser} from "./LocalUser";
|
import {LocalUser} from "./LocalUser";
|
||||||
import {Room} from "./Room";
|
import {Room} from "./Room";
|
||||||
|
|
||||||
const URL_ROOM_STARTED = '/Floor0/floor0.json';
|
|
||||||
|
|
||||||
class ConnectionManager {
|
class ConnectionManager {
|
||||||
private localUser!:LocalUser;
|
private localUser!:LocalUser;
|
||||||
|
|
||||||
@ -29,9 +27,9 @@ class ConnectionManager {
|
|||||||
const organizationSlug = data.organizationSlug;
|
const organizationSlug = data.organizationSlug;
|
||||||
const worldSlug = data.worldSlug;
|
const worldSlug = data.worldSlug;
|
||||||
const roomSlug = data.roomSlug;
|
const roomSlug = data.roomSlug;
|
||||||
urlManager.editUrlForRoom(roomSlug, organizationSlug, worldSlug);
|
|
||||||
|
|
||||||
const room = new Room(window.location.pathname + window.location.hash);
|
const room = new Room('/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug + window.location.hash);
|
||||||
|
urlManager.pushRoomIdToUrl(room);
|
||||||
return Promise.resolve(room);
|
return Promise.resolve(room);
|
||||||
} else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) {
|
} else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) {
|
||||||
const localUser = localUserStore.getLocalUser();
|
const localUser = localUserStore.getLocalUser();
|
||||||
@ -50,8 +48,7 @@ class ConnectionManager {
|
|||||||
}
|
}
|
||||||
let roomId: string
|
let roomId: string
|
||||||
if (connexionType === GameConnexionTypes.empty) {
|
if (connexionType === GameConnexionTypes.empty) {
|
||||||
const defaultMapUrl = window.location.host.replace('play.', 'maps.') + URL_ROOM_STARTED;
|
roomId = START_ROOM_URL;
|
||||||
roomId = urlManager.editUrlForRoom(defaultMapUrl, null, null);
|
|
||||||
} else {
|
} else {
|
||||||
roomId = window.location.pathname + window.location.hash;
|
roomId = window.location.pathname + window.location.hash;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {PlayerAnimationNames} from "../Phaser/Player/Animation";
|
import {PlayerAnimationNames} from "../Phaser/Player/Animation";
|
||||||
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
|
||||||
import {SignalData} from "simple-peer";
|
import {SignalData} from "simple-peer";
|
||||||
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character";
|
|
||||||
import {RoomConnection} from "./RoomConnection";
|
import {RoomConnection} from "./RoomConnection";
|
||||||
|
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||||
|
|
||||||
export enum EventMessage{
|
export enum EventMessage{
|
||||||
CONNECT = "connect",
|
CONNECT = "connect",
|
||||||
|
@ -72,8 +72,9 @@ export class Room {
|
|||||||
console.log('Map ', this.id, ' resolves to URL ', data.mapUrl);
|
console.log('Map ', this.id, ' resolves to URL ', data.mapUrl);
|
||||||
resolve(data.mapUrl);
|
resolve(data.mapUrl);
|
||||||
return;
|
return;
|
||||||
|
}).catch((reason) => {
|
||||||
|
reject(reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ import {
|
|||||||
ViewportInterface, WebRtcDisconnectMessageInterface,
|
ViewportInterface, WebRtcDisconnectMessageInterface,
|
||||||
WebRtcSignalReceivedMessageInterface,
|
WebRtcSignalReceivedMessageInterface,
|
||||||
} from "./ConnexionModels";
|
} from "./ConnexionModels";
|
||||||
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character";
|
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
|
||||||
|
|
||||||
const manualPingDelay = 20000;
|
const manualPingDelay = 20000;
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
declare let window:WindowWithCypressAsserter;
|
|
||||||
|
|
||||||
interface WindowWithCypressAsserter extends Window {
|
|
||||||
cypressAsserter: CypressAsserter;
|
|
||||||
}
|
|
||||||
|
|
||||||
//this class is used to communicate with cypress, our e2e testing client
|
|
||||||
//Since cypress cannot manipulate canvas, we notified it with console logs
|
|
||||||
class CypressAsserter {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
window.cypressAsserter = this
|
|
||||||
}
|
|
||||||
|
|
||||||
gameStarted() {
|
|
||||||
console.log('Started the game')
|
|
||||||
}
|
|
||||||
|
|
||||||
preloadStarted() {
|
|
||||||
console.log('Preloading')
|
|
||||||
}
|
|
||||||
|
|
||||||
preloadFinished() {
|
|
||||||
console.log('Preloading done')
|
|
||||||
}
|
|
||||||
|
|
||||||
initStarted() {
|
|
||||||
console.log('startInit')
|
|
||||||
}
|
|
||||||
|
|
||||||
initFinished() {
|
|
||||||
console.log('startInit done')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const cypressAsserter = new CypressAsserter()
|
|
@ -1,7 +1,8 @@
|
|||||||
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
|
const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true";
|
||||||
|
const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json';
|
||||||
const API_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.API_URL || "pusher.workadventure.localhost");
|
const API_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.API_URL || "pusher.workadventure.localhost");
|
||||||
const UPLOADER_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.UPLOADER_URL || 'uploader.workadventure.localhost');
|
const UPLOADER_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.UPLOADER_URL || 'uploader.workadventure.localhost');
|
||||||
const ADMIN_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.ADMIN_URL || "admin.workadventure.localhost");
|
const ADMIN_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.ADMIN_URL || "workadventure.localhost");
|
||||||
const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca";
|
const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca";
|
||||||
const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com';
|
const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com';
|
||||||
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$';
|
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$';
|
||||||
@ -14,6 +15,7 @@ const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
DEBUG_MODE,
|
DEBUG_MODE,
|
||||||
|
START_ROOM_URL,
|
||||||
API_URL,
|
API_URL,
|
||||||
UPLOADER_URL,
|
UPLOADER_URL,
|
||||||
ADMIN_URL,
|
ADMIN_URL,
|
||||||
|
14
front/src/Phaser/Components/Loader.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
export const addLoader = (scene:Phaser.Scene): void => {
|
||||||
|
const loadingText = scene.add.text(scene.game.renderer.width / 2, 200, 'Loading');
|
||||||
|
const progress = scene.add.graphics();
|
||||||
|
scene.load.on('progress', (value: number) => {
|
||||||
|
progress.clear();
|
||||||
|
progress.fillStyle(0xffffff, 1);
|
||||||
|
progress.fillRect(0, 270, 800 * value, 60);
|
||||||
|
});
|
||||||
|
scene.load.on('complete', () => {
|
||||||
|
loadingText.destroy();
|
||||||
|
progress.destroy();
|
||||||
|
});
|
||||||
|
}
|
@ -5,33 +5,6 @@ import Container = Phaser.GameObjects.Container;
|
|||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import {TextureError} from "../../Exception/TextureError";
|
import {TextureError} from "../../Exception/TextureError";
|
||||||
|
|
||||||
export interface PlayerResourceDescriptionInterface {
|
|
||||||
name: string,
|
|
||||||
img: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PLAYER_RESOURCES: Array<PlayerResourceDescriptionInterface> = [
|
|
||||||
{name: "male1", img: "resources/characters/pipoya/Male 01-1.png" /*, x: 32, y: 32*/},
|
|
||||||
{name: "male2", img: "resources/characters/pipoya/Male 02-2.png"/*, x: 64, y: 32*/},
|
|
||||||
{name: "male3", img: "resources/characters/pipoya/Male 03-4.png"/*, x: 96, y: 32*/},
|
|
||||||
{name: "male4", img: "resources/characters/pipoya/Male 09-1.png"/*, x: 128, y: 32*/},
|
|
||||||
|
|
||||||
{name: "male5", img: "resources/characters/pipoya/Male 10-3.png"/*, x: 32, y: 64*/},
|
|
||||||
{name: "male6", img: "resources/characters/pipoya/Male 17-2.png"/*, x: 64, y: 64*/},
|
|
||||||
{name: "male7", img: "resources/characters/pipoya/Male 18-1.png"/*, x: 96, y: 64*/},
|
|
||||||
{name: "male8", img: "resources/characters/pipoya/Male 16-4.png"/*, x: 128, y: 64*/},
|
|
||||||
|
|
||||||
{name: "Female1", img: "resources/characters/pipoya/Female 01-1.png"/*, x: 32, y: 96*/},
|
|
||||||
{name: "Female2", img: "resources/characters/pipoya/Female 02-2.png"/*, x: 64, y: 96*/},
|
|
||||||
{name: "Female3", img: "resources/characters/pipoya/Female 03-4.png"/*, x: 96, y: 96*/},
|
|
||||||
{name: "Female4", img: "resources/characters/pipoya/Female 09-1.png"/*, x: 128, y: 96*/},
|
|
||||||
|
|
||||||
{name: "Female5", img: "resources/characters/pipoya/Female 10-3.png"/*, x: 32, y: 128*/},
|
|
||||||
{name: "Female6", img: "resources/characters/pipoya/Female 17-2.png"/*, x: 64, y: 128*/},
|
|
||||||
{name: "Female7", img: "resources/characters/pipoya/Female 18-1.png"/*, x: 96, y: 128*/},
|
|
||||||
{name: "Female8", img: "resources/characters/pipoya/Female 16-4.png"/*, x: 128, y: 128*/}
|
|
||||||
];
|
|
||||||
|
|
||||||
interface AnimationData {
|
interface AnimationData {
|
||||||
key: string;
|
key: string;
|
||||||
frameRate: number;
|
frameRate: number;
|
||||||
@ -48,11 +21,12 @@ export abstract class Character extends Container {
|
|||||||
public sprites: Map<string, Sprite>;
|
public sprites: Map<string, Sprite>;
|
||||||
private lastDirection: string = PlayerAnimationNames.WalkDown;
|
private lastDirection: string = PlayerAnimationNames.WalkDown;
|
||||||
//private teleportation: Sprite;
|
//private teleportation: Sprite;
|
||||||
|
private invisible: boolean;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene,
|
constructor(scene: Phaser.Scene,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
textures: string[],
|
texturesPromise: Promise<string[]>,
|
||||||
name: string,
|
name: string,
|
||||||
direction: string,
|
direction: string,
|
||||||
moving: boolean,
|
moving: boolean,
|
||||||
@ -60,10 +34,15 @@ export abstract class Character extends Container {
|
|||||||
) {
|
) {
|
||||||
super(scene, x, y/*, texture, frame*/);
|
super(scene, x, y/*, texture, frame*/);
|
||||||
this.PlayerValue = name;
|
this.PlayerValue = name;
|
||||||
|
this.invisible = true
|
||||||
|
|
||||||
this.sprites = new Map<string, Sprite>();
|
this.sprites = new Map<string, Sprite>();
|
||||||
|
|
||||||
this.addTextures(textures, frame);
|
//textures are inside a Promise in case they need to be lazyloaded before use.
|
||||||
|
texturesPromise.then((textures) => {
|
||||||
|
this.addTextures(textures, frame);
|
||||||
|
this.invisible = false
|
||||||
|
})
|
||||||
|
|
||||||
/*this.teleportation = new Sprite(scene, -20, -10, 'teleportation', 3);
|
/*this.teleportation = new Sprite(scene, -20, -10, 'teleportation', 3);
|
||||||
this.teleportation.setInteractive();
|
this.teleportation.setInteractive();
|
||||||
@ -148,6 +127,7 @@ export abstract class Character extends Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected playAnimation(direction : string, moving: boolean): void {
|
protected playAnimation(direction : string, moving: boolean): void {
|
||||||
|
if (this.invisible) return;
|
||||||
for (const [texture, sprite] of this.sprites.entries()) {
|
for (const [texture, sprite] of this.sprites.entries()) {
|
||||||
if (!sprite.anims) {
|
if (!sprite.anims) {
|
||||||
console.error('ANIMS IS NOT DEFINED!!!');
|
console.error('ANIMS IS NOT DEFINED!!!');
|
||||||
@ -190,7 +170,6 @@ export abstract class Character extends Container {
|
|||||||
this.playAnimation(PlayerAnimationNames.WalkLeft, true);
|
this.playAnimation(PlayerAnimationNames.WalkLeft, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//update depth user
|
|
||||||
this.setDepth(this.y);
|
this.setDepth(this.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
344
front/src/Phaser/Entity/PlayerTextures.ts
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
//The list of all the player textures, both the default models and the partial textures used for customization
|
||||||
|
|
||||||
|
export interface BodyResourceDescriptionListInterface {
|
||||||
|
[key: string]: BodyResourceDescriptionInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BodyResourceDescriptionInterface {
|
||||||
|
name: string,
|
||||||
|
img: string,
|
||||||
|
level?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {
|
||||||
|
"male1": {name: "male1", img: "resources/characters/pipoya/Male 01-1.png"},
|
||||||
|
"male2": {name: "male2", img: "resources/characters/pipoya/Male 02-2.png"},
|
||||||
|
"male3": {name: "male3", img: "resources/characters/pipoya/Male 03-4.png"},
|
||||||
|
"male4": {name: "male4", img: "resources/characters/pipoya/Male 09-1.png"},
|
||||||
|
"male5": {name: "male5", img: "resources/characters/pipoya/Male 10-3.png"},
|
||||||
|
"male6": {name: "male6", img: "resources/characters/pipoya/Male 17-2.png"},
|
||||||
|
"male7": {name: "male7", img: "resources/characters/pipoya/Male 18-1.png"},
|
||||||
|
"male8": {name: "male8", img: "resources/characters/pipoya/Male 16-4.png"},
|
||||||
|
"male9": {name: "male9", img: "resources/characters/pipoya/Male 07-2.png"},
|
||||||
|
"male10": {name: "male10", img: "resources/characters/pipoya/Male 05-3.png"},
|
||||||
|
"male11": {name: "male11", img: "resources/characters/pipoya/Teacher male 02.png"},
|
||||||
|
"male12": {name: "male12", img: "resources/characters/pipoya/su4 Student male 12.png"},
|
||||||
|
|
||||||
|
"Female1": {name: "Female1", img: "resources/characters/pipoya/Female 01-1.png"},
|
||||||
|
"Female2": {name: "Female2", img: "resources/characters/pipoya/Female 02-2.png"},
|
||||||
|
"Female3": {name: "Female3", img: "resources/characters/pipoya/Female 03-4.png"},
|
||||||
|
"Female4": {name: "Female4", img: "resources/characters/pipoya/Female 09-1.png"},
|
||||||
|
"Female5": {name: "Female5", img: "resources/characters/pipoya/Female 10-3.png"},
|
||||||
|
"Female6": {name: "Female6", img: "resources/characters/pipoya/Female 17-2.png"},
|
||||||
|
"Female7": {name: "Female7", img: "resources/characters/pipoya/Female 18-1.png"},
|
||||||
|
"Female8": {name: "Female8", img: "resources/characters/pipoya/Female 16-4.png"},
|
||||||
|
"Female9": {name: "Female9", img: "resources/characters/pipoya/Female 07-2.png"},
|
||||||
|
"Female10": {name: "Female10", img: "resources/characters/pipoya/Female 05-3.png"},
|
||||||
|
"Female11": {name: "Female11", img: "resources/characters/pipoya/Teacher fmale 02.png"},
|
||||||
|
"Female12": {name: "Female12", img: "resources/characters/pipoya/su4 Student fmale 12.png"},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const COLOR_RESOURCES: BodyResourceDescriptionListInterface = {
|
||||||
|
"color_1": {name: "color_1", img: "resources/customisation/character_color/character_color0.png"},
|
||||||
|
"color_2": {name: "color_2", img: "resources/customisation/character_color/character_color1.png"},
|
||||||
|
"color_3": {name: "color_3", img: "resources/customisation/character_color/character_color2.png"},
|
||||||
|
"color_4": {name: "color_4", img: "resources/customisation/character_color/character_color3.png"},
|
||||||
|
"color_5": {name: "color_5", img: "resources/customisation/character_color/character_color4.png"},
|
||||||
|
"color_6": {name: "color_6", img: "resources/customisation/character_color/character_color5.png"},
|
||||||
|
"color_7": {name: "color_7", img: "resources/customisation/character_color/character_color6.png"},
|
||||||
|
"color_8": {name: "color_8", img: "resources/customisation/character_color/character_color7.png"},
|
||||||
|
"color_9": {name: "color_9", img: "resources/customisation/character_color/character_color8.png"},
|
||||||
|
"color_10": {name: "color_10", img: "resources/customisation/character_color/character_color9.png"},
|
||||||
|
"color_11": {name: "color_11", img: "resources/customisation/character_color/character_color10.png"},
|
||||||
|
"color_12": {name: "color_12", img: "resources/customisation/character_color/character_color11.png"},
|
||||||
|
"color_13": {name: "color_13", img: "resources/customisation/character_color/character_color12.png"},
|
||||||
|
"color_14": {name: "color_14", img: "resources/customisation/character_color/character_color13.png"},
|
||||||
|
"color_15": {name: "color_15", img: "resources/customisation/character_color/character_color14.png"},
|
||||||
|
"color_16": {name: "color_16", img: "resources/customisation/character_color/character_color15.png"},
|
||||||
|
"color_17": {name: "color_17", img: "resources/customisation/character_color/character_color16.png"},
|
||||||
|
"color_18": {name: "color_18", img: "resources/customisation/character_color/character_color17.png"},
|
||||||
|
"color_19": {name: "color_19", img: "resources/customisation/character_color/character_color18.png"},
|
||||||
|
"color_20": {name: "color_20", img: "resources/customisation/character_color/character_color19.png"},
|
||||||
|
"color_21": {name: "color_21", img: "resources/customisation/character_color/character_color20.png"},
|
||||||
|
"color_22": {name: "color_22", img: "resources/customisation/character_color/character_color21.png"},
|
||||||
|
"color_23": {name: "color_23", img: "resources/customisation/character_color/character_color22.png"},
|
||||||
|
"color_24": {name: "color_24", img: "resources/customisation/character_color/character_color23.png"},
|
||||||
|
"color_25": {name: "color_25", img: "resources/customisation/character_color/character_color24.png"},
|
||||||
|
"color_26": {name: "color_26", img: "resources/customisation/character_color/character_color25.png"},
|
||||||
|
"color_27": {name: "color_27", img: "resources/customisation/character_color/character_color26.png"},
|
||||||
|
"color_28": {name: "color_28", img: "resources/customisation/character_color/character_color27.png"},
|
||||||
|
"color_29": {name: "color_29", img: "resources/customisation/character_color/character_color28.png"},
|
||||||
|
"color_30": {name: "color_30", img: "resources/customisation/character_color/character_color29.png"},
|
||||||
|
"color_31": {name: "color_31", img: "resources/customisation/character_color/character_color30.png"},
|
||||||
|
"color_32": {name: "color_32", img: "resources/customisation/character_color/character_color31.png"},
|
||||||
|
"color_33": {name: "color_33", img: "resources/customisation/character_color/character_color32.png"}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EYES_RESOURCES: BodyResourceDescriptionListInterface = {
|
||||||
|
"eyes_1": {name: "eyes_1", img: "resources/customisation/character_eyes/character_eyes1.png"},
|
||||||
|
"eyes_2": {name: "eyes_2", img: "resources/customisation/character_eyes/character_eyes2.png"},
|
||||||
|
"eyes_3": {name: "eyes_3", img: "resources/customisation/character_eyes/character_eyes3.png"},
|
||||||
|
"eyes_4": {name: "eyes_4", img: "resources/customisation/character_eyes/character_eyes4.png"},
|
||||||
|
"eyes_5": {name: "eyes_5", img: "resources/customisation/character_eyes/character_eyes5.png"},
|
||||||
|
"eyes_6": {name: "eyes_6", img: "resources/customisation/character_eyes/character_eyes6.png"},
|
||||||
|
"eyes_7": {name: "eyes_7", img: "resources/customisation/character_eyes/character_eyes7.png"},
|
||||||
|
"eyes_8": {name: "eyes_8", img: "resources/customisation/character_eyes/character_eyes8.png"},
|
||||||
|
"eyes_9": {name: "eyes_9", img: "resources/customisation/character_eyes/character_eyes9.png"},
|
||||||
|
"eyes_10": {name: "eyes_10", img: "resources/customisation/character_eyes/character_eyes10.png"},
|
||||||
|
"eyes_11": {name: "eyes_11", img: "resources/customisation/character_eyes/character_eyes11.png"},
|
||||||
|
"eyes_12": {name: "eyes_12", img: "resources/customisation/character_eyes/character_eyes12.png"},
|
||||||
|
"eyes_13": {name: "eyes_13", img: "resources/customisation/character_eyes/character_eyes13.png"},
|
||||||
|
"eyes_14": {name: "eyes_14", img: "resources/customisation/character_eyes/character_eyes14.png"},
|
||||||
|
"eyes_15": {name: "eyes_15", img: "resources/customisation/character_eyes/character_eyes15.png"},
|
||||||
|
"eyes_16": {name: "eyes_16", img: "resources/customisation/character_eyes/character_eyes16.png"},
|
||||||
|
"eyes_17": {name: "eyes_17", img: "resources/customisation/character_eyes/character_eyes17.png"},
|
||||||
|
"eyes_18": {name: "eyes_18", img: "resources/customisation/character_eyes/character_eyes18.png"},
|
||||||
|
"eyes_19": {name: "eyes_19", img: "resources/customisation/character_eyes/character_eyes19.png"},
|
||||||
|
"eyes_20": {name: "eyes_20", img: "resources/customisation/character_eyes/character_eyes20.png"},
|
||||||
|
"eyes_21": {name: "eyes_21", img: "resources/customisation/character_eyes/character_eyes21.png"},
|
||||||
|
"eyes_22": {name: "eyes_22", img: "resources/customisation/character_eyes/character_eyes22.png"},
|
||||||
|
"eyes_23": {name: "eyes_23", img: "resources/customisation/character_eyes/character_eyes23.png"},
|
||||||
|
"eyes_24": {name: "eyes_24", img: "resources/customisation/character_eyes/character_eyes24.png"},
|
||||||
|
"eyes_25": {name: "eyes_25", img: "resources/customisation/character_eyes/character_eyes25.png"},
|
||||||
|
"eyes_26": {name: "eyes_26", img: "resources/customisation/character_eyes/character_eyes26.png"},
|
||||||
|
"eyes_27": {name: "eyes_27", img: "resources/customisation/character_eyes/character_eyes27.png"},
|
||||||
|
"eyes_28": {name: "eyes_28", img: "resources/customisation/character_eyes/character_eyes28.png"},
|
||||||
|
"eyes_29": {name: "eyes_29", img: "resources/customisation/character_eyes/character_eyes29.png"},
|
||||||
|
"eyes_30": {name: "eyes_30", img: "resources/customisation/character_eyes/character_eyes30.png"}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HAIR_RESOURCES: BodyResourceDescriptionListInterface = {
|
||||||
|
"hair_1": {name:"hair_1", img: "resources/customisation/character_hairs/character_hairs0.png"},
|
||||||
|
"hair_2": {name:"hair_2", img: "resources/customisation/character_hairs/character_hairs1.png"},
|
||||||
|
"hair_3": {name:"hair_3", img: "resources/customisation/character_hairs/character_hairs2.png"},
|
||||||
|
"hair_4": {name:"hair_4", img: "resources/customisation/character_hairs/character_hairs3.png"},
|
||||||
|
"hair_5": {name:"hair_5", img: "resources/customisation/character_hairs/character_hairs4.png"},
|
||||||
|
"hair_6": {name:"hair_6", img: "resources/customisation/character_hairs/character_hairs5.png"},
|
||||||
|
"hair_7": {name:"hair_7", img: "resources/customisation/character_hairs/character_hairs6.png"},
|
||||||
|
"hair_8": {name:"hair_8", img: "resources/customisation/character_hairs/character_hairs7.png"},
|
||||||
|
"hair_9": {name:"hair_9", img: "resources/customisation/character_hairs/character_hairs8.png"},
|
||||||
|
"hair_10": {name:"hair_10",img: "resources/customisation/character_hairs/character_hairs9.png"},
|
||||||
|
"hair_11": {name:"hair_11",img: "resources/customisation/character_hairs/character_hairs10.png"},
|
||||||
|
"hair_12": {name:"hair_12",img: "resources/customisation/character_hairs/character_hairs11.png"},
|
||||||
|
"hair_13": {name:"hair_13",img: "resources/customisation/character_hairs/character_hairs12.png"},
|
||||||
|
"hair_14": {name:"hair_14",img: "resources/customisation/character_hairs/character_hairs13.png"},
|
||||||
|
"hair_15": {name:"hair_15",img: "resources/customisation/character_hairs/character_hairs14.png"},
|
||||||
|
"hair_16": {name:"hair_16",img: "resources/customisation/character_hairs/character_hairs15.png"},
|
||||||
|
"hair_17": {name:"hair_17",img: "resources/customisation/character_hairs/character_hairs16.png"},
|
||||||
|
"hair_18": {name:"hair_18",img: "resources/customisation/character_hairs/character_hairs17.png"},
|
||||||
|
"hair_19": {name:"hair_19",img: "resources/customisation/character_hairs/character_hairs18.png"},
|
||||||
|
"hair_20": {name:"hair_20",img: "resources/customisation/character_hairs/character_hairs19.png"},
|
||||||
|
"hair_21": {name:"hair_21",img: "resources/customisation/character_hairs/character_hairs20.png"},
|
||||||
|
"hair_22": {name:"hair_22",img: "resources/customisation/character_hairs/character_hairs21.png"},
|
||||||
|
"hair_23": {name:"hair_23",img: "resources/customisation/character_hairs/character_hairs22.png"},
|
||||||
|
"hair_24": {name:"hair_24",img: "resources/customisation/character_hairs/character_hairs23.png"},
|
||||||
|
"hair_25": {name:"hair_25",img: "resources/customisation/character_hairs/character_hairs24.png"},
|
||||||
|
"hair_26": {name:"hair_26",img: "resources/customisation/character_hairs/character_hairs25.png"},
|
||||||
|
"hair_27": {name:"hair_27",img: "resources/customisation/character_hairs/character_hairs26.png"},
|
||||||
|
"hair_28": {name:"hair_28",img: "resources/customisation/character_hairs/character_hairs27.png"},
|
||||||
|
"hair_29": {name:"hair_29",img: "resources/customisation/character_hairs/character_hairs28.png"},
|
||||||
|
"hair_30": {name:"hair_30",img: "resources/customisation/character_hairs/character_hairs29.png"},
|
||||||
|
"hair_31": {name:"hair_31",img: "resources/customisation/character_hairs/character_hairs30.png"},
|
||||||
|
"hair_32": {name:"hair_32",img: "resources/customisation/character_hairs/character_hairs31.png"},
|
||||||
|
"hair_33": {name:"hair_33",img: "resources/customisation/character_hairs/character_hairs32.png"},
|
||||||
|
"hair_34": {name:"hair_34",img: "resources/customisation/character_hairs/character_hairs33.png"},
|
||||||
|
"hair_35": {name:"hair_35",img: "resources/customisation/character_hairs/character_hairs34.png"},
|
||||||
|
"hair_36": {name:"hair_36",img: "resources/customisation/character_hairs/character_hairs35.png"},
|
||||||
|
"hair_37": {name:"hair_37",img: "resources/customisation/character_hairs/character_hairs36.png"},
|
||||||
|
"hair_38": {name:"hair_38",img: "resources/customisation/character_hairs/character_hairs37.png"},
|
||||||
|
"hair_39": {name:"hair_39",img: "resources/customisation/character_hairs/character_hairs38.png"},
|
||||||
|
"hair_40": {name:"hair_40",img: "resources/customisation/character_hairs/character_hairs39.png"},
|
||||||
|
"hair_41": {name:"hair_41",img: "resources/customisation/character_hairs/character_hairs40.png"},
|
||||||
|
"hair_42": {name:"hair_42",img: "resources/customisation/character_hairs/character_hairs41.png"},
|
||||||
|
"hair_43": {name:"hair_43",img: "resources/customisation/character_hairs/character_hairs42.png"},
|
||||||
|
"hair_44": {name:"hair_44",img: "resources/customisation/character_hairs/character_hairs43.png"},
|
||||||
|
"hair_45": {name:"hair_45",img: "resources/customisation/character_hairs/character_hairs44.png"},
|
||||||
|
"hair_46": {name:"hair_46",img: "resources/customisation/character_hairs/character_hairs45.png"},
|
||||||
|
"hair_47": {name:"hair_47",img: "resources/customisation/character_hairs/character_hairs46.png"},
|
||||||
|
"hair_48": {name:"hair_48",img: "resources/customisation/character_hairs/character_hairs47.png"},
|
||||||
|
"hair_49": {name:"hair_49",img: "resources/customisation/character_hairs/character_hairs48.png"},
|
||||||
|
"hair_50": {name:"hair_50",img: "resources/customisation/character_hairs/character_hairs49.png"},
|
||||||
|
"hair_51": {name:"hair_51",img: "resources/customisation/character_hairs/character_hairs50.png"},
|
||||||
|
"hair_52": {name:"hair_52",img: "resources/customisation/character_hairs/character_hairs51.png"},
|
||||||
|
"hair_53": {name:"hair_53",img: "resources/customisation/character_hairs/character_hairs52.png"},
|
||||||
|
"hair_54": {name:"hair_54",img: "resources/customisation/character_hairs/character_hairs53.png"},
|
||||||
|
"hair_55": {name:"hair_55",img: "resources/customisation/character_hairs/character_hairs54.png"},
|
||||||
|
"hair_56": {name:"hair_56",img: "resources/customisation/character_hairs/character_hairs55.png"},
|
||||||
|
"hair_57": {name:"hair_57",img: "resources/customisation/character_hairs/character_hairs56.png"},
|
||||||
|
"hair_58": {name:"hair_58",img: "resources/customisation/character_hairs/character_hairs57.png"},
|
||||||
|
"hair_59": {name:"hair_59",img: "resources/customisation/character_hairs/character_hairs58.png"},
|
||||||
|
"hair_60": {name:"hair_60",img: "resources/customisation/character_hairs/character_hairs59.png"},
|
||||||
|
"hair_61": {name:"hair_61",img: "resources/customisation/character_hairs/character_hairs60.png"},
|
||||||
|
"hair_62": {name:"hair_62",img: "resources/customisation/character_hairs/character_hairs61.png"},
|
||||||
|
"hair_63": {name:"hair_63",img: "resources/customisation/character_hairs/character_hairs62.png"},
|
||||||
|
"hair_64": {name:"hair_64",img: "resources/customisation/character_hairs/character_hairs63.png"},
|
||||||
|
"hair_65": {name:"hair_65",img: "resources/customisation/character_hairs/character_hairs64.png"},
|
||||||
|
"hair_66": {name:"hair_66",img: "resources/customisation/character_hairs/character_hairs65.png"},
|
||||||
|
"hair_67": {name:"hair_67",img: "resources/customisation/character_hairs/character_hairs66.png"},
|
||||||
|
"hair_68": {name:"hair_68",img: "resources/customisation/character_hairs/character_hairs67.png"},
|
||||||
|
"hair_69": {name:"hair_69",img: "resources/customisation/character_hairs/character_hairs68.png"},
|
||||||
|
"hair_70": {name:"hair_70",img: "resources/customisation/character_hairs/character_hairs69.png"},
|
||||||
|
"hair_71": {name:"hair_71",img: "resources/customisation/character_hairs/character_hairs70.png"},
|
||||||
|
"hair_72": {name:"hair_72",img: "resources/customisation/character_hairs/character_hairs71.png"},
|
||||||
|
"hair_73": {name:"hair_73",img: "resources/customisation/character_hairs/character_hairs72.png"},
|
||||||
|
"hair_74": {name:"hair_74",img: "resources/customisation/character_hairs/character_hairs73.png"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {
|
||||||
|
"clothes_1": {name:"clothes_1", img: "resources/customisation/character_clothes/character_clothes0.png"},
|
||||||
|
"clothes_2": {name:"clothes_2", img: "resources/customisation/character_clothes/character_clothes1.png"},
|
||||||
|
"clothes_3": {name:"clothes_3", img: "resources/customisation/character_clothes/character_clothes2.png"},
|
||||||
|
"clothes_4": {name:"clothes_4", img: "resources/customisation/character_clothes/character_clothes3.png"},
|
||||||
|
"clothes_5": {name:"clothes_5", img: "resources/customisation/character_clothes/character_clothes4.png"},
|
||||||
|
"clothes_6": {name:"clothes_6", img: "resources/customisation/character_clothes/character_clothes5.png"},
|
||||||
|
"clothes_7": {name:"clothes_7", img: "resources/customisation/character_clothes/character_clothes6.png"},
|
||||||
|
"clothes_8": {name:"clothes_8", img: "resources/customisation/character_clothes/character_clothes7.png"},
|
||||||
|
"clothes_9": {name:"clothes_9", img: "resources/customisation/character_clothes/character_clothes8.png"},
|
||||||
|
"clothes_10": {name:"clothes_10",img: "resources/customisation/character_clothes/character_clothes9.png"},
|
||||||
|
"clothes_11": {name:"clothes_11",img: "resources/customisation/character_clothes/character_clothes10.png"},
|
||||||
|
"clothes_12": {name:"clothes_12",img: "resources/customisation/character_clothes/character_clothes11.png"},
|
||||||
|
"clothes_13": {name:"clothes_13",img: "resources/customisation/character_clothes/character_clothes12.png"},
|
||||||
|
"clothes_14": {name:"clothes_14",img: "resources/customisation/character_clothes/character_clothes13.png"},
|
||||||
|
"clothes_15": {name:"clothes_15",img: "resources/customisation/character_clothes/character_clothes14.png"},
|
||||||
|
"clothes_16": {name:"clothes_16",img: "resources/customisation/character_clothes/character_clothes15.png"},
|
||||||
|
"clothes_17": {name:"clothes_17",img: "resources/customisation/character_clothes/character_clothes16.png"},
|
||||||
|
"clothes_18": {name:"clothes_18",img: "resources/customisation/character_clothes/character_clothes17.png"},
|
||||||
|
"clothes_19": {name:"clothes_19",img: "resources/customisation/character_clothes/character_clothes18.png"},
|
||||||
|
"clothes_20": {name:"clothes_20",img: "resources/customisation/character_clothes/character_clothes19.png"},
|
||||||
|
"clothes_21": {name:"clothes_21",img: "resources/customisation/character_clothes/character_clothes20.png"},
|
||||||
|
"clothes_22": {name:"clothes_22",img: "resources/customisation/character_clothes/character_clothes21.png"},
|
||||||
|
"clothes_23": {name:"clothes_23",img: "resources/customisation/character_clothes/character_clothes22.png"},
|
||||||
|
"clothes_24": {name:"clothes_24",img: "resources/customisation/character_clothes/character_clothes23.png"},
|
||||||
|
"clothes_25": {name:"clothes_25",img: "resources/customisation/character_clothes/character_clothes24.png"},
|
||||||
|
"clothes_26": {name:"clothes_26",img: "resources/customisation/character_clothes/character_clothes25.png"},
|
||||||
|
"clothes_27": {name:"clothes_27",img: "resources/customisation/character_clothes/character_clothes26.png"},
|
||||||
|
"clothes_28": {name:"clothes_28",img: "resources/customisation/character_clothes/character_clothes27.png"},
|
||||||
|
"clothes_29": {name:"clothes_29",img: "resources/customisation/character_clothes/character_clothes28.png"},
|
||||||
|
"clothes_30": {name:"clothes_30",img: "resources/customisation/character_clothes/character_clothes29.png"},
|
||||||
|
"clothes_31": {name:"clothes_31",img: "resources/customisation/character_clothes/character_clothes30.png"},
|
||||||
|
"clothes_32": {name:"clothes_32",img: "resources/customisation/character_clothes/character_clothes31.png"},
|
||||||
|
"clothes_33": {name:"clothes_33",img: "resources/customisation/character_clothes/character_clothes32.png"},
|
||||||
|
"clothes_34": {name:"clothes_34",img: "resources/customisation/character_clothes/character_clothes33.png"},
|
||||||
|
"clothes_35": {name:"clothes_35",img: "resources/customisation/character_clothes/character_clothes34.png"},
|
||||||
|
"clothes_36": {name:"clothes_36",img: "resources/customisation/character_clothes/character_clothes35.png"},
|
||||||
|
"clothes_37": {name:"clothes_37",img: "resources/customisation/character_clothes/character_clothes36.png"},
|
||||||
|
"clothes_38": {name:"clothes_38",img: "resources/customisation/character_clothes/character_clothes37.png"},
|
||||||
|
"clothes_39": {name:"clothes_39",img: "resources/customisation/character_clothes/character_clothes38.png"},
|
||||||
|
"clothes_40": {name:"clothes_40",img: "resources/customisation/character_clothes/character_clothes39.png"},
|
||||||
|
"clothes_41": {name:"clothes_41",img: "resources/customisation/character_clothes/character_clothes40.png"},
|
||||||
|
"clothes_42": {name:"clothes_42",img: "resources/customisation/character_clothes/character_clothes41.png"},
|
||||||
|
"clothes_43": {name:"clothes_43",img: "resources/customisation/character_clothes/character_clothes42.png"},
|
||||||
|
"clothes_44": {name:"clothes_44",img: "resources/customisation/character_clothes/character_clothes43.png"},
|
||||||
|
"clothes_45": {name:"clothes_45",img: "resources/customisation/character_clothes/character_clothes44.png"},
|
||||||
|
"clothes_46": {name:"clothes_46",img: "resources/customisation/character_clothes/character_clothes45.png"},
|
||||||
|
"clothes_47": {name:"clothes_47",img: "resources/customisation/character_clothes/character_clothes46.png"},
|
||||||
|
"clothes_48": {name:"clothes_48",img: "resources/customisation/character_clothes/character_clothes47.png"},
|
||||||
|
"clothes_49": {name:"clothes_49",img: "resources/customisation/character_clothes/character_clothes48.png"},
|
||||||
|
"clothes_50": {name:"clothes_50",img: "resources/customisation/character_clothes/character_clothes49.png"},
|
||||||
|
"clothes_51": {name:"clothes_51",img: "resources/customisation/character_clothes/character_clothes50.png"},
|
||||||
|
"clothes_52": {name:"clothes_52",img: "resources/customisation/character_clothes/character_clothes51.png"},
|
||||||
|
"clothes_53": {name:"clothes_53",img: "resources/customisation/character_clothes/character_clothes52.png"},
|
||||||
|
"clothes_54": {name:"clothes_54",img: "resources/customisation/character_clothes/character_clothes53.png"},
|
||||||
|
"clothes_55": {name:"clothes_55",img: "resources/customisation/character_clothes/character_clothes54.png"},
|
||||||
|
"clothes_56": {name:"clothes_56",img: "resources/customisation/character_clothes/character_clothes55.png"},
|
||||||
|
"clothes_57": {name:"clothes_57",img: "resources/customisation/character_clothes/character_clothes56.png"},
|
||||||
|
"clothes_58": {name:"clothes_58",img: "resources/customisation/character_clothes/character_clothes57.png"},
|
||||||
|
"clothes_59": {name:"clothes_59",img: "resources/customisation/character_clothes/character_clothes58.png"},
|
||||||
|
"clothes_60": {name:"clothes_60",img: "resources/customisation/character_clothes/character_clothes59.png"},
|
||||||
|
"clothes_61": {name:"clothes_61",img: "resources/customisation/character_clothes/character_clothes60.png"},
|
||||||
|
"clothes_62": {name:"clothes_62",img: "resources/customisation/character_clothes/character_clothes61.png"},
|
||||||
|
"clothes_63": {name:"clothes_63",img: "resources/customisation/character_clothes/character_clothes62.png"},
|
||||||
|
"clothes_64": {name:"clothes_64",img: "resources/customisation/character_clothes/character_clothes63.png"},
|
||||||
|
"clothes_65": {name:"clothes_65",img: "resources/customisation/character_clothes/character_clothes64.png"},
|
||||||
|
"clothes_66": {name:"clothes_66",img: "resources/customisation/character_clothes/character_clothes65.png"},
|
||||||
|
"clothes_67": {name:"clothes_67",img: "resources/customisation/character_clothes/character_clothes66.png"},
|
||||||
|
"clothes_68": {name:"clothes_68",img: "resources/customisation/character_clothes/character_clothes67.png"},
|
||||||
|
"clothes_69": {name:"clothes_69",img: "resources/customisation/character_clothes/character_clothes68.png"},
|
||||||
|
"clothes_70": {name:"clothes_70",img: "resources/customisation/character_clothes/character_clothes69.png"},
|
||||||
|
"clothes_pride_shirt": {name:"clothes_pride_shirt",img: "resources/customisation/character_clothes/pride_shirt.png"},
|
||||||
|
"clothes_black_hoodie": {name:"clothes_black_hoodie",img: "resources/customisation/character_clothes/black_hoodie.png"},
|
||||||
|
"clothes_white_hoodie": {name:"clothes_white_hoodie",img: "resources/customisation/character_clothes/white_hoodie.png"},
|
||||||
|
"clothes_engelbert": {name:"clothes_engelbert",img: "resources/customisation/character_clothes/engelbert.png"}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HATS_RESOURCES: BodyResourceDescriptionListInterface = {
|
||||||
|
"hats_1": {name: "hats_1", img: "resources/customisation/character_hats/character_hats1.png"},
|
||||||
|
"hats_2": {name: "hats_2", img: "resources/customisation/character_hats/character_hats2.png"},
|
||||||
|
"hats_3": {name: "hats_3", img: "resources/customisation/character_hats/character_hats3.png"},
|
||||||
|
"hats_4": {name: "hats_4", img: "resources/customisation/character_hats/character_hats4.png"},
|
||||||
|
"hats_5": {name: "hats_5", img: "resources/customisation/character_hats/character_hats5.png"},
|
||||||
|
"hats_6": {name: "hats_6", img: "resources/customisation/character_hats/character_hats6.png"},
|
||||||
|
"hats_7": {name: "hats_7", img: "resources/customisation/character_hats/character_hats7.png"},
|
||||||
|
"hats_8": {name: "hats_8", img: "resources/customisation/character_hats/character_hats8.png"},
|
||||||
|
"hats_9": {name: "hats_9", img: "resources/customisation/character_hats/character_hats9.png"},
|
||||||
|
"hats_10": {name: "hats_10", img: "resources/customisation/character_hats/character_hats10.png"},
|
||||||
|
"hats_11": {name: "hats_11", img: "resources/customisation/character_hats/character_hats11.png"},
|
||||||
|
"hats_12": {name: "hats_12", img: "resources/customisation/character_hats/character_hats12.png"},
|
||||||
|
"hats_13": {name: "hats_13", img: "resources/customisation/character_hats/character_hats13.png"},
|
||||||
|
"hats_14": {name: "hats_14", img: "resources/customisation/character_hats/character_hats14.png"},
|
||||||
|
"hats_15": {name: "hats_15", img: "resources/customisation/character_hats/character_hats15.png"},
|
||||||
|
"hats_16": {name: "hats_16", img: "resources/customisation/character_hats/character_hats16.png"},
|
||||||
|
"hats_17": {name: "hats_17", img: "resources/customisation/character_hats/character_hats17.png"},
|
||||||
|
"hats_18": {name: "hats_18", img: "resources/customisation/character_hats/character_hats18.png"},
|
||||||
|
"hats_19": {name: "hats_19", img: "resources/customisation/character_hats/character_hats19.png"},
|
||||||
|
"hats_20": {name: "hats_20", img: "resources/customisation/character_hats/character_hats20.png"},
|
||||||
|
"hats_21": {name: "hats_21", img: "resources/customisation/character_hats/character_hats21.png"},
|
||||||
|
"hats_22": {name: "hats_22", img: "resources/customisation/character_hats/character_hats22.png"},
|
||||||
|
"hats_23": {name: "hats_23", img: "resources/customisation/character_hats/character_hats23.png"},
|
||||||
|
"hats_24": {name: "hats_24", img: "resources/customisation/character_hats/character_hats24.png"},
|
||||||
|
"hats_25": {name: "hats_25", img: "resources/customisation/character_hats/character_hats25.png"},
|
||||||
|
"hats_26": {name: "hats_26", img: "resources/customisation/character_hats/character_hats26.png"},
|
||||||
|
"tinfoil_hat1": {name: "tinfoil_hat1", img: "resources/customisation/character_hats/tinfoil_hat1.png"}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {
|
||||||
|
"accessory_1": {name: "accessory_1", img: "resources/customisation/character_accessories/character_accessories1.png"},
|
||||||
|
"accessory_2": {name: "accessory_2", img: "resources/customisation/character_accessories/character_accessories2.png"},
|
||||||
|
"accessory_3": {name: "accessory_3", img: "resources/customisation/character_accessories/character_accessories3.png"},
|
||||||
|
"accessory_4": {name: "accessory_4", img: "resources/customisation/character_accessories/character_accessories4.png"},
|
||||||
|
"accessory_5": {name: "accessory_5", img: "resources/customisation/character_accessories/character_accessories5.png"},
|
||||||
|
"accessory_6": {name: "accessory_6", img: "resources/customisation/character_accessories/character_accessories6.png"},
|
||||||
|
"accessory_7": {name: "accessory_7", img: "resources/customisation/character_accessories/character_accessories7.png"},
|
||||||
|
"accessory_8": {name: "accessory_8", img: "resources/customisation/character_accessories/character_accessories8.png"},
|
||||||
|
"accessory_9": {name: "accessory_9", img: "resources/customisation/character_accessories/character_accessories9.png"},
|
||||||
|
"accessory_10": {name: "accessory_10", img: "resources/customisation/character_accessories/character_accessories10.png"},
|
||||||
|
"accessory_11": {name: "accessory_11", img: "resources/customisation/character_accessories/character_accessories11.png"},
|
||||||
|
"accessory_12": {name: "accessory_12", img: "resources/customisation/character_accessories/character_accessories12.png"},
|
||||||
|
"accessory_13": {name: "accessory_13", img: "resources/customisation/character_accessories/character_accessories13.png"},
|
||||||
|
"accessory_14": {name: "accessory_14", img: "resources/customisation/character_accessories/character_accessories14.png"},
|
||||||
|
"accessory_15": {name: "accessory_15", img: "resources/customisation/character_accessories/character_accessories15.png"},
|
||||||
|
"accessory_16": {name: "accessory_16", img: "resources/customisation/character_accessories/character_accessories16.png"},
|
||||||
|
"accessory_17": {name: "accessory_17", img: "resources/customisation/character_accessories/character_accessories17.png"},
|
||||||
|
"accessory_18": {name: "accessory_18", img: "resources/customisation/character_accessories/character_accessories18.png"},
|
||||||
|
"accessory_19": {name: "accessory_19", img: "resources/customisation/character_accessories/character_accessories19.png"},
|
||||||
|
"accessory_20": {name: "accessory_20", img: "resources/customisation/character_accessories/character_accessories20.png"},
|
||||||
|
"accessory_21": {name: "accessory_21", img: "resources/customisation/character_accessories/character_accessories21.png"},
|
||||||
|
"accessory_22": {name: "accessory_22", img: "resources/customisation/character_accessories/character_accessories22.png"},
|
||||||
|
"accessory_23": {name: "accessory_23", img: "resources/customisation/character_accessories/character_accessories23.png"},
|
||||||
|
"accessory_24": {name: "accessory_24", img: "resources/customisation/character_accessories/character_accessories24.png"},
|
||||||
|
"accessory_25": {name: "accessory_25", img: "resources/customisation/character_accessories/character_accessories25.png"},
|
||||||
|
"accessory_26": {name: "accessory_26", img: "resources/customisation/character_accessories/character_accessories26.png"},
|
||||||
|
"accessory_27": {name: "accessory_27", img: "resources/customisation/character_accessories/character_accessories27.png"},
|
||||||
|
"accessory_28": {name: "accessory_28", img: "resources/customisation/character_accessories/character_accessories28.png"},
|
||||||
|
"accessory_29": {name: "accessory_29", img: "resources/customisation/character_accessories/character_accessories29.png"},
|
||||||
|
"accessory_30": {name: "accessory_30", img: "resources/customisation/character_accessories/character_accessories30.png"},
|
||||||
|
"accessory_31": {name: "accessory_31", img: "resources/customisation/character_accessories/character_accessories31.png"},
|
||||||
|
"accessory_32": {name: "accessory_32", img: "resources/customisation/character_accessories/character_accessories32.png"},
|
||||||
|
"accessory_mate_bottle": {name: "accessory_mate_bottle", img: "resources/customisation/character_accessories/mate_bottle1.png"},
|
||||||
|
"accessory_mask": {name: "accessory_mask", img: "resources/customisation/character_accessories/mask.png"}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LAYERS: BodyResourceDescriptionListInterface[] = [
|
||||||
|
COLOR_RESOURCES,
|
||||||
|
EYES_RESOURCES,
|
||||||
|
HAIR_RESOURCES,
|
||||||
|
CLOTHES_RESOURCES,
|
||||||
|
HATS_RESOURCES,
|
||||||
|
ACCESSORIES_RESOURCES
|
||||||
|
];
|
||||||
|
|
||||||
|
export const OBJECTS: BodyResourceDescriptionInterface[] = [
|
||||||
|
{name:'teleportation', img:'resources/objects/teleportation.png'},
|
||||||
|
];
|
84
front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||||
|
import TextureManager = Phaser.Textures.TextureManager;
|
||||||
|
import {CharacterTexture} from "../../Connexion/LocalUser";
|
||||||
|
import {BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES} from "./PlayerTextures";
|
||||||
|
|
||||||
|
|
||||||
|
export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => {
|
||||||
|
const returnArray:BodyResourceDescriptionInterface[][] = [];
|
||||||
|
LAYERS.forEach(layer => {
|
||||||
|
const layerArray:BodyResourceDescriptionInterface[] = [];
|
||||||
|
Object.values(layer).forEach((textureDescriptor) => {
|
||||||
|
layerArray.push(textureDescriptor);
|
||||||
|
load.spritesheet(textureDescriptor.name,textureDescriptor.img,{frameWidth: 32, frameHeight: 32});
|
||||||
|
})
|
||||||
|
returnArray.push(layerArray)
|
||||||
|
});
|
||||||
|
return returnArray;
|
||||||
|
}
|
||||||
|
export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => {
|
||||||
|
const returnArray = Object.values(PLAYER_RESOURCES);
|
||||||
|
returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => {
|
||||||
|
load.spritesheet(playerResource.name, playerResource.img, {frameWidth: 32, frameHeight: 32});
|
||||||
|
});
|
||||||
|
return returnArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadCustomTexture = (loaderPlugin: LoaderPlugin, texture: CharacterTexture) : Promise<BodyResourceDescriptionInterface> => {
|
||||||
|
const name = 'customCharacterTexture'+texture.id;
|
||||||
|
const playerResourceDescriptor: BodyResourceDescriptionInterface = {name, img: texture.url, level: texture.level}
|
||||||
|
return createLoadingPromise(loaderPlugin, playerResourceDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const lazyLoadPlayerCharacterTextures = (loadPlugin: LoaderPlugin, texturekeys:Array<string|BodyResourceDescriptionInterface>): Promise<string[]> => {
|
||||||
|
const promisesList:Promise<unknown>[] = [];
|
||||||
|
texturekeys.forEach((textureKey: string|BodyResourceDescriptionInterface) => {
|
||||||
|
try {
|
||||||
|
//TODO refactor
|
||||||
|
const playerResourceDescriptor = getRessourceDescriptor(textureKey);
|
||||||
|
if (playerResourceDescriptor && !loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
|
||||||
|
promisesList.push(createLoadingPromise(loadPlugin, playerResourceDescriptor));
|
||||||
|
}
|
||||||
|
}catch (err){
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let returnPromise:Promise<Array<string|BodyResourceDescriptionInterface>>;
|
||||||
|
if (promisesList.length > 0) {
|
||||||
|
loadPlugin.start();
|
||||||
|
returnPromise = Promise.all(promisesList).then(() => texturekeys);
|
||||||
|
} else {
|
||||||
|
returnPromise = Promise.resolve(texturekeys);
|
||||||
|
}
|
||||||
|
return returnPromise.then((keys) => keys.map((key) => {
|
||||||
|
return typeof key !== 'string' ? key.name : key;
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRessourceDescriptor = (textureKey: string|BodyResourceDescriptionInterface): BodyResourceDescriptionInterface => {
|
||||||
|
if (typeof textureKey !== 'string' && textureKey.img) {
|
||||||
|
return textureKey;
|
||||||
|
}
|
||||||
|
const textureName:string = typeof textureKey === 'string' ? textureKey : textureKey.name;
|
||||||
|
const playerResource = PLAYER_RESOURCES[textureName];
|
||||||
|
if (playerResource !== undefined) return playerResource;
|
||||||
|
|
||||||
|
for (let i=0; i<LAYERS.length;i++) {
|
||||||
|
const playerResource = LAYERS[i][textureName];
|
||||||
|
if (playerResource !== undefined) return playerResource;
|
||||||
|
}
|
||||||
|
throw 'Could not find a data for texture '+textureName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createLoadingPromise = (loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) => {
|
||||||
|
return new Promise<BodyResourceDescriptionInterface>((res) => {
|
||||||
|
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
|
||||||
|
return res(playerResourceDescriptor);
|
||||||
|
}
|
||||||
|
loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, {
|
||||||
|
frameWidth: 32,
|
||||||
|
frameHeight: 32
|
||||||
|
});
|
||||||
|
loadPlugin.once('filecomplete-spritesheet-' + playerResourceDescriptor.name, () => res(playerResourceDescriptor));
|
||||||
|
});
|
||||||
|
}
|
@ -15,11 +15,11 @@ export class RemotePlayer extends Character {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
name: string,
|
name: string,
|
||||||
PlayerTextures: string[],
|
texturesPromise: Promise<string[]>,
|
||||||
direction: string,
|
direction: string,
|
||||||
moving: boolean
|
moving: boolean
|
||||||
) {
|
) {
|
||||||
super(Scene, x, y, PlayerTextures, name, direction, moving, 1);
|
super(Scene, x, y, texturesPromise, name, direction, moving, 1);
|
||||||
|
|
||||||
//set data
|
//set data
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
|
@ -1,355 +0,0 @@
|
|||||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
|
||||||
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "./Character";
|
|
||||||
import {CharacterTexture} from "../../Connexion/LocalUser";
|
|
||||||
|
|
||||||
export interface BodyResourceDescriptionInterface {
|
|
||||||
name: string,
|
|
||||||
img: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const COLOR_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
|
||||||
{name:"color_1", img: "resources/customisation/character_color/character_color0.png"},
|
|
||||||
{name:"color_2", img: "resources/customisation/character_color/character_color1.png"},
|
|
||||||
{name:"color_3", img: "resources/customisation/character_color/character_color2.png"},
|
|
||||||
{name:"color_4", img: "resources/customisation/character_color/character_color3.png"},
|
|
||||||
{name:"color_5", img: "resources/customisation/character_color/character_color4.png"},
|
|
||||||
{name:"color_6", img: "resources/customisation/character_color/character_color5.png"},
|
|
||||||
{name:"color_7", img: "resources/customisation/character_color/character_color6.png"},
|
|
||||||
{name:"color_8", img: "resources/customisation/character_color/character_color7.png"},
|
|
||||||
{name:"color_9", img: "resources/customisation/character_color/character_color8.png"},
|
|
||||||
{name:"color_10",img: "resources/customisation/character_color/character_color9.png"},
|
|
||||||
{name:"color_11",img: "resources/customisation/character_color/character_color10.png"},
|
|
||||||
{name:"color_12",img: "resources/customisation/character_color/character_color11.png"},
|
|
||||||
{name:"color_13",img: "resources/customisation/character_color/character_color12.png"},
|
|
||||||
{name:"color_14",img: "resources/customisation/character_color/character_color13.png"},
|
|
||||||
{name:"color_15",img: "resources/customisation/character_color/character_color14.png"},
|
|
||||||
{name:"color_16",img: "resources/customisation/character_color/character_color15.png"},
|
|
||||||
{name:"color_17",img: "resources/customisation/character_color/character_color16.png"},
|
|
||||||
{name:"color_18",img: "resources/customisation/character_color/character_color17.png"},
|
|
||||||
{name:"color_19",img: "resources/customisation/character_color/character_color18.png"},
|
|
||||||
{name:"color_20",img: "resources/customisation/character_color/character_color19.png"},
|
|
||||||
{name:"color_21",img: "resources/customisation/character_color/character_color20.png"},
|
|
||||||
{name:"color_22",img: "resources/customisation/character_color/character_color21.png"},
|
|
||||||
{name:"color_23",img: "resources/customisation/character_color/character_color22.png"},
|
|
||||||
{name:"color_24",img: "resources/customisation/character_color/character_color23.png"},
|
|
||||||
{name:"color_25",img: "resources/customisation/character_color/character_color24.png"},
|
|
||||||
{name:"color_26",img: "resources/customisation/character_color/character_color25.png"},
|
|
||||||
{name:"color_27",img: "resources/customisation/character_color/character_color26.png"},
|
|
||||||
{name:"color_28",img: "resources/customisation/character_color/character_color27.png"},
|
|
||||||
{name:"color_29",img: "resources/customisation/character_color/character_color28.png"},
|
|
||||||
{name:"color_30",img: "resources/customisation/character_color/character_color29.png"},
|
|
||||||
{name:"color_31",img: "resources/customisation/character_color/character_color30.png"},
|
|
||||||
{name:"color_32",img: "resources/customisation/character_color/character_color31.png"},
|
|
||||||
{name:"color_33",img: "resources/customisation/character_color/character_color32.png"}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const EYES_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
|
||||||
{name: "eyes_1", img: "resources/customisation/character_eyes/character_eyes1.png"},
|
|
||||||
{name: "eyes_2", img: "resources/customisation/character_eyes/character_eyes2.png"},
|
|
||||||
{name: "eyes_3", img: "resources/customisation/character_eyes/character_eyes3.png"},
|
|
||||||
{name: "eyes_4", img: "resources/customisation/character_eyes/character_eyes4.png"},
|
|
||||||
{name: "eyes_5", img: "resources/customisation/character_eyes/character_eyes5.png"},
|
|
||||||
{name: "eyes_6", img: "resources/customisation/character_eyes/character_eyes6.png"},
|
|
||||||
{name: "eyes_7", img: "resources/customisation/character_eyes/character_eyes7.png"},
|
|
||||||
{name: "eyes_8", img: "resources/customisation/character_eyes/character_eyes8.png"},
|
|
||||||
{name: "eyes_9", img: "resources/customisation/character_eyes/character_eyes9.png"},
|
|
||||||
{name: "eyes_10", img: "resources/customisation/character_eyes/character_eyes10.png"},
|
|
||||||
{name: "eyes_11", img: "resources/customisation/character_eyes/character_eyes11.png"},
|
|
||||||
{name: "eyes_12", img: "resources/customisation/character_eyes/character_eyes12.png"},
|
|
||||||
{name: "eyes_13", img: "resources/customisation/character_eyes/character_eyes13.png"},
|
|
||||||
{name: "eyes_14", img: "resources/customisation/character_eyes/character_eyes14.png"},
|
|
||||||
{name: "eyes_15", img: "resources/customisation/character_eyes/character_eyes15.png"},
|
|
||||||
{name: "eyes_16", img: "resources/customisation/character_eyes/character_eyes16.png"},
|
|
||||||
{name: "eyes_17", img: "resources/customisation/character_eyes/character_eyes17.png"},
|
|
||||||
{name: "eyes_18", img: "resources/customisation/character_eyes/character_eyes18.png"},
|
|
||||||
{name: "eyes_19", img: "resources/customisation/character_eyes/character_eyes19.png"},
|
|
||||||
{name: "eyes_20", img: "resources/customisation/character_eyes/character_eyes20.png"},
|
|
||||||
{name: "eyes_21", img: "resources/customisation/character_eyes/character_eyes21.png"},
|
|
||||||
{name: "eyes_22", img: "resources/customisation/character_eyes/character_eyes22.png"},
|
|
||||||
{name: "eyes_23", img: "resources/customisation/character_eyes/character_eyes23.png"},
|
|
||||||
{name: "eyes_24", img: "resources/customisation/character_eyes/character_eyes24.png"},
|
|
||||||
{name: "eyes_25", img: "resources/customisation/character_eyes/character_eyes25.png"},
|
|
||||||
{name: "eyes_26", img: "resources/customisation/character_eyes/character_eyes26.png"},
|
|
||||||
{name: "eyes_27", img: "resources/customisation/character_eyes/character_eyes27.png"},
|
|
||||||
{name: "eyes_28", img: "resources/customisation/character_eyes/character_eyes28.png"},
|
|
||||||
{name: "eyes_29", img: "resources/customisation/character_eyes/character_eyes29.png"},
|
|
||||||
{name: "eyes_30", img: "resources/customisation/character_eyes/character_eyes30.png"}
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
export const HAIR_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
|
||||||
{name:"hair_1", img: "resources/customisation/character_hairs/character_hairs0.png"},
|
|
||||||
{name:"hair_2", img: "resources/customisation/character_hairs/character_hairs1.png"},
|
|
||||||
{name:"hair_3", img: "resources/customisation/character_hairs/character_hairs2.png"},
|
|
||||||
{name:"hair_4", img: "resources/customisation/character_hairs/character_hairs3.png"},
|
|
||||||
{name:"hair_5", img: "resources/customisation/character_hairs/character_hairs4.png"},
|
|
||||||
{name:"hair_6", img: "resources/customisation/character_hairs/character_hairs5.png"},
|
|
||||||
{name:"hair_7", img: "resources/customisation/character_hairs/character_hairs6.png"},
|
|
||||||
{name:"hair_8", img: "resources/customisation/character_hairs/character_hairs7.png"},
|
|
||||||
{name:"hair_9", img: "resources/customisation/character_hairs/character_hairs8.png"},
|
|
||||||
{name:"hair_10",img: "resources/customisation/character_hairs/character_hairs9.png"},
|
|
||||||
{name:"hair_11",img: "resources/customisation/character_hairs/character_hairs10.png"},
|
|
||||||
{name:"hair_12",img: "resources/customisation/character_hairs/character_hairs11.png"},
|
|
||||||
{name:"hair_13",img: "resources/customisation/character_hairs/character_hairs12.png"},
|
|
||||||
{name:"hair_14",img: "resources/customisation/character_hairs/character_hairs13.png"},
|
|
||||||
{name:"hair_15",img: "resources/customisation/character_hairs/character_hairs14.png"},
|
|
||||||
{name:"hair_16",img: "resources/customisation/character_hairs/character_hairs15.png"},
|
|
||||||
{name:"hair_17",img: "resources/customisation/character_hairs/character_hairs16.png"},
|
|
||||||
{name:"hair_18",img: "resources/customisation/character_hairs/character_hairs17.png"},
|
|
||||||
{name:"hair_19",img: "resources/customisation/character_hairs/character_hairs18.png"},
|
|
||||||
{name:"hair_20",img: "resources/customisation/character_hairs/character_hairs19.png"},
|
|
||||||
{name:"hair_21",img: "resources/customisation/character_hairs/character_hairs20.png"},
|
|
||||||
{name:"hair_22",img: "resources/customisation/character_hairs/character_hairs21.png"},
|
|
||||||
{name:"hair_23",img: "resources/customisation/character_hairs/character_hairs22.png"},
|
|
||||||
{name:"hair_24",img: "resources/customisation/character_hairs/character_hairs23.png"},
|
|
||||||
{name:"hair_25",img: "resources/customisation/character_hairs/character_hairs24.png"},
|
|
||||||
{name:"hair_26",img: "resources/customisation/character_hairs/character_hairs25.png"},
|
|
||||||
{name:"hair_27",img: "resources/customisation/character_hairs/character_hairs26.png"},
|
|
||||||
{name:"hair_28",img: "resources/customisation/character_hairs/character_hairs27.png"},
|
|
||||||
{name:"hair_29",img: "resources/customisation/character_hairs/character_hairs28.png"},
|
|
||||||
{name:"hair_30",img: "resources/customisation/character_hairs/character_hairs29.png"},
|
|
||||||
{name:"hair_31",img: "resources/customisation/character_hairs/character_hairs30.png"},
|
|
||||||
{name:"hair_32",img: "resources/customisation/character_hairs/character_hairs31.png"},
|
|
||||||
{name:"hair_33",img: "resources/customisation/character_hairs/character_hairs32.png"},
|
|
||||||
{name:"hair_34",img: "resources/customisation/character_hairs/character_hairs33.png"},
|
|
||||||
{name:"hair_35",img: "resources/customisation/character_hairs/character_hairs34.png"},
|
|
||||||
{name:"hair_36",img: "resources/customisation/character_hairs/character_hairs35.png"},
|
|
||||||
{name:"hair_37",img: "resources/customisation/character_hairs/character_hairs36.png"},
|
|
||||||
{name:"hair_38",img: "resources/customisation/character_hairs/character_hairs37.png"},
|
|
||||||
{name:"hair_39",img: "resources/customisation/character_hairs/character_hairs38.png"},
|
|
||||||
{name:"hair_40",img: "resources/customisation/character_hairs/character_hairs39.png"},
|
|
||||||
{name:"hair_41",img: "resources/customisation/character_hairs/character_hairs40.png"},
|
|
||||||
{name:"hair_42",img: "resources/customisation/character_hairs/character_hairs41.png"},
|
|
||||||
{name:"hair_43",img: "resources/customisation/character_hairs/character_hairs42.png"},
|
|
||||||
{name:"hair_44",img: "resources/customisation/character_hairs/character_hairs43.png"},
|
|
||||||
{name:"hair_45",img: "resources/customisation/character_hairs/character_hairs44.png"},
|
|
||||||
{name:"hair_46",img: "resources/customisation/character_hairs/character_hairs45.png"},
|
|
||||||
{name:"hair_47",img: "resources/customisation/character_hairs/character_hairs46.png"},
|
|
||||||
{name:"hair_48",img: "resources/customisation/character_hairs/character_hairs47.png"},
|
|
||||||
{name:"hair_49",img: "resources/customisation/character_hairs/character_hairs48.png"},
|
|
||||||
{name:"hair_50",img: "resources/customisation/character_hairs/character_hairs49.png"},
|
|
||||||
{name:"hair_51",img: "resources/customisation/character_hairs/character_hairs50.png"},
|
|
||||||
{name:"hair_52",img: "resources/customisation/character_hairs/character_hairs51.png"},
|
|
||||||
{name:"hair_53",img: "resources/customisation/character_hairs/character_hairs52.png"},
|
|
||||||
{name:"hair_54",img: "resources/customisation/character_hairs/character_hairs53.png"},
|
|
||||||
{name:"hair_55",img: "resources/customisation/character_hairs/character_hairs54.png"},
|
|
||||||
{name:"hair_56",img: "resources/customisation/character_hairs/character_hairs55.png"},
|
|
||||||
{name:"hair_57",img: "resources/customisation/character_hairs/character_hairs56.png"},
|
|
||||||
{name:"hair_58",img: "resources/customisation/character_hairs/character_hairs57.png"},
|
|
||||||
{name:"hair_59",img: "resources/customisation/character_hairs/character_hairs58.png"},
|
|
||||||
{name:"hair_60",img: "resources/customisation/character_hairs/character_hairs59.png"},
|
|
||||||
{name:"hair_61",img: "resources/customisation/character_hairs/character_hairs60.png"},
|
|
||||||
{name:"hair_62",img: "resources/customisation/character_hairs/character_hairs61.png"},
|
|
||||||
{name:"hair_63",img: "resources/customisation/character_hairs/character_hairs62.png"},
|
|
||||||
{name:"hair_64",img: "resources/customisation/character_hairs/character_hairs63.png"},
|
|
||||||
{name:"hair_65",img: "resources/customisation/character_hairs/character_hairs64.png"},
|
|
||||||
{name:"hair_66",img: "resources/customisation/character_hairs/character_hairs65.png"},
|
|
||||||
{name:"hair_67",img: "resources/customisation/character_hairs/character_hairs66.png"},
|
|
||||||
{name:"hair_68",img: "resources/customisation/character_hairs/character_hairs67.png"},
|
|
||||||
{name:"hair_69",img: "resources/customisation/character_hairs/character_hairs68.png"},
|
|
||||||
{name:"hair_70",img: "resources/customisation/character_hairs/character_hairs69.png"},
|
|
||||||
{name:"hair_71",img: "resources/customisation/character_hairs/character_hairs70.png"},
|
|
||||||
{name:"hair_72",img: "resources/customisation/character_hairs/character_hairs71.png"},
|
|
||||||
{name:"hair_73",img: "resources/customisation/character_hairs/character_hairs72.png"},
|
|
||||||
{name:"hair_74",img: "resources/customisation/character_hairs/character_hairs73.png"}
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
export const CLOTHES_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
|
||||||
{name:"clothes_1", img: "resources/customisation/character_clothes/character_clothes0.png"},
|
|
||||||
{name:"clothes_2", img: "resources/customisation/character_clothes/character_clothes1.png"},
|
|
||||||
{name:"clothes_3", img: "resources/customisation/character_clothes/character_clothes2.png"},
|
|
||||||
{name:"clothes_4", img: "resources/customisation/character_clothes/character_clothes3.png"},
|
|
||||||
{name:"clothes_5", img: "resources/customisation/character_clothes/character_clothes4.png"},
|
|
||||||
{name:"clothes_6", img: "resources/customisation/character_clothes/character_clothes5.png"},
|
|
||||||
{name:"clothes_7", img: "resources/customisation/character_clothes/character_clothes6.png"},
|
|
||||||
{name:"clothes_8", img: "resources/customisation/character_clothes/character_clothes7.png"},
|
|
||||||
{name:"clothes_9", img: "resources/customisation/character_clothes/character_clothes8.png"},
|
|
||||||
{name:"clothes_10",img: "resources/customisation/character_clothes/character_clothes9.png"},
|
|
||||||
{name:"clothes_11",img: "resources/customisation/character_clothes/character_clothes10.png"},
|
|
||||||
{name:"clothes_12",img: "resources/customisation/character_clothes/character_clothes11.png"},
|
|
||||||
{name:"clothes_13",img: "resources/customisation/character_clothes/character_clothes12.png"},
|
|
||||||
{name:"clothes_14",img: "resources/customisation/character_clothes/character_clothes13.png"},
|
|
||||||
{name:"clothes_15",img: "resources/customisation/character_clothes/character_clothes14.png"},
|
|
||||||
{name:"clothes_16",img: "resources/customisation/character_clothes/character_clothes15.png"},
|
|
||||||
{name:"clothes_17",img: "resources/customisation/character_clothes/character_clothes16.png"},
|
|
||||||
{name:"clothes_18",img: "resources/customisation/character_clothes/character_clothes17.png"},
|
|
||||||
{name:"clothes_19",img: "resources/customisation/character_clothes/character_clothes18.png"},
|
|
||||||
{name:"clothes_20",img: "resources/customisation/character_clothes/character_clothes19.png"},
|
|
||||||
{name:"clothes_21",img: "resources/customisation/character_clothes/character_clothes20.png"},
|
|
||||||
{name:"clothes_22",img: "resources/customisation/character_clothes/character_clothes21.png"},
|
|
||||||
{name:"clothes_23",img: "resources/customisation/character_clothes/character_clothes22.png"},
|
|
||||||
{name:"clothes_24",img: "resources/customisation/character_clothes/character_clothes23.png"},
|
|
||||||
{name:"clothes_25",img: "resources/customisation/character_clothes/character_clothes24.png"},
|
|
||||||
{name:"clothes_26",img: "resources/customisation/character_clothes/character_clothes25.png"},
|
|
||||||
{name:"clothes_27",img: "resources/customisation/character_clothes/character_clothes26.png"},
|
|
||||||
{name:"clothes_28",img: "resources/customisation/character_clothes/character_clothes27.png"},
|
|
||||||
{name:"clothes_29",img: "resources/customisation/character_clothes/character_clothes28.png"},
|
|
||||||
{name:"clothes_30",img: "resources/customisation/character_clothes/character_clothes29.png"},
|
|
||||||
{name:"clothes_31",img: "resources/customisation/character_clothes/character_clothes30.png"},
|
|
||||||
{name:"clothes_32",img: "resources/customisation/character_clothes/character_clothes31.png"},
|
|
||||||
{name:"clothes_33",img: "resources/customisation/character_clothes/character_clothes32.png"},
|
|
||||||
{name:"clothes_34",img: "resources/customisation/character_clothes/character_clothes33.png"},
|
|
||||||
{name:"clothes_35",img: "resources/customisation/character_clothes/character_clothes34.png"},
|
|
||||||
{name:"clothes_36",img: "resources/customisation/character_clothes/character_clothes35.png"},
|
|
||||||
{name:"clothes_37",img: "resources/customisation/character_clothes/character_clothes36.png"},
|
|
||||||
{name:"clothes_38",img: "resources/customisation/character_clothes/character_clothes37.png"},
|
|
||||||
{name:"clothes_39",img: "resources/customisation/character_clothes/character_clothes38.png"},
|
|
||||||
{name:"clothes_40",img: "resources/customisation/character_clothes/character_clothes39.png"},
|
|
||||||
{name:"clothes_41",img: "resources/customisation/character_clothes/character_clothes40.png"},
|
|
||||||
{name:"clothes_42",img: "resources/customisation/character_clothes/character_clothes41.png"},
|
|
||||||
{name:"clothes_43",img: "resources/customisation/character_clothes/character_clothes42.png"},
|
|
||||||
{name:"clothes_44",img: "resources/customisation/character_clothes/character_clothes43.png"},
|
|
||||||
{name:"clothes_45",img: "resources/customisation/character_clothes/character_clothes44.png"},
|
|
||||||
{name:"clothes_46",img: "resources/customisation/character_clothes/character_clothes45.png"},
|
|
||||||
{name:"clothes_47",img: "resources/customisation/character_clothes/character_clothes46.png"},
|
|
||||||
{name:"clothes_48",img: "resources/customisation/character_clothes/character_clothes47.png"},
|
|
||||||
{name:"clothes_49",img: "resources/customisation/character_clothes/character_clothes48.png"},
|
|
||||||
{name:"clothes_50",img: "resources/customisation/character_clothes/character_clothes49.png"},
|
|
||||||
{name:"clothes_51",img: "resources/customisation/character_clothes/character_clothes50.png"},
|
|
||||||
{name:"clothes_52",img: "resources/customisation/character_clothes/character_clothes51.png"},
|
|
||||||
{name:"clothes_53",img: "resources/customisation/character_clothes/character_clothes52.png"},
|
|
||||||
{name:"clothes_54",img: "resources/customisation/character_clothes/character_clothes53.png"},
|
|
||||||
{name:"clothes_55",img: "resources/customisation/character_clothes/character_clothes54.png"},
|
|
||||||
{name:"clothes_56",img: "resources/customisation/character_clothes/character_clothes55.png"},
|
|
||||||
{name:"clothes_57",img: "resources/customisation/character_clothes/character_clothes56.png"},
|
|
||||||
{name:"clothes_58",img: "resources/customisation/character_clothes/character_clothes57.png"},
|
|
||||||
{name:"clothes_59",img: "resources/customisation/character_clothes/character_clothes58.png"},
|
|
||||||
{name:"clothes_60",img: "resources/customisation/character_clothes/character_clothes59.png"},
|
|
||||||
{name:"clothes_61",img: "resources/customisation/character_clothes/character_clothes60.png"},
|
|
||||||
{name:"clothes_62",img: "resources/customisation/character_clothes/character_clothes61.png"},
|
|
||||||
{name:"clothes_63",img: "resources/customisation/character_clothes/character_clothes62.png"},
|
|
||||||
{name:"clothes_64",img: "resources/customisation/character_clothes/character_clothes63.png"},
|
|
||||||
{name:"clothes_65",img: "resources/customisation/character_clothes/character_clothes64.png"},
|
|
||||||
{name:"clothes_66",img: "resources/customisation/character_clothes/character_clothes65.png"},
|
|
||||||
{name:"clothes_67",img: "resources/customisation/character_clothes/character_clothes66.png"},
|
|
||||||
{name:"clothes_68",img: "resources/customisation/character_clothes/character_clothes67.png"},
|
|
||||||
{name:"clothes_69",img: "resources/customisation/character_clothes/character_clothes68.png"},
|
|
||||||
{name:"clothes_70",img: "resources/customisation/character_clothes/character_clothes69.png"},
|
|
||||||
{name:"clothes_pride_shirt",img: "resources/customisation/character_clothes/pride_shirt.png"},
|
|
||||||
{name:"clothes_black_hoodie",img: "resources/customisation/character_clothes/black_hoodie.png"},
|
|
||||||
{name:"clothes_white_hoodie",img: "resources/customisation/character_clothes/white_hoodie.png"},
|
|
||||||
{name:"clothes_engelbert",img: "resources/customisation/character_clothes/engelbert.png"}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const HATS_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
|
||||||
{name: "hats_1", img: "resources/customisation/character_hats/character_hats1.png"},
|
|
||||||
{name: "hats_2", img: "resources/customisation/character_hats/character_hats2.png"},
|
|
||||||
{name: "hats_3", img: "resources/customisation/character_hats/character_hats3.png"},
|
|
||||||
{name: "hats_4", img: "resources/customisation/character_hats/character_hats4.png"},
|
|
||||||
{name: "hats_5", img: "resources/customisation/character_hats/character_hats5.png"},
|
|
||||||
{name: "hats_6", img: "resources/customisation/character_hats/character_hats6.png"},
|
|
||||||
{name: "hats_7", img: "resources/customisation/character_hats/character_hats7.png"},
|
|
||||||
{name: "hats_8", img: "resources/customisation/character_hats/character_hats8.png"},
|
|
||||||
{name: "hats_9", img: "resources/customisation/character_hats/character_hats9.png"},
|
|
||||||
{name: "hats_10", img: "resources/customisation/character_hats/character_hats10.png"},
|
|
||||||
{name: "hats_11", img: "resources/customisation/character_hats/character_hats11.png"},
|
|
||||||
{name: "hats_12", img: "resources/customisation/character_hats/character_hats12.png"},
|
|
||||||
{name: "hats_13", img: "resources/customisation/character_hats/character_hats13.png"},
|
|
||||||
{name: "hats_14", img: "resources/customisation/character_hats/character_hats14.png"},
|
|
||||||
{name: "hats_15", img: "resources/customisation/character_hats/character_hats15.png"},
|
|
||||||
{name: "hats_16", img: "resources/customisation/character_hats/character_hats16.png"},
|
|
||||||
{name: "hats_17", img: "resources/customisation/character_hats/character_hats17.png"},
|
|
||||||
{name: "hats_18", img: "resources/customisation/character_hats/character_hats18.png"},
|
|
||||||
{name: "hats_19", img: "resources/customisation/character_hats/character_hats19.png"},
|
|
||||||
{name: "hats_20", img: "resources/customisation/character_hats/character_hats20.png"},
|
|
||||||
{name: "hats_21", img: "resources/customisation/character_hats/character_hats21.png"},
|
|
||||||
{name: "hats_22", img: "resources/customisation/character_hats/character_hats22.png"},
|
|
||||||
{name: "hats_23", img: "resources/customisation/character_hats/character_hats23.png"},
|
|
||||||
{name: "hats_24", img: "resources/customisation/character_hats/character_hats24.png"},
|
|
||||||
{name: "hats_25", img: "resources/customisation/character_hats/character_hats25.png"},
|
|
||||||
{name: "hats_26", img: "resources/customisation/character_hats/character_hats26.png"},
|
|
||||||
{name: "tinfoil_hat1", img: "resources/customisation/character_hats/tinfoil_hat1.png"}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ACCESSORIES_RESOURCES: Array<BodyResourceDescriptionInterface> = [
|
|
||||||
{name: "accessory_1", img: "resources/customisation/character_accessories/character_accessories1.png"},
|
|
||||||
{name: "accessory_2", img: "resources/customisation/character_accessories/character_accessories2.png"},
|
|
||||||
{name: "accessory_3", img: "resources/customisation/character_accessories/character_accessories3.png"},
|
|
||||||
{name: "accessory_4", img: "resources/customisation/character_accessories/character_accessories4.png"},
|
|
||||||
{name: "accessory_5", img: "resources/customisation/character_accessories/character_accessories5.png"},
|
|
||||||
{name: "accessory_6", img: "resources/customisation/character_accessories/character_accessories6.png"},
|
|
||||||
{name: "accessory_7", img: "resources/customisation/character_accessories/character_accessories7.png"},
|
|
||||||
{name: "accessory_8", img: "resources/customisation/character_accessories/character_accessories8.png"},
|
|
||||||
{name: "accessory_9", img: "resources/customisation/character_accessories/character_accessories9.png"},
|
|
||||||
{name: "accessory_10", img: "resources/customisation/character_accessories/character_accessories10.png"},
|
|
||||||
{name: "accessory_11", img: "resources/customisation/character_accessories/character_accessories11.png"},
|
|
||||||
{name: "accessory_12", img: "resources/customisation/character_accessories/character_accessories12.png"},
|
|
||||||
{name: "accessory_13", img: "resources/customisation/character_accessories/character_accessories13.png"},
|
|
||||||
{name: "accessory_14", img: "resources/customisation/character_accessories/character_accessories14.png"},
|
|
||||||
{name: "accessory_15", img: "resources/customisation/character_accessories/character_accessories15.png"},
|
|
||||||
{name: "accessory_16", img: "resources/customisation/character_accessories/character_accessories16.png"},
|
|
||||||
{name: "accessory_17", img: "resources/customisation/character_accessories/character_accessories17.png"},
|
|
||||||
{name: "accessory_18", img: "resources/customisation/character_accessories/character_accessories18.png"},
|
|
||||||
{name: "accessory_19", img: "resources/customisation/character_accessories/character_accessories19.png"},
|
|
||||||
{name: "accessory_20", img: "resources/customisation/character_accessories/character_accessories20.png"},
|
|
||||||
{name: "accessory_21", img: "resources/customisation/character_accessories/character_accessories21.png"},
|
|
||||||
{name: "accessory_22", img: "resources/customisation/character_accessories/character_accessories22.png"},
|
|
||||||
{name: "accessory_23", img: "resources/customisation/character_accessories/character_accessories23.png"},
|
|
||||||
{name: "accessory_24", img: "resources/customisation/character_accessories/character_accessories24.png"},
|
|
||||||
{name: "accessory_25", img: "resources/customisation/character_accessories/character_accessories25.png"},
|
|
||||||
{name: "accessory_26", img: "resources/customisation/character_accessories/character_accessories26.png"},
|
|
||||||
{name: "accessory_27", img: "resources/customisation/character_accessories/character_accessories27.png"},
|
|
||||||
{name: "accessory_28", img: "resources/customisation/character_accessories/character_accessories28.png"},
|
|
||||||
{name: "accessory_29", img: "resources/customisation/character_accessories/character_accessories29.png"},
|
|
||||||
{name: "accessory_30", img: "resources/customisation/character_accessories/character_accessories30.png"},
|
|
||||||
{name: "accessory_31", img: "resources/customisation/character_accessories/character_accessories31.png"},
|
|
||||||
{name: "accessory_32", img: "resources/customisation/character_accessories/character_accessories32.png"},
|
|
||||||
{name: "accessory_mate_bottle", img: "resources/customisation/character_accessories/mate_bottle1.png"},
|
|
||||||
{name: "accessory_mask", img: "resources/customisation/character_accessories/mask.png"}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const LAYERS: Array<Array<BodyResourceDescriptionInterface>> = [
|
|
||||||
COLOR_RESOURCES,
|
|
||||||
EYES_RESOURCES,
|
|
||||||
HAIR_RESOURCES,
|
|
||||||
CLOTHES_RESOURCES,
|
|
||||||
HATS_RESOURCES,
|
|
||||||
ACCESSORIES_RESOURCES
|
|
||||||
];
|
|
||||||
|
|
||||||
export const loadAllLayers = (load: LoaderPlugin) => {
|
|
||||||
for (let j = 0; j < LAYERS.length; j++) {
|
|
||||||
for (let i = 0; i < LAYERS[j].length; i++) {
|
|
||||||
load.spritesheet(
|
|
||||||
LAYERS[j][i].name,
|
|
||||||
LAYERS[j][i].img,
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadCustomTexture = (load: LoaderPlugin, texture: CharacterTexture) => {
|
|
||||||
const name = 'customCharacterTexture'+texture.id;
|
|
||||||
load.spritesheet(
|
|
||||||
name,
|
|
||||||
texture.url,
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const OBJECTS: Array<PlayerResourceDescriptionInterface> = [
|
|
||||||
{name:'layout_modes', img:'resources/objects/layout_modes.png'},
|
|
||||||
{name:'teleportation', img:'resources/objects/teleportation.png'},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const loadObject = (load: LoaderPlugin) => {
|
|
||||||
for (let j = 0; j < OBJECTS.length; j++) {
|
|
||||||
load.spritesheet(
|
|
||||||
OBJECTS[j].name,
|
|
||||||
OBJECTS[j].img,
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadPlayerCharacters = (load: LoaderPlugin) => {
|
|
||||||
PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => {
|
|
||||||
load.spritesheet(
|
|
||||||
playerResource.name,
|
|
||||||
playerResource.img,
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
import {PointInterface} from "../../Connexion/ConnexionModels";
|
import {PointInterface} from "../../Connexion/ConnexionModels";
|
||||||
import {BodyResourceDescriptionInterface} from "../Entity/body_character";
|
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
|
|
||||||
export interface AddPlayerInterface {
|
export interface AddPlayerInterface {
|
||||||
userId: number;
|
userId: number;
|
||||||
|
@ -55,7 +55,10 @@ export class GameManager {
|
|||||||
return this.playerName;
|
return this.playerName;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCharacterLayers(): string[]|null {
|
getCharacterLayers(): string[] {
|
||||||
|
if (!this.characterLayers) {
|
||||||
|
throw 'characterLayers are not set';
|
||||||
|
}
|
||||||
return this.characterLayers;
|
return this.characterLayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import {RemotePlayer} from "../Entity/RemotePlayer";
|
|||||||
import {Queue} from 'queue-typescript';
|
import {Queue} from 'queue-typescript';
|
||||||
import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer";
|
import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer";
|
||||||
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene";
|
||||||
import {loadAllLayers, loadObject, loadPlayerCharacters} from "../Entity/body_character";
|
import {lazyLoadPlayerCharacterTextures, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||||
import {
|
import {
|
||||||
CenterListener,
|
CenterListener,
|
||||||
layoutManager,
|
layoutManager,
|
||||||
@ -45,7 +45,6 @@ import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
|
|||||||
import {GameMap} from "./GameMap";
|
import {GameMap} from "./GameMap";
|
||||||
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
|
||||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||||
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
|
|
||||||
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface";
|
||||||
import {ActionableItem} from "../Items/ActionableItem";
|
import {ActionableItem} from "../Items/ActionableItem";
|
||||||
import {UserInputManager} from "../UserInput/UserInputManager";
|
import {UserInputManager} from "../UserInput/UserInputManager";
|
||||||
@ -66,7 +65,10 @@ import {ChatModeIcon} from "../Components/ChatModeIcon";
|
|||||||
import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon";
|
import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon";
|
||||||
import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene";
|
import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene";
|
||||||
import {TextureError} from "../../Exception/TextureError";
|
import {TextureError} from "../../Exception/TextureError";
|
||||||
import {TextField} from "../Components/TextField";
|
import {addLoader} from "../Components/Loader";
|
||||||
|
import {ErrorSceneName} from "../Reconnecting/ErrorScene";
|
||||||
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
|
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
|
|
||||||
export interface GameSceneInitInterface {
|
export interface GameSceneInitInterface {
|
||||||
initPosition: PointInterface|null,
|
initPosition: PointInterface|null,
|
||||||
@ -111,7 +113,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
MapPlayers!: Phaser.Physics.Arcade.Group;
|
MapPlayers!: Phaser.Physics.Arcade.Group;
|
||||||
MapPlayersByKey : Map<number, RemotePlayer> = new Map<number, RemotePlayer>();
|
MapPlayersByKey : Map<number, RemotePlayer> = new Map<number, RemotePlayer>();
|
||||||
Map!: Phaser.Tilemaps.Tilemap;
|
Map!: Phaser.Tilemaps.Tilemap;
|
||||||
Layers!: Array<Phaser.Tilemaps.TilemapLayer>;
|
Layers!: Array<Phaser.Tilemaps.StaticTilemapLayer>;
|
||||||
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
Objects!: Array<Phaser.Physics.Arcade.Sprite>;
|
||||||
mapFile!: ITiledMap;
|
mapFile!: ITiledMap;
|
||||||
groups: Map<number, Sprite>;
|
groups: Map<number, Sprite>;
|
||||||
@ -181,12 +183,22 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
|
|
||||||
//hook preload scene
|
//hook preload scene
|
||||||
preload(): void {
|
preload(): void {
|
||||||
this.initProgressBar();
|
addLoader(this);
|
||||||
|
|
||||||
|
const localUser = localUserStore.getLocalUser();
|
||||||
|
const textures = localUser?.textures;
|
||||||
|
if (textures) {
|
||||||
|
for (const texture of textures) {
|
||||||
|
loadCustomTexture(this.load, texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.load.image(openChatIconName, 'resources/objects/talk.png');
|
this.load.image(openChatIconName, 'resources/objects/talk.png');
|
||||||
this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => {
|
this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => {
|
||||||
this.scene.start(FourOFourSceneName, {
|
this.scene.start(ErrorSceneName, {
|
||||||
file: file.src
|
title: 'Network error',
|
||||||
|
subTitle: 'An error occurred while loading resource:',
|
||||||
|
message: file.src
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => {
|
this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => {
|
||||||
@ -201,28 +213,10 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.onMapLoad(data);
|
this.onMapLoad(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
//add player png
|
this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32});
|
||||||
loadPlayerCharacters(this.load);
|
|
||||||
loadAllLayers(this.load);
|
|
||||||
loadObject(this.load);
|
|
||||||
|
|
||||||
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
private initProgressBar(): void {
|
|
||||||
const loadingText = this.add.text(this.game.renderer.width / 2, 200, 'Loading');
|
|
||||||
const progress = this.add.graphics();
|
|
||||||
this.load.on('progress', (value: number) => {
|
|
||||||
progress.clear();
|
|
||||||
progress.fillStyle(0xffffff, 1);
|
|
||||||
progress.fillRect(0, 270, 800 * value, 60);
|
|
||||||
});
|
|
||||||
this.load.on('complete', () => {
|
|
||||||
loadingText.destroy();
|
|
||||||
progress.destroy();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
|
// FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
private async onMapLoad(data: any): Promise<void> {
|
private async onMapLoad(data: any): Promise<void> {
|
||||||
@ -340,11 +334,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
throw 'playerName is not set';
|
throw 'playerName is not set';
|
||||||
}
|
}
|
||||||
this.playerName = playerName;
|
this.playerName = playerName;
|
||||||
const characterLayers = gameManager.getCharacterLayers();
|
this.characterLayers = gameManager.getCharacterLayers();
|
||||||
if (!characterLayers) {
|
|
||||||
throw 'characterLayers are not set';
|
|
||||||
}
|
|
||||||
this.characterLayers = characterLayers;
|
|
||||||
|
|
||||||
|
|
||||||
//initalise map
|
//initalise map
|
||||||
@ -359,11 +349,11 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.physics.world.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
|
this.physics.world.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels);
|
||||||
|
|
||||||
//add layer on map
|
//add layer on map
|
||||||
this.Layers = new Array<Phaser.Tilemaps.TilemapLayer>();
|
this.Layers = new Array<Phaser.Tilemaps.StaticTilemapLayer>();
|
||||||
let depth = -2;
|
let depth = -2;
|
||||||
for (const layer of this.mapFile.layers) {
|
for (const layer of this.mapFile.layers) {
|
||||||
if (layer.type === 'tilelayer') {
|
if (layer.type === 'tilelayer') {
|
||||||
this.addLayer(this.Map.createLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
this.addLayer(this.Map.createStaticLayer(layer.name, this.Terrains, 0, 0).setDepth(depth));
|
||||||
|
|
||||||
const exitSceneUrl = this.getExitSceneUrl(layer);
|
const exitSceneUrl = this.getExitSceneUrl(layer);
|
||||||
if (exitSceneUrl !== undefined) {
|
if (exitSceneUrl !== undefined) {
|
||||||
@ -544,6 +534,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName);
|
this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName);
|
||||||
this.GlobalMessageManager = new GlobalMessageManager(this.connection);
|
this.GlobalMessageManager = new GlobalMessageManager(this.connection);
|
||||||
this.UserMessageManager = new UserMessageManager(this.connection);
|
this.UserMessageManager = new UserMessageManager(this.connection);
|
||||||
|
this.UserMessageManager.setReceiveBanListener(this.bannedUser.bind(this));
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
this.simplePeer.registerPeerConnectionListener({
|
this.simplePeer.registerPeerConnectionListener({
|
||||||
@ -620,8 +611,18 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
if (url === undefined) {
|
if (url === undefined) {
|
||||||
audioManager.unloadAudio();
|
audioManager.unloadAudio();
|
||||||
} else {
|
} else {
|
||||||
const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/'));
|
const audioPath = url as string;
|
||||||
const realAudioPath = mapDirUrl + '/' + url;
|
let realAudioPath = '';
|
||||||
|
|
||||||
|
if (audioPath.indexOf('://') > 0) {
|
||||||
|
// remote file or stream
|
||||||
|
realAudioPath = audioPath;
|
||||||
|
} else {
|
||||||
|
// local file, include it relative to map directory
|
||||||
|
const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/'));
|
||||||
|
realAudioPath = mapDirUrl + '/' + url;
|
||||||
|
}
|
||||||
|
|
||||||
audioManager.loadAudio(realAudioPath);
|
audioManager.loadAudio(realAudioPath);
|
||||||
|
|
||||||
if (loop) {
|
if (loop) {
|
||||||
@ -718,6 +719,10 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public cleanupClosingScene(): void {
|
public cleanupClosingScene(): void {
|
||||||
|
// stop playing audio, close any open website, stop any open Jitsi
|
||||||
|
coWebsiteManager.closeCoWebsite();
|
||||||
|
this.stopJitsi();
|
||||||
|
this.playAudio(undefined);
|
||||||
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
// We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map.
|
||||||
if(this.connection) {
|
if(this.connection) {
|
||||||
this.connection.closeConnection();
|
this.connection.closeConnection();
|
||||||
@ -854,13 +859,13 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.cameras.main.setZoom(ZOOM_LEVEL);
|
this.cameras.main.setZoom(ZOOM_LEVEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
addLayer(Layer : Phaser.Tilemaps.TilemapLayer){
|
addLayer(Layer : Phaser.Tilemaps.StaticTilemapLayer){
|
||||||
this.Layers.push(Layer);
|
this.Layers.push(Layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
createCollisionWithPlayer() {
|
createCollisionWithPlayer() {
|
||||||
//add collision layer
|
//add collision layer
|
||||||
this.Layers.forEach((Layer: Phaser.Tilemaps.TilemapLayer) => {
|
this.Layers.forEach((Layer: Phaser.Tilemaps.StaticTilemapLayer) => {
|
||||||
this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => {
|
this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => {
|
||||||
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
//this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name)
|
||||||
});
|
});
|
||||||
@ -877,15 +882,15 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createCurrentPlayer(){
|
createCurrentPlayer(){
|
||||||
//initialise player
|
|
||||||
//TODO create animation moving between exit and start
|
//TODO create animation moving between exit and start
|
||||||
|
const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, this.characterLayers);
|
||||||
try {
|
try {
|
||||||
this.CurrentPlayer = new Player(
|
this.CurrentPlayer = new Player(
|
||||||
this,
|
this,
|
||||||
this.startX,
|
this.startX,
|
||||||
this.startY,
|
this.startY,
|
||||||
this.playerName,
|
this.playerName,
|
||||||
this.characterLayers,
|
texturesPromise,
|
||||||
PlayerAnimationNames.WalkDown,
|
PlayerAnimationNames.WalkDown,
|
||||||
false,
|
false,
|
||||||
this.userInputManager
|
this.userInputManager
|
||||||
@ -1062,10 +1067,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private doAddPlayer(addPlayerData : AddPlayerInterface): void {
|
||||||
* Create new player
|
|
||||||
*/
|
|
||||||
private async doAddPlayer(addPlayerData : AddPlayerInterface) : Promise<void> {
|
|
||||||
//check if exist player, if exist, move position
|
//check if exist player, if exist, move position
|
||||||
if(this.MapPlayersByKey.has(addPlayerData.userId)){
|
if(this.MapPlayersByKey.has(addPlayerData.userId)){
|
||||||
this.updatePlayerPosition({
|
this.updatePlayerPosition({
|
||||||
@ -1074,39 +1076,21 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Load textures (in case it is a custom texture)
|
|
||||||
const characterLayerList: string[] = [];
|
|
||||||
const loadPromises: Promise<void>[] = [];
|
|
||||||
for (const characterLayer of addPlayerData.characterLayers) {
|
|
||||||
characterLayerList.push(characterLayer.name);
|
|
||||||
if (characterLayer.img) {
|
|
||||||
console.log('LOADING ', characterLayer.name, characterLayer.img)
|
|
||||||
loadPromises.push(this.loadSpritesheet(characterLayer.name, characterLayer.img));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (loadPromises.length > 0) {
|
|
||||||
this.load.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
//initialise player
|
const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, addPlayerData.characterLayers);
|
||||||
const player = new RemotePlayer(
|
const player = new RemotePlayer(
|
||||||
addPlayerData.userId,
|
addPlayerData.userId,
|
||||||
this,
|
this,
|
||||||
addPlayerData.position.x,
|
addPlayerData.position.x,
|
||||||
addPlayerData.position.y,
|
addPlayerData.position.y,
|
||||||
addPlayerData.name,
|
addPlayerData.name,
|
||||||
[], // Let's go with no textures and let's load textures when promises have returned.
|
texturesPromise,
|
||||||
addPlayerData.position.direction,
|
addPlayerData.position.direction,
|
||||||
addPlayerData.position.moving
|
addPlayerData.position.moving
|
||||||
);
|
);
|
||||||
this.MapPlayers.add(player);
|
this.MapPlayers.add(player);
|
||||||
this.MapPlayersByKey.set(player.userId, player);
|
this.MapPlayersByKey.set(player.userId, player);
|
||||||
player.updatePosition(addPlayerData.position);
|
player.updatePosition(addPlayerData.position);
|
||||||
|
|
||||||
|
|
||||||
await Promise.all(loadPromises);
|
|
||||||
|
|
||||||
player.addTextures(characterLayerList, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1262,23 +1246,14 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
|
mediaManager.removeTriggerCloseJitsiFrameButton('close-jisi');
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadSpritesheet(name: string, url: string): Promise<void> {
|
private bannedUser(){
|
||||||
return new Promise<void>(((resolve, reject) => {
|
this.cleanupClosingScene();
|
||||||
if (this.textures.exists(name)) {
|
this.userInputManager.clearAllInputKeyboard();
|
||||||
resolve();
|
this.scene.start(ErrorSceneName, {
|
||||||
return;
|
title: 'Banned',
|
||||||
}
|
subTitle: 'You was banned of WorkAdventure',
|
||||||
this.load.spritesheet(
|
message: 'If you want more information, you can contact us: workadventure@thecodingmachine.com'
|
||||||
name,
|
});
|
||||||
url,
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
);
|
|
||||||
this.load.on('filecomplete-spritesheet-'+name, () => {
|
|
||||||
console.log('RESOURCE LOADED!');
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,11 @@ export class ActionableItem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isSelectable = true;
|
this.isSelectable = true;
|
||||||
this.sprite.setPipeline(OutlinePipeline.KEY);
|
if (this.sprite.pipeline) {
|
||||||
this.sprite.pipeline.set2f('uTextureSize', this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);
|
// Commented out to try to fix MacOS issue
|
||||||
|
/*this.sprite.setPipeline(OutlinePipeline.KEY);
|
||||||
|
this.sprite.pipeline.set2f('uTextureSize', this.sprite.texture.getSourceImage().width, this.sprite.texture.getSourceImage().height);*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +57,8 @@ export class ActionableItem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isSelectable = false;
|
this.isSelectable = false;
|
||||||
this.sprite.resetPipeline();
|
// Commented out to try to fix MacOS issue
|
||||||
|
//this.sprite.resetPipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
41
front/src/Phaser/Login/AbstractCharacterScene.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import {ResizableScene} from "./ResizableScene";
|
||||||
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
|
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
|
import {loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||||
|
import {CharacterTexture} from "../../Connexion/LocalUser";
|
||||||
|
|
||||||
|
export abstract class AbstractCharacterScene extends ResizableScene {
|
||||||
|
|
||||||
|
loadCustomSceneSelectCharacters() : Promise<BodyResourceDescriptionInterface[]> {
|
||||||
|
const textures = this.getTextures();
|
||||||
|
const promises : Promise<BodyResourceDescriptionInterface>[] = [];
|
||||||
|
if (textures) {
|
||||||
|
for (const texture of textures) {
|
||||||
|
if (texture.level === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
promises.push(loadCustomTexture(this.load, texture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.all(promises)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSelectSceneCharacters() : Promise<BodyResourceDescriptionInterface[]> {
|
||||||
|
const textures = this.getTextures();
|
||||||
|
const promises: Promise<BodyResourceDescriptionInterface>[] = [];
|
||||||
|
if (textures) {
|
||||||
|
for (const texture of textures) {
|
||||||
|
if (texture.level !== -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
promises.push(loadCustomTexture(this.load, texture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.all(promises)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTextures() : CharacterTexture[]|undefined{
|
||||||
|
const localUser = localUserStore.getLocalUser();
|
||||||
|
return localUser?.textures;
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,15 @@ import {EnableCameraSceneName} from "./EnableCameraScene";
|
|||||||
import {TextField} from "../Components/TextField";
|
import {TextField} from "../Components/TextField";
|
||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||||
import {BodyResourceDescriptionInterface, LAYERS, loadAllLayers, loadCustomTexture} from "../Entity/body_character";
|
import {loadAllLayers, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import Container = Phaser.GameObjects.Container;
|
import Container = Phaser.GameObjects.Container;
|
||||||
import {gameManager} from "../Game/GameManager";
|
import {gameManager} from "../Game/GameManager";
|
||||||
import {ResizableScene} from "./ResizableScene";
|
import {ResizableScene} from "./ResizableScene";
|
||||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
import {PlayerResourceDescriptionInterface} from "../Entity/Character";
|
import {addLoader} from "../Components/Loader";
|
||||||
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
import {LoginSceneName} from "./LoginScene";
|
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||||
|
|
||||||
export const CustomizeSceneName = "CustomizeScene";
|
export const CustomizeSceneName = "CustomizeScene";
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ enum CustomizeTextures{
|
|||||||
arrowUp = "arrow_up",
|
arrowUp = "arrow_up",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CustomizeScene extends ResizableScene {
|
export class CustomizeScene extends AbstractCharacterScene {
|
||||||
|
|
||||||
private textField!: TextField;
|
private textField!: TextField;
|
||||||
private enterField!: TextField;
|
private enterField!: TextField;
|
||||||
@ -48,30 +48,22 @@ export class CustomizeScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
|
addLoader(this);
|
||||||
|
|
||||||
|
this.layers = loadAllLayers(this.load);
|
||||||
|
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
|
||||||
|
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
||||||
|
if(!bodyResourceDescription.level){
|
||||||
|
throw 'Texture level is null';
|
||||||
|
}
|
||||||
|
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this.load.image(CustomizeTextures.arrowRight, "resources/objects/arrow_right.png");
|
this.load.image(CustomizeTextures.arrowRight, "resources/objects/arrow_right.png");
|
||||||
this.load.image(CustomizeTextures.icon, "resources/logos/tcm_full.png");
|
this.load.image(CustomizeTextures.icon, "resources/logos/tcm_full.png");
|
||||||
this.load.image(CustomizeTextures.arrowUp, "resources/objects/arrow_up.png");
|
this.load.image(CustomizeTextures.arrowUp, "resources/objects/arrow_up.png");
|
||||||
this.load.bitmapFont(CustomizeTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
this.load.bitmapFont(CustomizeTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||||
|
|
||||||
//load all the png files
|
|
||||||
loadAllLayers(this.load);
|
|
||||||
|
|
||||||
// load custom layers
|
|
||||||
this.layers = LAYERS;
|
|
||||||
|
|
||||||
const localUser = localUserStore.getLocalUser();
|
|
||||||
|
|
||||||
const textures = localUser?.textures;
|
|
||||||
if (textures) {
|
|
||||||
for (const texture of textures) {
|
|
||||||
loadCustomTexture(this.load, texture);
|
|
||||||
const name = 'customCharacterTexture'+texture.id;
|
|
||||||
this.layers[texture.level].unshift({
|
|
||||||
name,
|
|
||||||
img: texture.url
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
@ -253,6 +253,8 @@ export class EnableCameraScene extends Phaser.Scene {
|
|||||||
this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2));
|
this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2));
|
||||||
|
|
||||||
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
||||||
|
|
||||||
|
mediaManager.setLastUpdateScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private login(): void {
|
private login(): void {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {gameManager} from "../Game/GameManager";
|
import {gameManager} from "../Game/GameManager";
|
||||||
import {Scene} from "phaser";
|
import {Scene} from "phaser";
|
||||||
import {LoginSceneName} from "./LoginScene";
|
import {ErrorScene} from "../Reconnecting/ErrorScene";
|
||||||
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
|
import {WAError} from "../Reconnecting/WAError";
|
||||||
|
|
||||||
export const EntrySceneName = "EntryScene";
|
export const EntrySceneName = "EntryScene";
|
||||||
|
|
||||||
@ -20,10 +20,11 @@ export class EntryScene extends Scene {
|
|||||||
gameManager.init(this.scene).then((nextSceneName) => {
|
gameManager.init(this.scene).then((nextSceneName) => {
|
||||||
this.scene.start(nextSceneName);
|
this.scene.start(nextSceneName);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err)
|
if (err.response && err.response.status == 404) {
|
||||||
this.scene.start(FourOFourSceneName, {
|
ErrorScene.showError(new WAError('Page Not Found', 'Could not find map', window.location.pathname), this.scene);
|
||||||
url: window.location.pathname.toString()
|
} else {
|
||||||
});
|
ErrorScene.showError(err, this.scene);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,8 @@ import {gameManager} from "../Game/GameManager";
|
|||||||
import {TextField} from "../Components/TextField";
|
import {TextField} from "../Components/TextField";
|
||||||
import {TextInput} from "../Components/TextInput";
|
import {TextInput} from "../Components/TextInput";
|
||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character";
|
|
||||||
import {cypressAsserter} from "../../Cypress/CypressAsserter";
|
|
||||||
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
import {SelectCharacterSceneName} from "./SelectCharacterScene";
|
||||||
import {ResizableScene} from "./ResizableScene";
|
import {ResizableScene} from "./ResizableScene";
|
||||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
|
||||||
|
|
||||||
//todo: put this constants in a dedicated file
|
//todo: put this constants in a dedicated file
|
||||||
export const LoginSceneName = "LoginScene";
|
export const LoginSceneName = "LoginScene";
|
||||||
@ -31,24 +28,13 @@ export class LoginScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
cypressAsserter.preloadStarted();
|
|
||||||
//this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
//this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||||
cypressAsserter.preloadFinished();
|
|
||||||
//add player png
|
|
||||||
PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => {
|
|
||||||
this.load.spritesheet(
|
|
||||||
playerResource.name,
|
|
||||||
playerResource.img,
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
cypressAsserter.initStarted();
|
|
||||||
|
|
||||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:');
|
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:');
|
||||||
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => {
|
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => {
|
||||||
@ -69,8 +55,6 @@ export class LoginScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
this.login(this.name);
|
this.login(this.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
cypressAsserter.initFinished();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
|
@ -2,11 +2,14 @@ import {gameManager} from "../Game/GameManager";
|
|||||||
import {TextField} from "../Components/TextField";
|
import {TextField} from "../Components/TextField";
|
||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
import Rectangle = Phaser.GameObjects.Rectangle;
|
||||||
import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character";
|
|
||||||
import {EnableCameraSceneName} from "./EnableCameraScene";
|
import {EnableCameraSceneName} from "./EnableCameraScene";
|
||||||
import {CustomizeSceneName} from "./CustomizeScene";
|
import {CustomizeSceneName} from "./CustomizeScene";
|
||||||
import {ResizableScene} from "./ResizableScene";
|
import {ResizableScene} from "./ResizableScene";
|
||||||
import {localUserStore} from "../../Connexion/LocalUserStore";
|
import {localUserStore} from "../../Connexion/LocalUserStore";
|
||||||
|
import {loadAllDefaultModels, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager";
|
||||||
|
import {addLoader} from "../Components/Loader";
|
||||||
|
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
|
||||||
|
import {AbstractCharacterScene} from "./AbstractCharacterScene";
|
||||||
|
|
||||||
|
|
||||||
//todo: put this constants in a dedicated file
|
//todo: put this constants in a dedicated file
|
||||||
@ -19,8 +22,8 @@ enum LoginTextures {
|
|||||||
customizeButtonSelected = "customize_button_selected"
|
customizeButtonSelected = "customize_button_selected"
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SelectCharacterScene extends ResizableScene {
|
export class SelectCharacterScene extends AbstractCharacterScene {
|
||||||
private readonly nbCharactersPerRow = 4;
|
private readonly nbCharactersPerRow = 6;
|
||||||
private textField!: TextField;
|
private textField!: TextField;
|
||||||
private pressReturnField!: TextField;
|
private pressReturnField!: TextField;
|
||||||
private logo!: Image;
|
private logo!: Image;
|
||||||
@ -32,6 +35,7 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
private selectedRectangleYPos = 0; // Number of the character selected in the columns
|
private selectedRectangleYPos = 0; // Number of the character selected in the columns
|
||||||
private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option
|
||||||
private players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
private players: Array<Phaser.Physics.Arcade.Sprite> = new Array<Phaser.Physics.Arcade.Sprite>();
|
||||||
|
private playerModels!: BodyResourceDescriptionInterface[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
@ -40,26 +44,30 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
|
addLoader(this);
|
||||||
|
|
||||||
|
this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => {
|
||||||
|
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
|
||||||
|
this.playerModels.push(bodyResourceDescription);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
||||||
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png");
|
||||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||||
//add player png
|
this.playerModels = loadAllDefaultModels(this.load);
|
||||||
PLAYER_RESOURCES.forEach((playerResource: PlayerResourceDescriptionInterface) => {
|
|
||||||
this.load.spritesheet(
|
|
||||||
playerResource.name,
|
|
||||||
playerResource.img,
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
this.load.image(LoginTextures.customizeButton, 'resources/objects/customize.png');
|
this.load.image(LoginTextures.customizeButton, 'resources/objects/customize.png');
|
||||||
this.load.image(LoginTextures.customizeButtonSelected, 'resources/objects/customize_selected.png');
|
this.load.image(LoginTextures.customizeButtonSelected, 'resources/objects/customize_selected.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character');
|
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character');
|
||||||
|
this.pressReturnField = new TextField(
|
||||||
this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 256, 'Press enter to start');
|
this,
|
||||||
|
this.game.renderer.width / 2,
|
||||||
|
90 + 32 * Math.ceil( this.playerModels.length / this.nbCharactersPerRow) + 40,
|
||||||
|
'Press enter to start');
|
||||||
|
|
||||||
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
|
||||||
|
|
||||||
@ -73,25 +81,41 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.input.keyboard.on('keydown-RIGHT', () => {
|
this.input.keyboard.on('keydown-RIGHT', () => {
|
||||||
if (this.selectedRectangleXPos < this.nbCharactersPerRow - 1) {
|
if(this.selectedRectangleYPos * this.nbCharactersPerRow + (this.selectedRectangleXPos + 2))
|
||||||
|
if (
|
||||||
|
this.selectedRectangleXPos < this.nbCharactersPerRow - 1
|
||||||
|
&& ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos + 1) + 1) <= this.playerModels.length
|
||||||
|
) {
|
||||||
this.selectedRectangleXPos++;
|
this.selectedRectangleXPos++;
|
||||||
}
|
}
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
});
|
});
|
||||||
this.input.keyboard.on('keydown-LEFT', () => {
|
this.input.keyboard.on('keydown-LEFT', () => {
|
||||||
if (this.selectedRectangleXPos > 0) {
|
if (
|
||||||
|
this.selectedRectangleXPos > 0
|
||||||
|
&& ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos - 1) + 1) <= this.playerModels.length
|
||||||
|
) {
|
||||||
this.selectedRectangleXPos--;
|
this.selectedRectangleXPos--;
|
||||||
}
|
}
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
});
|
});
|
||||||
this.input.keyboard.on('keydown-DOWN', () => {
|
this.input.keyboard.on('keydown-DOWN', () => {
|
||||||
if (this.selectedRectangleYPos < Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow)) {
|
if (
|
||||||
|
this.selectedRectangleYPos < Math.ceil(this.playerModels.length / this.nbCharactersPerRow)
|
||||||
|
&& (
|
||||||
|
(((this.selectedRectangleYPos + 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.playerModels.length // check if player isn't empty
|
||||||
|
|| (this.selectedRectangleYPos + 1) === Math.ceil(this.playerModels.length / this.nbCharactersPerRow) // check if is custom rectangle
|
||||||
|
)
|
||||||
|
) {
|
||||||
this.selectedRectangleYPos++;
|
this.selectedRectangleYPos++;
|
||||||
}
|
}
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
});
|
});
|
||||||
this.input.keyboard.on('keydown-UP', () => {
|
this.input.keyboard.on('keydown-UP', () => {
|
||||||
if (this.selectedRectangleYPos > 0) {
|
if (
|
||||||
|
this.selectedRectangleYPos > 0
|
||||||
|
&& (((this.selectedRectangleYPos - 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.playerModels.length
|
||||||
|
) {
|
||||||
this.selectedRectangleYPos--;
|
this.selectedRectangleYPos--;
|
||||||
}
|
}
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
@ -106,7 +130,7 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
this.selectedRectangleYPos = Math.floor(playerNumber / this.nbCharactersPerRow);
|
this.selectedRectangleYPos = Math.floor(playerNumber / this.nbCharactersPerRow);
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
} else if (playerNumber === -1) {
|
} else if (playerNumber === -1) {
|
||||||
this.selectedRectangleYPos = Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow);
|
this.selectedRectangleYPos = Math.ceil(this.playerModels.length / this.nbCharactersPerRow);
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,8 +151,8 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createCurrentPlayer(): void {
|
createCurrentPlayer(): void {
|
||||||
for (let i = 0; i <PLAYER_RESOURCES.length; i++) {
|
for (let i = 0; i <this.playerModels.length; i++) {
|
||||||
const playerResource = PLAYER_RESOURCES[i];
|
const playerResource = this.playerModels[i];
|
||||||
|
|
||||||
const col = i % this.nbCharactersPerRow;
|
const col = i % this.nbCharactersPerRow;
|
||||||
const row = Math.floor(i / this.nbCharactersPerRow);
|
const row = Math.floor(i / this.nbCharactersPerRow);
|
||||||
@ -151,21 +175,22 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
this.players.push(player);
|
this.players.push(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.customizeButton = new Image(this, this.game.renderer.width / 2, 90 + 32 * 4 + 6, LoginTextures.customizeButton);
|
const maxRow = Math.ceil( this.playerModels.length / this.nbCharactersPerRow);
|
||||||
|
this.customizeButton = new Image(this, this.game.renderer.width / 2, 90 + 32 * maxRow + 6, LoginTextures.customizeButton);
|
||||||
this.customizeButton.setOrigin(0.5, 0.5);
|
this.customizeButton.setOrigin(0.5, 0.5);
|
||||||
this.add.existing(this.customizeButton);
|
this.add.existing(this.customizeButton);
|
||||||
this.customizeButtonSelected = new Image(this, this.game.renderer.width / 2, 90 + 32 * 4 + 6, LoginTextures.customizeButtonSelected);
|
this.customizeButtonSelected = new Image(this, this.game.renderer.width / 2, 90 + 32 * maxRow + 6, LoginTextures.customizeButtonSelected);
|
||||||
this.customizeButtonSelected.setOrigin(0.5, 0.5);
|
this.customizeButtonSelected.setOrigin(0.5, 0.5);
|
||||||
this.customizeButtonSelected.setVisible(false);
|
this.customizeButtonSelected.setVisible(false);
|
||||||
this.add.existing(this.customizeButtonSelected);
|
this.add.existing(this.customizeButtonSelected);
|
||||||
|
|
||||||
this.customizeButton.setInteractive().on("pointerdown", () => {
|
this.customizeButton.setInteractive().on("pointerdown", () => {
|
||||||
this.selectedRectangleYPos = Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow);
|
this.selectedRectangleYPos = Math.ceil(this.playerModels.length / this.nbCharactersPerRow);
|
||||||
this.updateSelectedPlayer();
|
this.updateSelectedPlayer();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.selectedPlayer = this.players[0];
|
this.selectedPlayer = this.players[0];
|
||||||
this.selectedPlayer.play(PLAYER_RESOURCES[0].name);
|
this.selectedPlayer.play(this.playerModels[0].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,7 +206,7 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
private updateSelectedPlayer(): void {
|
private updateSelectedPlayer(): void {
|
||||||
this.selectedPlayer?.anims.pause();
|
this.selectedPlayer?.anims.pause();
|
||||||
// If we selected the customize button
|
// If we selected the customize button
|
||||||
if (this.selectedRectangleYPos === Math.ceil(PLAYER_RESOURCES.length / this.nbCharactersPerRow)) {
|
if (this.selectedRectangleYPos === Math.ceil(this.playerModels.length / this.nbCharactersPerRow)) {
|
||||||
this.selectedPlayer = null;
|
this.selectedPlayer = null;
|
||||||
this.selectedRectangle.setVisible(false);
|
this.selectedRectangle.setVisible(false);
|
||||||
this.customizeButtonSelected.setVisible(true);
|
this.customizeButtonSelected.setVisible(true);
|
||||||
@ -198,7 +223,7 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
this.selectedRectangle.setSize(32, 32);
|
this.selectedRectangle.setSize(32, 32);
|
||||||
const playerNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow;
|
const playerNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow;
|
||||||
const player = this.players[playerNumber];
|
const player = this.players[playerNumber];
|
||||||
player.play(PLAYER_RESOURCES[playerNumber].name);
|
player.play(this.playerModels[playerNumber].name);
|
||||||
this.selectedPlayer = player;
|
this.selectedPlayer = player;
|
||||||
localUserStore.setPlayerCharacterIndex(playerNumber);
|
localUserStore.setPlayerCharacterIndex(playerNumber);
|
||||||
}
|
}
|
||||||
@ -211,7 +236,7 @@ export class SelectCharacterScene extends ResizableScene {
|
|||||||
this.customizeButton.x = this.game.renderer.width / 2;
|
this.customizeButton.x = this.game.renderer.width / 2;
|
||||||
this.customizeButtonSelected.x = this.game.renderer.width / 2;
|
this.customizeButtonSelected.x = this.game.renderer.width / 2;
|
||||||
|
|
||||||
for (let i = 0; i <PLAYER_RESOURCES.length; i++) {
|
for (let i = 0; i <this.playerModels.length; i++) {
|
||||||
const player = this.players[i];
|
const player = this.players[i];
|
||||||
|
|
||||||
const col = i % this.nbCharactersPerRow;
|
const col = i % this.nbCharactersPerRow;
|
||||||
|
@ -110,6 +110,7 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
if (!this.sideMenuOpened) return;
|
if (!this.sideMenuOpened) return;
|
||||||
this.sideMenuOpened = false;
|
this.sideMenuOpened = false;
|
||||||
this.closeAll();
|
this.closeAll();
|
||||||
|
this.menuButton.getChildByID('openMenuButton').innerHTML = `<img src="/static/images/menu.svg">`;
|
||||||
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.disabledMessageConsole();
|
gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.disabledMessageConsole();
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
targets: this.menuElement,
|
targets: this.menuElement,
|
||||||
@ -121,6 +122,7 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
|
|
||||||
private openGameSettingsMenu(): void {
|
private openGameSettingsMenu(): void {
|
||||||
if (this.settingsMenuOpened) {
|
if (this.settingsMenuOpened) {
|
||||||
|
this.closeGameQualityMenu();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//close all
|
//close all
|
||||||
@ -278,6 +280,5 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
private closeAll(){
|
private closeAll(){
|
||||||
this.closeGameQualityMenu();
|
this.closeGameQualityMenu();
|
||||||
this.closeGameShare();
|
this.closeGameShare();
|
||||||
this.menuButton.getChildByID('openMenuButton').innerHTML = `<img src="/static/images/menu.svg">`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,12 @@ export class Player extends Character implements CurrentGamerInterface {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
name: string,
|
name: string,
|
||||||
PlayerTextures: string[],
|
texturesPromise: Promise<string[]>,
|
||||||
direction: string,
|
direction: string,
|
||||||
moving: boolean,
|
moving: boolean,
|
||||||
private userInputManager: UserInputManager
|
private userInputManager: UserInputManager
|
||||||
) {
|
) {
|
||||||
super(Scene, x, y, PlayerTextures, name, direction, moving, 1);
|
super(Scene, x, y, texturesPromise, name, direction, moving, 1);
|
||||||
|
|
||||||
//the current player model should be push away by other players to prevent conflict
|
//the current player model should be push away by other players to prevent conflict
|
||||||
this.getBody().setImmovable(false);
|
this.getBody().setImmovable(false);
|
||||||
|
120
front/src/Phaser/Reconnecting/ErrorScene.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import {TextField} from "../Components/TextField";
|
||||||
|
import Image = Phaser.GameObjects.Image;
|
||||||
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
|
import Text = Phaser.GameObjects.Text;
|
||||||
|
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
||||||
|
import {WAError} from "./WAError";
|
||||||
|
|
||||||
|
export const ErrorSceneName = "ErrorScene";
|
||||||
|
enum Textures {
|
||||||
|
icon = "icon",
|
||||||
|
mainFont = "main_font"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ErrorScene extends Phaser.Scene {
|
||||||
|
private titleField!: TextField;
|
||||||
|
private subTitleField!: TextField;
|
||||||
|
private messageField!: Text;
|
||||||
|
private logo!: Image;
|
||||||
|
private cat!: Sprite;
|
||||||
|
private title!: string;
|
||||||
|
private subTitle!: string;
|
||||||
|
private message!: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
key: ErrorSceneName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init({title, subTitle, message}: { title?: string, subTitle?: string, message?: string }) {
|
||||||
|
this.title = title ? title : '';
|
||||||
|
this.subTitle = subTitle ? subTitle : '';
|
||||||
|
this.message = message ? message : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
preload() {
|
||||||
|
this.load.image(Textures.icon, "resources/logos/tcm_full.png");
|
||||||
|
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
||||||
|
this.load.bitmapFont(Textures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
||||||
|
this.load.spritesheet(
|
||||||
|
'cat',
|
||||||
|
'resources/characters/pipoya/Cat 01-1.png',
|
||||||
|
{frameWidth: 32, frameHeight: 32}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, Textures.icon);
|
||||||
|
this.add.existing(this.logo);
|
||||||
|
|
||||||
|
this.titleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, this.title);
|
||||||
|
|
||||||
|
this.subTitleField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, this.subTitle);
|
||||||
|
|
||||||
|
this.messageField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 38, this.message, {
|
||||||
|
fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif',
|
||||||
|
fontSize: '10px'
|
||||||
|
});
|
||||||
|
this.messageField.setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
|
this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat', 6);
|
||||||
|
this.cat.flipY = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the error page, with an error message matching the "error" parameters passed in.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
public static showError(error: any, scene: ScenePlugin): void {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
if (typeof error === 'string' || error instanceof String) {
|
||||||
|
scene.start(ErrorSceneName, {
|
||||||
|
title: 'An error occurred',
|
||||||
|
subTitle: error
|
||||||
|
});
|
||||||
|
} else if (error instanceof WAError) {
|
||||||
|
scene.start(ErrorSceneName, {
|
||||||
|
title: error.title,
|
||||||
|
subTitle: error.subTitle,
|
||||||
|
message: error.details
|
||||||
|
});
|
||||||
|
} else if (error.response) {
|
||||||
|
// Axios HTTP error
|
||||||
|
// client received an error response (5xx, 4xx)
|
||||||
|
scene.start(ErrorSceneName, {
|
||||||
|
title: 'HTTP ' + error.response.status + ' - ' + error.response.statusText,
|
||||||
|
subTitle: 'An error occurred while accessing URL:',
|
||||||
|
message: error.response.config.url
|
||||||
|
});
|
||||||
|
} else if (error.request) {
|
||||||
|
// Axios HTTP error
|
||||||
|
// client never received a response, or request never left
|
||||||
|
scene.start(ErrorSceneName, {
|
||||||
|
title: 'Network error',
|
||||||
|
subTitle: error.message
|
||||||
|
});
|
||||||
|
} else if (error instanceof Error) {
|
||||||
|
// Error
|
||||||
|
scene.start(ErrorSceneName, {
|
||||||
|
title: 'An error occurred',
|
||||||
|
subTitle: error.name,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the error page, with an error message matching the "error" parameters passed in.
|
||||||
|
*/
|
||||||
|
public static startErrorPage(title: string, subTitle: string, message: string, scene: ScenePlugin): void {
|
||||||
|
scene.start(ErrorSceneName, {
|
||||||
|
title,
|
||||||
|
subTitle,
|
||||||
|
message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +0,0 @@
|
|||||||
import {TextField} from "../Components/TextField";
|
|
||||||
import Image = Phaser.GameObjects.Image;
|
|
||||||
import Sprite = Phaser.GameObjects.Sprite;
|
|
||||||
import Text = Phaser.GameObjects.Text;
|
|
||||||
|
|
||||||
export const FourOFourSceneName = "FourOFourScene";
|
|
||||||
enum Textures {
|
|
||||||
icon = "icon",
|
|
||||||
mainFont = "main_font"
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FourOFourScene extends Phaser.Scene {
|
|
||||||
private mapNotFoundField!: TextField;
|
|
||||||
private couldNotFindField!: TextField;
|
|
||||||
private fileNameField!: Text;
|
|
||||||
private logo!: Image;
|
|
||||||
private cat!: Sprite;
|
|
||||||
private file: string|undefined;
|
|
||||||
private url: string|undefined;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
key: FourOFourSceneName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
init({ file, url }: { file?: string, url?: string }) {
|
|
||||||
this.file = file;
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
preload() {
|
|
||||||
this.load.image(Textures.icon, "resources/logos/tcm_full.png");
|
|
||||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
|
||||||
this.load.bitmapFont(Textures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
|
||||||
this.load.spritesheet(
|
|
||||||
'cat',
|
|
||||||
'resources/characters/pipoya/Cat 01-1.png',
|
|
||||||
{frameWidth: 32, frameHeight: 32}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
create() {
|
|
||||||
this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, Textures.icon);
|
|
||||||
this.add.existing(this.logo);
|
|
||||||
|
|
||||||
this.mapNotFoundField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2, "404 - File not found");
|
|
||||||
|
|
||||||
let text: string = '';
|
|
||||||
if (this.file !== undefined) {
|
|
||||||
text = "Could not load map"
|
|
||||||
}
|
|
||||||
if (this.url !== undefined) {
|
|
||||||
text = "Invalid URL"
|
|
||||||
}
|
|
||||||
|
|
||||||
this.couldNotFindField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height / 2 + 24, text);
|
|
||||||
|
|
||||||
const url = this.file ? this.file : this.url;
|
|
||||||
if (url !== undefined) {
|
|
||||||
this.fileNameField = this.add.text(this.game.renderer.width / 2, this.game.renderer.height / 2 + 38, url, { fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif', fontSize: '10px' });
|
|
||||||
this.fileNameField.setOrigin(0.5, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, 'cat', 6);
|
|
||||||
this.cat.flipY=true;
|
|
||||||
}
|
|
||||||
}
|
|
26
front/src/Phaser/Reconnecting/WAError.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export class WAError extends Error {
|
||||||
|
private _title: string;
|
||||||
|
private _subTitle: string;
|
||||||
|
private _details: string;
|
||||||
|
|
||||||
|
constructor (title: string, subTitle: string, details: string) {
|
||||||
|
super(title+' - '+subTitle+' - '+details);
|
||||||
|
this._title = title;
|
||||||
|
this._subTitle = subTitle;
|
||||||
|
this._details = details;
|
||||||
|
// Set the prototype explicitly.
|
||||||
|
Object.setPrototypeOf (this, WAError.prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
get title(): string {
|
||||||
|
return this._title;
|
||||||
|
}
|
||||||
|
|
||||||
|
get subTitle(): string {
|
||||||
|
return this._subTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
get details(): string {
|
||||||
|
return this._details;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline {
|
export class OutlinePipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline {
|
||||||
|
|
||||||
// the unique id of this pipeline
|
// the unique id of this pipeline
|
||||||
public static readonly KEY = 'Outline';
|
public static readonly KEY = 'Outline';
|
||||||
|
@ -60,7 +60,7 @@ export class UserInputManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearAllInputKeyboard(){
|
clearAllInputKeyboard(){
|
||||||
this.Scene.input.keyboard.removeAllKeys();
|
this.Scene.input.keyboard.removeAllListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
getEventListForGameTick(): ActiveEventList {
|
getEventListForGameTick(): ActiveEventList {
|
||||||
|
@ -32,20 +32,6 @@ class UrlManager {
|
|||||||
return match ? match [1] : null;
|
return match ? match [1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//todo: simply use the roomId
|
|
||||||
//todo: test this with cypress
|
|
||||||
public editUrlForRoom(roomSlug: string, organizationSlug: string|null, worldSlug: string |null): string {
|
|
||||||
let newUrl:string;
|
|
||||||
if (organizationSlug) {
|
|
||||||
newUrl = '/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug;
|
|
||||||
} else {
|
|
||||||
newUrl = '/_/global/'+roomSlug;
|
|
||||||
}
|
|
||||||
history.pushState({}, 'WorkAdventure', newUrl);
|
|
||||||
return newUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public pushRoomIdToUrl(room:Room): void {
|
public pushRoomIdToUrl(room:Room): void {
|
||||||
if (window.location.pathname === room.id) return;
|
if (window.location.pathname === room.id) return;
|
||||||
const hash = window.location.hash;
|
const hash = window.location.hash;
|
||||||
|
@ -59,6 +59,16 @@ export class DiscussionManager {
|
|||||||
const sendDivMessage: HTMLDivElement = document.createElement('div');
|
const sendDivMessage: HTMLDivElement = document.createElement('div');
|
||||||
sendDivMessage.classList.add('send-message');
|
sendDivMessage.classList.add('send-message');
|
||||||
const inputMessage: HTMLInputElement = document.createElement('input');
|
const inputMessage: HTMLInputElement = document.createElement('input');
|
||||||
|
inputMessage.onfocus = () => {
|
||||||
|
if(this.userInputManager) {
|
||||||
|
this.userInputManager.clearAllInputKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputMessage.onblur = () => {
|
||||||
|
if(this.userInputManager) {
|
||||||
|
this.userInputManager.initKeyBoardEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
inputMessage.type = "text";
|
inputMessage.type = "text";
|
||||||
inputMessage.addEventListener('keyup', (event: KeyboardEvent) => {
|
inputMessage.addEventListener('keyup', (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
@ -151,7 +161,7 @@ export class DiscussionManager {
|
|||||||
const pMessage: HTMLParagraphElement = document.createElement('p');
|
const pMessage: HTMLParagraphElement = document.createElement('p');
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
if(isMe){
|
if(isMe){
|
||||||
name = 'Moi';
|
name = 'Me';
|
||||||
}
|
}
|
||||||
pMessage.innerHTML = `<span style="font-weight: bold">${name}</span>
|
pMessage.innerHTML = `<span style="font-weight: bold">${name}</span>
|
||||||
<span style="color:#bac2cc;display:inline-block;font-size:12px;">
|
<span style="color:#bac2cc;display:inline-block;font-size:12px;">
|
||||||
@ -160,11 +170,18 @@ export class DiscussionManager {
|
|||||||
divMessage.appendChild(pMessage);
|
divMessage.appendChild(pMessage);
|
||||||
|
|
||||||
const userMessage: HTMLParagraphElement = document.createElement('p');
|
const userMessage: HTMLParagraphElement = document.createElement('p');
|
||||||
userMessage.innerText = message;
|
userMessage.innerHTML = HtmlUtils.urlify(message);
|
||||||
userMessage.classList.add('body');
|
userMessage.classList.add('body');
|
||||||
divMessage.appendChild(userMessage);
|
divMessage.appendChild(userMessage);
|
||||||
|
|
||||||
this.divMessages?.appendChild(divMessage);
|
this.divMessages?.appendChild(divMessage);
|
||||||
|
|
||||||
|
//automatic scroll when there are new message
|
||||||
|
setTimeout(() => {
|
||||||
|
this.divMessages?.scroll({
|
||||||
|
top: this.divMessages?.scrollTop + divMessage.getBoundingClientRect().y,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeParticipant(userId: number|string){
|
public removeParticipant(userId: number|string){
|
||||||
@ -188,17 +205,11 @@ export class DiscussionManager {
|
|||||||
|
|
||||||
private showDiscussion(){
|
private showDiscussion(){
|
||||||
this.activeDiscussion = true;
|
this.activeDiscussion = true;
|
||||||
if(this.userInputManager) {
|
|
||||||
this.userInputManager.clearAllInputKeyboard();
|
|
||||||
}
|
|
||||||
this.divDiscuss?.classList.add('active');
|
this.divDiscuss?.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
private hideDiscussion(){
|
private hideDiscussion(){
|
||||||
this.activeDiscussion = false;
|
this.activeDiscussion = false;
|
||||||
if(this.userInputManager) {
|
|
||||||
this.userInputManager.initKeyBoardEvent();
|
|
||||||
}
|
|
||||||
this.divDiscuss?.classList.remove('active');
|
this.divDiscuss?.classList.remove('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,29 @@
|
|||||||
export class HtmlUtils {
|
export class HtmlUtils {
|
||||||
public static getElementByIdOrFail<T extends HTMLElement>(id: string): T {
|
public static getElementByIdOrFail<T extends HTMLElement>(id: string): T {
|
||||||
const elem = document.getElementById(id);
|
const elem = document.getElementById(id);
|
||||||
if (elem === null) {
|
if (HtmlUtils.isHtmlElement<T>(elem)) {
|
||||||
throw new Error("Cannot find HTML element with id '"+id+"'");
|
return elem;
|
||||||
}
|
}
|
||||||
// FIXME: does not check the type of the returned type
|
throw new Error("Cannot find HTML element with id '"+id+"'");
|
||||||
return elem as T;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static removeElementByIdOrFail<T extends HTMLElement>(id: string): T {
|
public static removeElementByIdOrFail<T extends HTMLElement>(id: string): T {
|
||||||
const elem = document.getElementById(id);
|
const elem = document.getElementById(id);
|
||||||
if (elem === null) {
|
if (HtmlUtils.isHtmlElement<T>(elem)) {
|
||||||
throw new Error("Cannot find HTML element with id '"+id+"'");
|
elem.remove();
|
||||||
|
return elem;
|
||||||
}
|
}
|
||||||
// FIXME: does not check the type of the returned type
|
throw new Error("Cannot find HTML element with id '"+id+"'");
|
||||||
elem.remove();
|
}
|
||||||
return elem as T;
|
|
||||||
|
public static urlify(text: string): string {
|
||||||
|
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
||||||
|
return text.replace(urlRegex, (url: string) => {
|
||||||
|
return '<a href="' + url + '" target="_blank" style=":visited {color: white}">' + url + '</a>';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private static isHtmlElement<T extends HTMLElement>(elem: HTMLElement | null): elem is T {
|
||||||
|
return elem !== null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ if(localValueVideo){
|
|||||||
let videoConstraint: boolean|MediaTrackConstraints = {
|
let videoConstraint: boolean|MediaTrackConstraints = {
|
||||||
width: { min: 640, ideal: 1280, max: 1920 },
|
width: { min: 640, ideal: 1280, max: 1920 },
|
||||||
height: { min: 400, ideal: 720 },
|
height: { min: 400, ideal: 720 },
|
||||||
frameRate: {exact: valueVideo, ideal: valueVideo},
|
frameRate: { ideal: valueVideo },
|
||||||
facingMode: "user",
|
facingMode: "user",
|
||||||
resizeMode: 'crop-and-scale',
|
resizeMode: 'crop-and-scale',
|
||||||
aspectRatio: 1.777777778
|
aspectRatio: 1.777777778
|
||||||
@ -209,10 +209,19 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enableCamera() {
|
public enableCamera() {
|
||||||
this.enableCameraStyle();
|
|
||||||
this.constraintsMedia.video = videoConstraint;
|
this.constraintsMedia.video = videoConstraint;
|
||||||
|
|
||||||
this.getCamera().then((stream: MediaStream) => {
|
this.getCamera().then((stream: MediaStream) => {
|
||||||
|
//TODO show error message tooltip upper of camera button
|
||||||
|
//TODO message : please check camera permission of your navigator
|
||||||
|
if(stream.getVideoTracks().length === 0) {
|
||||||
|
throw Error('Video track is empty, please check camera permission of your navigator')
|
||||||
|
}
|
||||||
|
this.enableCameraStyle();
|
||||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.disableCameraStyle();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,11 +237,19 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enableMicrophone() {
|
public enableMicrophone() {
|
||||||
this.enableMicrophoneStyle();
|
|
||||||
this.constraintsMedia.audio = true;
|
this.constraintsMedia.audio = true;
|
||||||
|
|
||||||
this.getCamera().then((stream) => {
|
this.getCamera().then((stream) => {
|
||||||
|
//TODO show error message tooltip upper of camera button
|
||||||
|
//TODO message : please check microphone permission of your navigator
|
||||||
|
if(stream.getAudioTracks().length === 0) {
|
||||||
|
throw Error('Audio track is empty, please check microphone permission of your navigator')
|
||||||
|
}
|
||||||
|
this.enableMicrophoneStyle();
|
||||||
this.triggerUpdatedLocalStreamCallbacks(stream);
|
this.triggerUpdatedLocalStreamCallbacks(stream);
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.disableMicrophoneStyle();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,6 +335,9 @@ export class MediaManager {
|
|||||||
const localScreenCapture = this.localScreenCapture;
|
const localScreenCapture = this.localScreenCapture;
|
||||||
this.getCamera().then((stream) => {
|
this.getCamera().then((stream) => {
|
||||||
this.triggerStoppedScreenSharingCallbacks(localScreenCapture);
|
this.triggerStoppedScreenSharingCallbacks(localScreenCapture);
|
||||||
|
}).catch((err) => { //catch error get camera
|
||||||
|
console.error(err);
|
||||||
|
this.triggerStoppedScreenSharingCallbacks(localScreenCapture);
|
||||||
});
|
});
|
||||||
this.localScreenCapture = null;
|
this.localScreenCapture = null;
|
||||||
}
|
}
|
||||||
|
@ -229,6 +229,14 @@ export class SimplePeer {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("closeConnection", err)
|
console.error("closeConnection", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if user left discussion, clear array peer connection of sharing
|
||||||
|
if(this.Users.length === 0) {
|
||||||
|
for (const userId of this.PeerScreenSharingConnectionArray.keys()) {
|
||||||
|
this.closeScreenSharingConnection(userId);
|
||||||
|
this.PeerScreenSharingConnectionArray.delete(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,9 +253,11 @@ export class SimplePeer {
|
|||||||
// FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray"
|
// FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray"
|
||||||
// I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel.
|
// I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel.
|
||||||
peer.destroy();
|
peer.destroy();
|
||||||
if(!this.PeerScreenSharingConnectionArray.delete(userId)){
|
|
||||||
|
//Comment this peer connexion because if we delete and try to reshare screen, the RTCPeerConnection send renegociate event. This array will be remove when user left circle discussion
|
||||||
|
/*if(!this.PeerScreenSharingConnectionArray.delete(userId)){
|
||||||
throw 'Couln\'t delete peer screen sharing connexion';
|
throw 'Couln\'t delete peer screen sharing connexion';
|
||||||
}
|
}*/
|
||||||
//console.log('Nb users in peerConnectionArray '+this.PeerConnectionArray.size);
|
//console.log('Nb users in peerConnectionArray '+this.PeerConnectionArray.size);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("closeConnection", err)
|
console.error("closeConnection", err)
|
||||||
@ -301,11 +311,13 @@ export class SimplePeer {
|
|||||||
peer.signal(data.signal);
|
peer.signal(data.signal);
|
||||||
} else {
|
} else {
|
||||||
console.error('Could not find peer whose ID is "'+data.userId+'" in receiveWebrtcScreenSharingSignal');
|
console.error('Could not find peer whose ID is "'+data.userId+'" in receiveWebrtcScreenSharingSignal');
|
||||||
|
console.info('tentative to create new peer connexion');
|
||||||
|
this.sendLocalScreenSharingStreamToUser(data.userId);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`receiveWebrtcSignal => ${data.userId}`, e);
|
console.error(`receiveWebrtcSignal => ${data.userId}`, e);
|
||||||
//force delete and recreate peer connexion
|
//Comment this peer connexion because if we delete and try to reshare screen, the RTCPeerConnection send renegociate event. This array will be remove when user left circle discussion
|
||||||
this.PeerScreenSharingConnectionArray.delete(data.userId);
|
//this.PeerScreenSharingConnectionArray.delete(data.userId);
|
||||||
this.receiveWebrtcScreenSharingSignal(data);
|
this.receiveWebrtcScreenSharingSignal(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,8 +428,8 @@ export class SimplePeer {
|
|||||||
|
|
||||||
if (!PeerConnectionScreenSharing.isReceivingScreenSharingStream()) {
|
if (!PeerConnectionScreenSharing.isReceivingScreenSharingStream()) {
|
||||||
PeerConnectionScreenSharing.destroy();
|
PeerConnectionScreenSharing.destroy();
|
||||||
|
//Comment this peer connexion because if we delete and try to reshare screen, the RTCPeerConnection send renegociate event. This array will be remove when user left circle discussion
|
||||||
this.PeerScreenSharingConnectionArray.delete(userId);
|
//this.PeerScreenSharingConnectionArray.delete(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import 'phaser';
|
import 'phaser';
|
||||||
import GameConfig = Phaser.Types.Core.GameConfig;
|
import GameConfig = Phaser.Types.Core.GameConfig;
|
||||||
import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable";
|
import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable";
|
||||||
import {cypressAsserter} from "./Cypress/CypressAsserter";
|
|
||||||
import {LoginScene} from "./Phaser/Login/LoginScene";
|
import {LoginScene} from "./Phaser/Login/LoginScene";
|
||||||
import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene";
|
import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene";
|
||||||
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene";
|
||||||
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
|
import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene";
|
||||||
import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene";
|
|
||||||
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
|
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
|
||||||
import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline";
|
import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline";
|
||||||
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
|
||||||
@ -15,6 +13,7 @@ import {EntryScene} from "./Phaser/Login/EntryScene";
|
|||||||
import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
|
import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
|
||||||
import {MenuScene} from "./Phaser/Menu/MenuScene";
|
import {MenuScene} from "./Phaser/Menu/MenuScene";
|
||||||
import {localUserStore} from "./Connexion/LocalUserStore";
|
import {localUserStore} from "./Connexion/LocalUserStore";
|
||||||
|
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
|
||||||
|
|
||||||
// Load Jitsi if the environment variable is set.
|
// Load Jitsi if the environment variable is set.
|
||||||
if (JITSI_URL) {
|
if (JITSI_URL) {
|
||||||
@ -53,13 +52,33 @@ const fps : Phaser.Types.Core.FPSConfig = {
|
|||||||
smoothStep: false
|
smoothStep: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the ?phaserMode=canvas parameter can be used to force Canvas usage
|
||||||
|
const params = new URLSearchParams(document.location.search.substring(1));
|
||||||
|
const phaserMode = params.get("phaserMode");
|
||||||
|
let mode: number;
|
||||||
|
switch (phaserMode) {
|
||||||
|
case 'auto':
|
||||||
|
case null:
|
||||||
|
mode = Phaser.AUTO;
|
||||||
|
break;
|
||||||
|
case 'canvas':
|
||||||
|
mode = Phaser.CANVAS;
|
||||||
|
break;
|
||||||
|
case 'webgl':
|
||||||
|
mode = Phaser.WEBGL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('phaserMode parameter must be one of "auto", "canvas" or "webgl"');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const config: GameConfig = {
|
const config: GameConfig = {
|
||||||
type: Phaser.AUTO,
|
type: mode,
|
||||||
title: "WorkAdventure",
|
title: "WorkAdventure",
|
||||||
width: width / RESOLUTION,
|
width: width / RESOLUTION,
|
||||||
height: height / RESOLUTION,
|
height: height / RESOLUTION,
|
||||||
parent: "game",
|
parent: "game",
|
||||||
scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, FourOFourScene, CustomizeScene, MenuScene],
|
scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, ErrorScene, CustomizeScene, MenuScene],
|
||||||
zoom: RESOLUTION,
|
zoom: RESOLUTION,
|
||||||
fps: fps,
|
fps: fps,
|
||||||
dom: {
|
dom: {
|
||||||
@ -73,15 +92,15 @@ const config: GameConfig = {
|
|||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
postBoot: game => {
|
postBoot: game => {
|
||||||
// FIXME: we should fore WebGL in the config.
|
// Commented out to try to fix MacOS bug
|
||||||
const renderer = game.renderer as WebGLRenderer;
|
/*const renderer = game.renderer;
|
||||||
renderer.pipelines.add(OutlinePipeline.KEY, new OutlinePipeline(game));
|
if (renderer instanceof WebGLRenderer) {
|
||||||
|
renderer.pipelines.add(OutlinePipeline.KEY, new OutlinePipeline(game));
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
cypressAsserter.gameStarted();
|
|
||||||
|
|
||||||
const game = new Phaser.Game(config);
|
const game = new Phaser.Game(config);
|
||||||
|
|
||||||
window.addEventListener('resize', function (event) {
|
window.addEventListener('resize', function (event) {
|
||||||
|
26
front/templater.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -x
|
||||||
|
set -o nounset errexit
|
||||||
|
template_file_index=/usr/share/nginx/html/index.html.tmpl
|
||||||
|
generated_file_index=/usr/share/nginx/html/index.html
|
||||||
|
tmp_trackcodefile=/tmp/trackcode
|
||||||
|
|
||||||
|
# To inject tracking code, you have two choices:
|
||||||
|
# 1) Template using the provided google analytics
|
||||||
|
# 2) Insert with your own provided code, by overriding the ANALYTICS_CODE_PATH
|
||||||
|
# The ANALYTICS_CODE_PATH is the location of a file inside the container
|
||||||
|
ANALYTICS_CODE_PATH="${ANALYTICS_CODE_PATH:-/usr/share/nginx/html/ga.html.tmpl}"
|
||||||
|
|
||||||
|
if [[ "${INSERT_ANALYTICS:-NO}" == "NO" ]]; then
|
||||||
|
echo "" > "${tmp_trackcodefile}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Automatically insert analytics if GA_TRACKING_ID is set
|
||||||
|
if [[ "${GA_TRACKING_ID:-}" != "" || "${INSERT_ANALYTICS:-NO}" != "NO" ]]; then
|
||||||
|
echo "Templating code from ${ANALYTICS_CODE_PATH}"
|
||||||
|
sed "s#<!-- TRACKING NUMBER -->#${GA_TRACKING_ID:-}#g" "${ANALYTICS_CODE_PATH}" > "$tmp_trackcodefile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Templating ${generated_file_index} from ${template_file_index}"
|
||||||
|
sed "/<!-- TRACK CODE -->/r ${tmp_trackcodefile}" "${template_file_index}" > "${generated_file_index}"
|
||||||
|
rm "${tmp_trackcodefile}"
|
14
front/tests/Phaser/Game/HtmlUtilsTest.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import "jasmine";
|
||||||
|
import {HtmlUtils} from "../../../src/WebRtc/HtmlUtils";
|
||||||
|
|
||||||
|
describe("urlify()", () => {
|
||||||
|
it("should transform an url into a link", () => {
|
||||||
|
const text = HtmlUtils.urlify('https://google.com');
|
||||||
|
expect(text).toEqual('<a href="https://google.com" target="_blank" style=":visited {color: white}">https://google.com</a>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not transform a normal text into a link", () => {
|
||||||
|
const text = HtmlUtils.urlify('hello');
|
||||||
|
expect(text).toEqual('hello');
|
||||||
|
});
|
||||||
|
});
|
28
front/tests/Phaser/Game/PlayerTexturesLoadingTest.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import "jasmine";
|
||||||
|
import {getRessourceDescriptor} from "../../../src/Phaser/Entity/PlayerTexturesLoadingManager";
|
||||||
|
|
||||||
|
describe("getRessourceDescriptor()", () => {
|
||||||
|
it(", if given a valid descriptor as parameter, should return it", () => {
|
||||||
|
const desc = getRessourceDescriptor({name: 'name', img: 'url'});
|
||||||
|
expect(desc.name).toEqual('name');
|
||||||
|
expect(desc.img).toEqual('url');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(", if given a string as parameter, should search trough hardcoded values", () => {
|
||||||
|
const desc = getRessourceDescriptor('male1');
|
||||||
|
expect(desc.name).toEqual('male1');
|
||||||
|
expect(desc.img).toEqual("resources/characters/pipoya/Male 01-1.png");
|
||||||
|
});
|
||||||
|
|
||||||
|
it(", if given a string as parameter, should search trough hardcoded values (bis)", () => {
|
||||||
|
const desc = getRessourceDescriptor('color_2');
|
||||||
|
expect(desc.name).toEqual('color_2');
|
||||||
|
expect(desc.img).toEqual("resources/customisation/character_color/character_color1.png");
|
||||||
|
});
|
||||||
|
|
||||||
|
it(", if given a descriptor without url as parameter, should search trough hardcoded values", () => {
|
||||||
|
const desc = getRessourceDescriptor({name: 'male1', img: ''});
|
||||||
|
expect(desc.name).toEqual('male1');
|
||||||
|
expect(desc.img).toEqual("resources/characters/pipoya/Male 01-1.png");
|
||||||
|
});
|
||||||
|
});
|
@ -45,7 +45,7 @@ module.exports = {
|
|||||||
new webpack.ProvidePlugin({
|
new webpack.ProvidePlugin({
|
||||||
Phaser: 'phaser'
|
Phaser: 'phaser'
|
||||||
}),
|
}),
|
||||||
new webpack.EnvironmentPlugin(['API_URL', 'UPLOADER_URL', 'ADMIN_URL', 'DEBUG_MODE', 'TURN_SERVER', 'TURN_USER', 'TURN_PASSWORD', 'JITSI_URL', 'JITSI_PRIVATE_MODE'])
|
new webpack.EnvironmentPlugin(['API_URL', 'UPLOADER_URL', 'ADMIN_URL', 'DEBUG_MODE', 'TURN_SERVER', 'TURN_USER', 'TURN_PASSWORD', 'JITSI_URL', 'JITSI_PRIVATE_MODE', 'START_ROOM_URL'])
|
||||||
],
|
],
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1771,7 +1771,7 @@ eventemitter3@^2.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
|
||||||
integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
|
integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
|
||||||
|
|
||||||
eventemitter3@^4.0.0, eventemitter3@^4.0.7:
|
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
|
||||||
version "4.0.7"
|
version "4.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||||
@ -1829,7 +1829,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
homedir-polyfill "^1.0.1"
|
homedir-polyfill "^1.0.1"
|
||||||
|
|
||||||
exports-loader@^1.1.1:
|
exports-loader@^1.1.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-1.1.1.tgz#88c9a6877ee6a5519d7c41a016bdd99148421e69"
|
resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-1.1.1.tgz#88c9a6877ee6a5519d7c41a016bdd99148421e69"
|
||||||
integrity sha512-CmyhIR2sJ3KOfVsHjsR0Yvo+0lhRhRMAevCbB8dhTVLHsZPs0lCQTvRmR9YNvBXDBxUuhmCE2f54KqEjZUaFrg==
|
integrity sha512-CmyhIR2sJ3KOfVsHjsR0Yvo+0lhRhRMAevCbB8dhTVLHsZPs0lCQTvRmR9YNvBXDBxUuhmCE2f54KqEjZUaFrg==
|
||||||
@ -2528,7 +2528,7 @@ import-local@^2.0.0:
|
|||||||
pkg-dir "^3.0.0"
|
pkg-dir "^3.0.0"
|
||||||
resolve-cwd "^2.0.0"
|
resolve-cwd "^2.0.0"
|
||||||
|
|
||||||
imports-loader@^1.2.0:
|
imports-loader@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-1.2.0.tgz#b06823d0bb42e6f5ff89bc893829000eda46693f"
|
resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-1.2.0.tgz#b06823d0bb42e6f5ff89bc893829000eda46693f"
|
||||||
integrity sha512-zPvangKEgrrPeqeUqH0Uhc59YqK07JqZBi9a9cQ3v/EKUIqrbJHY4CvUrDus2lgQa5AmPyXuGrWP8JJTqzE5RQ==
|
integrity sha512-zPvangKEgrrPeqeUqH0Uhc59YqK07JqZBi9a9cQ3v/EKUIqrbJHY4CvUrDus2lgQa5AmPyXuGrWP8JJTqzE5RQ==
|
||||||
@ -3663,14 +3663,14 @@ pbkdf2@^3.0.3:
|
|||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
sha.js "^2.4.8"
|
sha.js "^2.4.8"
|
||||||
|
|
||||||
phaser@^3.22.0:
|
phaser@3.24.1:
|
||||||
version "3.51.0"
|
version "3.24.1"
|
||||||
resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.51.0.tgz#b0c7ee2b21e795830d74f476dd30816a42b023bd"
|
resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.24.1.tgz#376e0c965d2a35af37c06ee78627dafbde5be017"
|
||||||
integrity sha512-Z7XNToZWO60Zx/YetaoeGSeELy5ND45TPPfYB9HtQU2692ACXc/nioQaWp20NzTMgeBsgl6vYf3CI82y/DzSyg==
|
integrity sha512-WbrRMkbpEzarkfrq83akeauc6b8xNxsOTpDygyW7wrU2G2ne6kOYu3hji4UAaGnZaOLrVuj8ycYPjX9P1LxcDw==
|
||||||
dependencies:
|
dependencies:
|
||||||
eventemitter3 "^4.0.7"
|
eventemitter3 "^4.0.4"
|
||||||
exports-loader "^1.1.1"
|
exports-loader "^1.1.0"
|
||||||
imports-loader "^1.2.0"
|
imports-loader "^1.1.0"
|
||||||
path "^0.12.7"
|
path "^0.12.7"
|
||||||
|
|
||||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
|
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
|
||||||
|
@ -278,7 +278,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitUrl",
|
"name":"exitUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor1\/floor1.json"
|
"value":"\/@\/tcm\/workadventure\/floor1"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitSceneUrl",
|
"name":"exitSceneUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor0\/floor0.json#down-the-stairs"
|
"value":"\/@\/tcm\/workadventure\/floor0#down-the-stairs"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
@ -103,7 +103,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitSceneUrl",
|
"name":"exitSceneUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor2\/floor2.json#down-the-stairs"
|
"value":"\/@\/tcm\/workadventure\/floor2#down-the-stairs"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
@ -121,7 +121,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitSceneUrl",
|
"name":"exitSceneUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor2\/floor2.json#down-the-stairs-secours"
|
"value":"\/@\/tcm\/workadventure\/floor2#down-the-stairs-secours"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitSceneUrl",
|
"name":"exitSceneUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor1\/floor1.json#down-the-stairs"
|
"value":"\/@\/tcm\/workadventure\/floor1#down-the-stairs"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
@ -139,7 +139,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitSceneUrl",
|
"name":"exitSceneUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor1\/floor1.json#down-the-stairs-secours"
|
"value":"\/@\/tcm\/workadventure\/floor1#down-the-stairs-secours"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
{
|
{
|
||||||
"name":"exitSceneUrl",
|
"name":"exitSceneUrl",
|
||||||
"type":"string",
|
"type":"string",
|
||||||
"value":"..\/Floor0\/floor0.json"
|
"value":"\/@\/tcm\/workadventure\/floor0"
|
||||||
}],
|
}],
|
||||||
"type":"tilelayer",
|
"type":"tilelayer",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
|
@ -193,6 +193,11 @@ message SendUserMessage{
|
|||||||
string message = 2;
|
string message = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message BanUserMessage{
|
||||||
|
string type = 1;
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message ServerToClientMessage {
|
message ServerToClientMessage {
|
||||||
oneof message {
|
oneof message {
|
||||||
BatchMessage batchMessage = 1;
|
BatchMessage batchMessage = 1;
|
||||||
@ -207,6 +212,7 @@ message ServerToClientMessage {
|
|||||||
TeleportMessageMessage teleportMessageMessage = 10;
|
TeleportMessageMessage teleportMessageMessage = 10;
|
||||||
SendJitsiJwtMessage sendJitsiJwtMessage = 11;
|
SendJitsiJwtMessage sendJitsiJwtMessage = 11;
|
||||||
SendUserMessage sendUserMessage = 12;
|
SendUserMessage sendUserMessage = 12;
|
||||||
|
BanUserMessage banUserMessage = 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +226,7 @@ message JoinRoomMessage {
|
|||||||
string userUuid = 4;
|
string userUuid = 4;
|
||||||
string roomId = 5;
|
string roomId = 5;
|
||||||
repeated string tag = 6;
|
repeated string tag = 6;
|
||||||
|
string IPAddress = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UserJoinedZoneMessage {
|
message UserJoinedZoneMessage {
|
||||||
@ -272,6 +279,8 @@ message PusherToBackMessage {
|
|||||||
StopGlobalMessage stopGlobalMessage = 9;
|
StopGlobalMessage stopGlobalMessage = 9;
|
||||||
ReportPlayerMessage reportPlayerMessage = 10;
|
ReportPlayerMessage reportPlayerMessage = 10;
|
||||||
QueryJitsiJwtMessage queryJitsiJwtMessage = 11;
|
QueryJitsiJwtMessage queryJitsiJwtMessage = 11;
|
||||||
|
SendUserMessage sendUserMessage = 12;
|
||||||
|
BanUserMessage banUserMessage = 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,6 +297,7 @@ message SubToPusherMessage {
|
|||||||
UserLeftZoneMessage userLeftZoneMessage = 5;
|
UserLeftZoneMessage userLeftZoneMessage = 5;
|
||||||
ItemEventMessage itemEventMessage = 6;
|
ItemEventMessage itemEventMessage = 6;
|
||||||
SendUserMessage sendUserMessage = 7;
|
SendUserMessage sendUserMessage = 7;
|
||||||
|
BanUserMessage banUserMessage = 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,10 +316,20 @@ message ServerToAdminClientMessage {
|
|||||||
repeated SubToAdminPusherMessage payload = 2;
|
repeated SubToAdminPusherMessage payload = 2;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
message UserJoinedRoomMessage {
|
||||||
|
string uuid = 1;
|
||||||
|
string ipAddress = 2;
|
||||||
|
string name = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UserLeftRoomMessage {
|
||||||
|
string uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message ServerToAdminClientMessage {
|
message ServerToAdminClientMessage {
|
||||||
oneof message {
|
oneof message {
|
||||||
string userUuidJoinedRoom = 1;
|
UserJoinedRoomMessage userJoinedRoom = 1;
|
||||||
string userUuidLeftRoom = 2;
|
UserLeftRoomMessage userLeftRoom = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
"runprod": "node --max-old-space-size=4096 ./dist/server.js",
|
"runprod": "node --max-old-space-size=4096 ./dist/server.js",
|
||||||
"profile": "tsc && node --prof ./dist/server.js",
|
"profile": "tsc && node --prof ./dist/server.js",
|
||||||
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",
|
||||||
"lint": "node_modules/.bin/eslint src/ . --ext .ts",
|
"lint": "DEBUG= node_modules/.bin/eslint src/ . --ext .ts",
|
||||||
"fix": "node_modules/.bin/eslint --fix src/ . --ext .ts"
|
"fix": "DEBUG= node_modules/.bin/eslint --fix src/ . --ext .ts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -52,7 +52,7 @@
|
|||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"prom-client": "^12.0.0",
|
"prom-client": "^12.0.0",
|
||||||
"query-string": "^6.13.3",
|
"query-string": "^6.13.3",
|
||||||
"systeminformation": "^4.30.5",
|
"systeminformation": "^4.31.1",
|
||||||
"ts-node-dev": "^1.0.0-pre.44",
|
"ts-node-dev": "^1.0.0-pre.44",
|
||||||
"typescript": "^3.8.3",
|
"typescript": "^3.8.3",
|
||||||
"uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0",
|
"uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0",
|
||||||
|
@ -60,10 +60,7 @@ export class AuthenticateController extends BaseController {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("An error happened", e)
|
this.errorToResponse(e, res);
|
||||||
res.writeStatus(e.status || "500 Internal Server Error");
|
|
||||||
this.addCorsHeaders(res);
|
|
||||||
res.end('An error happened');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,4 +8,21 @@ export class BaseController {
|
|||||||
res.writeHeader('access-control-allow-methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
res.writeHeader('access-control-allow-methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
||||||
res.writeHeader('access-control-allow-origin', '*');
|
res.writeHeader('access-control-allow-origin', '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns any exception into a HTTP response (and logs the error)
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
protected errorToResponse(e: any, res: HttpResponse): void {
|
||||||
|
console.error("An error happened", e);
|
||||||
|
if (e.response) {
|
||||||
|
res.writeStatus(e.response.status+" "+e.response.statusText);
|
||||||
|
this.addCorsHeaders(res);
|
||||||
|
res.end("An error occurred: "+e.response.status+" "+e.response.statusText);
|
||||||
|
} else {
|
||||||
|
res.writeStatus("500 Internal Server Error")
|
||||||
|
this.addCorsHeaders(res);
|
||||||
|
res.end("An error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,18 +91,11 @@ export class IoSocketController {
|
|||||||
|
|
||||||
if(message.event === 'user-message') {
|
if(message.event === 'user-message') {
|
||||||
const messageToEmit = (message.message as { message: string, type: string, userUuid: string });
|
const messageToEmit = (message.message as { message: string, type: string, userUuid: string });
|
||||||
switch (message.message.type) {
|
if(messageToEmit.type === 'banned'){
|
||||||
case 'ban': {
|
socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type);
|
||||||
socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, roomId);
|
}
|
||||||
break;
|
if(messageToEmit.type === 'ban') {
|
||||||
}
|
socketManager.emitSendUserMessage(messageToEmit.userUuid, messageToEmit.message, messageToEmit.type);
|
||||||
case 'banned': {
|
|
||||||
socketManager.emitBan(messageToEmit.userUuid, messageToEmit.message, roomId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch (err) {
|
}catch (err) {
|
||||||
@ -148,6 +141,7 @@ export class IoSocketController {
|
|||||||
const websocketKey = req.getHeader('sec-websocket-key');
|
const websocketKey = req.getHeader('sec-websocket-key');
|
||||||
const websocketProtocol = req.getHeader('sec-websocket-protocol');
|
const websocketProtocol = req.getHeader('sec-websocket-protocol');
|
||||||
const websocketExtensions = req.getHeader('sec-websocket-extensions');
|
const websocketExtensions = req.getHeader('sec-websocket-extensions');
|
||||||
|
const IPAddress = req.getHeader('x-forwarded-for');
|
||||||
|
|
||||||
const roomId = query.roomId;
|
const roomId = query.roomId;
|
||||||
if (typeof roomId !== 'string') {
|
if (typeof roomId !== 'string') {
|
||||||
@ -176,7 +170,7 @@ export class IoSocketController {
|
|||||||
characterLayers = [ characterLayers ];
|
characterLayers = [ characterLayers ];
|
||||||
}
|
}
|
||||||
|
|
||||||
const userUuid = await jwtTokenManager.getUserUuidFromToken(token);
|
const userUuid = await jwtTokenManager.getUserUuidFromToken(token, IPAddress, roomId);
|
||||||
|
|
||||||
let memberTags: string[] = [];
|
let memberTags: string[] = [];
|
||||||
let memberTextures: CharacterTexture[] = [];
|
let memberTextures: CharacterTexture[] = [];
|
||||||
@ -217,6 +211,7 @@ export class IoSocketController {
|
|||||||
url,
|
url,
|
||||||
token,
|
token,
|
||||||
userUuid,
|
userUuid,
|
||||||
|
IPAddress,
|
||||||
roomId,
|
roomId,
|
||||||
name,
|
name,
|
||||||
characterLayers: characterLayerObjs,
|
characterLayers: characterLayerObjs,
|
||||||
@ -336,6 +331,7 @@ export class IoSocketController {
|
|||||||
client.userId = this.nextUserId;
|
client.userId = this.nextUserId;
|
||||||
this.nextUserId++;
|
this.nextUserId++;
|
||||||
client.userUuid = ws.userUuid;
|
client.userUuid = ws.userUuid;
|
||||||
|
client.IPAddress = ws.IPAddress;
|
||||||
client.token = ws.token;
|
client.token = ws.token;
|
||||||
client.batchedMessages = new BatchMessage();
|
client.batchedMessages = new BatchMessage();
|
||||||
client.batchTimeout = null;
|
client.batchTimeout = null;
|
||||||
|
@ -59,10 +59,7 @@ export class MapController extends BaseController{
|
|||||||
this.addCorsHeaders(res);
|
this.addCorsHeaders(res);
|
||||||
res.end(JSON.stringify(mapDetails));
|
res.end(JSON.stringify(mapDetails));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.message || e);
|
this.errorToResponse(e, res);
|
||||||
res.writeStatus("500 Internal Server Error")
|
|
||||||
this.addCorsHeaders(res);
|
|
||||||
res.end("An error occurred");
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
||||||
const URL_ROOM_STARTED = "/Floor0/floor0.json";
|
|
||||||
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
|
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
|
||||||
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
|
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
|
||||||
const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false;
|
const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false;
|
||||||
@ -16,7 +15,6 @@ export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as strin
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
SECRET_KEY,
|
SECRET_KEY,
|
||||||
URL_ROOM_STARTED,
|
|
||||||
MINIMUM_DISTANCE,
|
MINIMUM_DISTANCE,
|
||||||
API_URL,
|
API_URL,
|
||||||
ADMIN_API_URL,
|
ADMIN_API_URL,
|
||||||
|
@ -24,6 +24,7 @@ export interface ExSocketInterface extends WebSocket, Identificable {
|
|||||||
roomId: string;
|
roomId: string;
|
||||||
//userId: number; // A temporary (autoincremented) identifier for this user
|
//userId: number; // A temporary (autoincremented) identifier for this user
|
||||||
userUuid: string; // A unique identifier for this user
|
userUuid: string; // A unique identifier for this user
|
||||||
|
IPAddress: string; // IP address
|
||||||
name: string;
|
name: string;
|
||||||
characterLayers: CharacterLayer[];
|
characterLayers: CharacterLayer[];
|
||||||
position: PointInterface;
|
position: PointInterface;
|
||||||
|
@ -14,6 +14,11 @@ export interface AdminApiData {
|
|||||||
textures: CharacterTexture[]
|
textures: CharacterTexture[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdminBannedData {
|
||||||
|
is_banned: boolean,
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface CharacterTexture {
|
export interface CharacterTexture {
|
||||||
id: number,
|
id: number,
|
||||||
level: number,
|
level: number,
|
||||||
@ -110,6 +115,18 @@ class AdminApi {
|
|||||||
headers: {"Authorization": `${ADMIN_API_TOKEN}`}
|
headers: {"Authorization": `${ADMIN_API_TOKEN}`}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async verifyBanUser(organizationMemberToken: string, ipAddress: string, organization: string, world: string): Promise<AdminBannedData> {
|
||||||
|
if (!ADMIN_API_URL) {
|
||||||
|
return Promise.reject('No admin backoffice set!');
|
||||||
|
}
|
||||||
|
//todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case.
|
||||||
|
return Axios.get(ADMIN_API_URL + '/api/check-moderate-user/'+organization+'/'+world+'?ipAddress='+ipAddress+'&token='+organizationMemberToken,
|
||||||
|
{headers: {"Authorization": `${ADMIN_API_TOKEN}`}}
|
||||||
|
).then((data) => {
|
||||||
|
return data.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const adminApi = new AdminApi();
|
export const adminApi = new AdminApi();
|
||||||
|
@ -2,7 +2,7 @@ import {ADMIN_API_URL, ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVar
|
|||||||
import {uuid} from "uuidv4";
|
import {uuid} from "uuidv4";
|
||||||
import Jwt from "jsonwebtoken";
|
import Jwt from "jsonwebtoken";
|
||||||
import {TokenInterface} from "../Controller/AuthenticateController";
|
import {TokenInterface} from "../Controller/AuthenticateController";
|
||||||
import {adminApi, AdminApiData} from "../Services/AdminApi";
|
import {adminApi, AdminBannedData} from "../Services/AdminApi";
|
||||||
|
|
||||||
class JWTTokenManager {
|
class JWTTokenManager {
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ class JWTTokenManager {
|
|||||||
return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token
|
return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getUserUuidFromToken(token: unknown): Promise<string> {
|
public async getUserUuidFromToken(token: unknown, ipAddress?: string, room?: string): Promise<string> {
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error('An authentication error happened, a user tried to connect without a token.');
|
throw new Error('An authentication error happened, a user tried to connect without a token.');
|
||||||
@ -50,14 +50,22 @@ class JWTTokenManager {
|
|||||||
|
|
||||||
if (ADMIN_API_URL) {
|
if (ADMIN_API_URL) {
|
||||||
//verify user in admin
|
//verify user in admin
|
||||||
adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => {
|
let promise = new Promise((resolve) => resolve());
|
||||||
resolve(tokenInterface.userUuid);
|
if(ipAddress && room) {
|
||||||
}).catch((err) => {
|
promise = this.verifyBanUser(tokenInterface.userUuid, ipAddress, room);
|
||||||
//anonymous user
|
}
|
||||||
if(err.response && err.response.status && err.response.status === 404){
|
promise.then(() => {
|
||||||
|
adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => {
|
||||||
resolve(tokenInterface.userUuid);
|
resolve(tokenInterface.userUuid);
|
||||||
return;
|
}).catch((err) => {
|
||||||
}
|
//anonymous user
|
||||||
|
if (err.response && err.response.status && err.response.status === 404) {
|
||||||
|
resolve(tokenInterface.userUuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
}).catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -67,6 +75,27 @@ class JWTTokenManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private verifyBanUser(userUuid: string, ipAddress: string, room: string): Promise<AdminBannedData> {
|
||||||
|
const parts = room.split('/');
|
||||||
|
if (parts.length < 3 || parts[0] !== '@') {
|
||||||
|
return Promise.resolve({
|
||||||
|
is_banned: false,
|
||||||
|
message: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const organization = parts[1];
|
||||||
|
const world = parts[2];
|
||||||
|
return adminApi.verifyBanUser(userUuid, ipAddress, organization, world).then((data: AdminBannedData) => {
|
||||||
|
if (data && data.is_banned) {
|
||||||
|
throw new Error('User was banned');
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}).catch((err) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private isValidToken(token: object): token is TokenInterface {
|
private isValidToken(token: object): token is TokenInterface {
|
||||||
return !(typeof((token as TokenInterface).userUuid) !== 'string');
|
return !(typeof((token as TokenInterface).userUuid) !== 'string');
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,8 @@ import {PusherRoom} from "../Model/PusherRoom";
|
|||||||
import {CharacterLayer, ExSocketInterface} from "../Model/Websocket/ExSocketInterface";
|
import {CharacterLayer, ExSocketInterface} from "../Model/Websocket/ExSocketInterface";
|
||||||
import {
|
import {
|
||||||
GroupDeleteMessage,
|
GroupDeleteMessage,
|
||||||
GroupUpdateMessage,
|
|
||||||
ItemEventMessage,
|
ItemEventMessage,
|
||||||
ItemStateMessage,
|
|
||||||
PlayGlobalMessage,
|
PlayGlobalMessage,
|
||||||
PointMessage,
|
|
||||||
PositionMessage,
|
PositionMessage,
|
||||||
RoomJoinedMessage,
|
RoomJoinedMessage,
|
||||||
ServerToClientMessage,
|
ServerToClientMessage,
|
||||||
@ -14,23 +11,19 @@ import {
|
|||||||
SilentMessage,
|
SilentMessage,
|
||||||
SubMessage,
|
SubMessage,
|
||||||
ReportPlayerMessage,
|
ReportPlayerMessage,
|
||||||
UserJoinedMessage,
|
|
||||||
UserLeftMessage,
|
UserLeftMessage,
|
||||||
UserMovedMessage,
|
|
||||||
UserMovesMessage,
|
UserMovesMessage,
|
||||||
ViewportMessage,
|
ViewportMessage,
|
||||||
WebRtcDisconnectMessage,
|
|
||||||
WebRtcSignalToClientMessage,
|
|
||||||
WebRtcSignalToServerMessage,
|
WebRtcSignalToServerMessage,
|
||||||
WebRtcStartMessage,
|
|
||||||
QueryJitsiJwtMessage,
|
QueryJitsiJwtMessage,
|
||||||
SendJitsiJwtMessage,
|
SendJitsiJwtMessage,
|
||||||
SendUserMessage,
|
|
||||||
JoinRoomMessage,
|
JoinRoomMessage,
|
||||||
CharacterLayerMessage,
|
CharacterLayerMessage,
|
||||||
PusherToBackMessage,
|
PusherToBackMessage,
|
||||||
AdminPusherToBackMessage,
|
AdminPusherToBackMessage,
|
||||||
ServerToAdminClientMessage, AdminMessage, BanMessage
|
ServerToAdminClientMessage,
|
||||||
|
SendUserMessage,
|
||||||
|
BanUserMessage, UserJoinedRoomMessage, UserLeftRoomMessage
|
||||||
} from "../Messages/generated/messages_pb";
|
} from "../Messages/generated/messages_pb";
|
||||||
import {PointInterface} from "../Model/Websocket/PointInterface";
|
import {PointInterface} from "../Model/Websocket/PointInterface";
|
||||||
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
|
||||||
@ -79,23 +72,33 @@ export class SocketManager implements ZoneEventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleAdminRoom(client: ExAdminSocketInterface, roomId: string): Promise<void> {
|
async handleAdminRoom(client: ExAdminSocketInterface, roomId: string): Promise<void> {
|
||||||
console.log('Calling adminRoom')
|
|
||||||
const apiClient = await apiClientRepository.getClient(roomId);
|
const apiClient = await apiClientRepository.getClient(roomId);
|
||||||
const adminRoomStream = apiClient.adminRoom();
|
const adminRoomStream = apiClient.adminRoom();
|
||||||
client.adminConnection = adminRoomStream;
|
client.adminConnection = adminRoomStream;
|
||||||
|
|
||||||
adminRoomStream.on('data', (message: ServerToAdminClientMessage) => {
|
adminRoomStream.on('data', (message: ServerToAdminClientMessage) => {
|
||||||
if (message.hasUseruuidjoinedroom()) {
|
if (message.hasUserjoinedroom()) {
|
||||||
const userUuid = message.getUseruuidjoinedroom();
|
const userJoinedRoomMessage = message.getUserjoinedroom() as UserJoinedRoomMessage;
|
||||||
|
|
||||||
if (!client.disconnecting) {
|
if (!client.disconnecting) {
|
||||||
client.send('MemberJoin:'+userUuid+';'+roomId);
|
client.send(JSON.stringify({
|
||||||
|
type: 'MemberJoin',
|
||||||
|
data: {
|
||||||
|
uuid: userJoinedRoomMessage.getUuid(),
|
||||||
|
name: userJoinedRoomMessage.getName(),
|
||||||
|
ipAddress: userJoinedRoomMessage.getIpaddress(),
|
||||||
|
roomId: roomId,
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
} else if (message.hasUseruuidleftroom()) {
|
} else if (message.hasUserleftroom()) {
|
||||||
const userUuid = message.getUseruuidleftroom();
|
const userLeftRoomMessage = message.getUserleftroom() as UserLeftRoomMessage;
|
||||||
|
|
||||||
if (!client.disconnecting) {
|
if (!client.disconnecting) {
|
||||||
client.send('MemberLeave:'+userUuid+';'+roomId);
|
client.send(JSON.stringify({
|
||||||
|
type: 'MemberLeave',
|
||||||
|
data: {
|
||||||
|
uuid: userLeftRoomMessage.getUuid()
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unexpected admin message');
|
throw new Error('Unexpected admin message');
|
||||||
@ -145,15 +148,16 @@ export class SocketManager implements ZoneEventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleJoinRoom(client: ExSocketInterface): Promise<void> {
|
async handleJoinRoom(client: ExSocketInterface): Promise<void> {
|
||||||
const position = client.position;
|
|
||||||
const viewport = client.viewport;
|
const viewport = client.viewport;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const joinRoomMessage = new JoinRoomMessage();
|
const joinRoomMessage = new JoinRoomMessage();
|
||||||
joinRoomMessage.setUseruuid(client.userUuid);
|
joinRoomMessage.setUseruuid(client.userUuid);
|
||||||
|
joinRoomMessage.setIpaddress(client.IPAddress);
|
||||||
joinRoomMessage.setRoomid(client.roomId);
|
joinRoomMessage.setRoomid(client.roomId);
|
||||||
joinRoomMessage.setName(client.name);
|
joinRoomMessage.setName(client.name);
|
||||||
joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position));
|
joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position));
|
||||||
|
joinRoomMessage.setTagList(client.tags);
|
||||||
for (const characterLayer of client.characterLayers) {
|
for (const characterLayer of client.characterLayers) {
|
||||||
const characterLayerMessage = new CharacterLayerMessage();
|
const characterLayerMessage = new CharacterLayerMessage();
|
||||||
characterLayerMessage.setName(characterLayer.name);
|
characterLayerMessage.setName(characterLayer.name);
|
||||||
@ -540,51 +544,54 @@ export class SocketManager implements ZoneEventListener {
|
|||||||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async emitSendUserMessage(userUuid: string, message: string, roomId: string): Promise<void> {
|
public emitSendUserMessage(userUuid: string, message: string, type: string): void {
|
||||||
|
const client = this.searchClientByUuid(userUuid);
|
||||||
|
if(!client){
|
||||||
|
throw Error('client not found');
|
||||||
|
}
|
||||||
|
|
||||||
const backConnection = await apiClientRepository.getClient(roomId);
|
const adminMessage = new SendUserMessage();
|
||||||
|
|
||||||
const adminMessage = new AdminMessage();
|
|
||||||
adminMessage.setRecipientuuid(userUuid);
|
|
||||||
adminMessage.setMessage(message);
|
adminMessage.setMessage(message);
|
||||||
adminMessage.setRoomid(roomId);
|
adminMessage.setType(type);
|
||||||
|
const pusherToBackMessage = new PusherToBackMessage();
|
||||||
|
pusherToBackMessage.setSendusermessage(adminMessage);
|
||||||
|
client.backConnection.write(pusherToBackMessage);
|
||||||
|
|
||||||
|
/*const backConnection = await apiClientRepository.getClient(client.roomId);
|
||||||
|
const adminMessage = new AdminMessage();
|
||||||
|
adminMessage.setMessage(message);
|
||||||
|
adminMessage.setRoomid(client.roomId);
|
||||||
|
adminMessage.setRecipientuuid(client.userUuid);
|
||||||
backConnection.sendAdminMessage(adminMessage, (error) => {
|
backConnection.sendAdminMessage(adminMessage, (error) => {
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
console.error('Error while sending admin message', error);
|
console.error('Error while sending admin message', error);
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
/*
|
|
||||||
const socket = this.searchClientByUuid(messageToSend.userUuid);
|
|
||||||
if(!socket){
|
|
||||||
throw 'socket was not found';
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendUserMessage = new SendUserMessage();
|
|
||||||
sendUserMessage.setMessage(messageToSend.message);
|
|
||||||
sendUserMessage.setType(messageToSend.type);
|
|
||||||
|
|
||||||
const serverToClientMessage = new ServerToClientMessage();
|
|
||||||
serverToClientMessage.setSendusermessage(sendUserMessage);
|
|
||||||
|
|
||||||
if (!socket.disconnecting) {
|
|
||||||
socket.send(serverToClientMessage.serializeBinary().buffer, true);
|
|
||||||
}
|
|
||||||
return socket;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async emitBan(userUuid: string, message: string, roomId: string): Promise<void> {
|
public emitBan(userUuid: string, message: string, type: string): void {
|
||||||
const backConnection = await apiClientRepository.getClient(roomId);
|
const client = this.searchClientByUuid(userUuid);
|
||||||
|
if(!client){
|
||||||
|
throw Error('client not found');
|
||||||
|
}
|
||||||
|
|
||||||
const banMessage = new BanMessage();
|
const banUserMessage = new BanUserMessage();
|
||||||
banMessage.setRecipientuuid(userUuid);
|
banUserMessage.setMessage(message);
|
||||||
banMessage.setRoomid(roomId);
|
banUserMessage.setType(type);
|
||||||
|
const pusherToBackMessage = new PusherToBackMessage();
|
||||||
|
pusherToBackMessage.setBanusermessage(banUserMessage);
|
||||||
|
client.backConnection.write(pusherToBackMessage);
|
||||||
|
|
||||||
backConnection.ban(banMessage, (error) => {
|
/*const backConnection = await apiClientRepository.getClient(client.roomId);
|
||||||
|
const adminMessage = new AdminMessage();
|
||||||
|
adminMessage.setMessage(message);
|
||||||
|
adminMessage.setRoomid(client.roomId);
|
||||||
|
adminMessage.setRecipientuuid(client.userUuid);
|
||||||
|
backConnection.sendAdminMessage(adminMessage, (error) => {
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
console.error('Error while sending ban message', error);
|
console.error('Error while sending admin message', error);
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2723,10 +2723,10 @@ supports-color@^7.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
systeminformation@^4.30.5:
|
systeminformation@^4.31.1:
|
||||||
version "4.30.5"
|
version "4.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.30.5.tgz#2219c305c8be56a2cfa527a5519c45bc81d4916c"
|
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.31.1.tgz#2e02c26987494d4b6a4d2d83138724593bc98d50"
|
||||||
integrity sha512-aYWs8yttl8ePpr6VOQ/Ak8cznuc9L/NQODVhbOKhInX73ZMLvV2BS86Mzr7LLfmUteVFR36CTDNQgiJgRqq+SQ==
|
integrity sha512-dVCDWNMN8ncMZo5vbMCA5dpAdMgzafK2ucuJy5LFmGtp1cG6farnPg8QNvoOSky9SkFoEX1Aw0XhcOFV6TnLYA==
|
||||||
|
|
||||||
table@^5.2.3:
|
table@^5.2.3:
|
||||||
version "5.4.6"
|
version "5.4.6"
|
||||||
|
5
website/.gitignore
vendored
@ -1,5 +0,0 @@
|
|||||||
/node_modules/
|
|
||||||
/dist/bundle.js
|
|
||||||
/dist/main.css
|
|
||||||
/dist/fonts
|
|
||||||
/dist/images
|
|
@ -1,9 +0,0 @@
|
|||||||
# we are rebuilding on each deploy to cope with the GAME_URL environment URL
|
|
||||||
FROM thecodingmachine/nodejs:12-apache
|
|
||||||
|
|
||||||
COPY --chown=docker:docker . .
|
|
||||||
RUN yarn install
|
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
ENV STARTUP_COMMAND_1="yarn run build"
|
|
||||||
ENV APACHE_DOCUMENT_ROOT=dist/
|
|
@ -1,45 +0,0 @@
|
|||||||
Basic Webpack config for simple website.
|
|
||||||
|
|
||||||
Install all packages:
|
|
||||||
```
|
|
||||||
$ npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
Run webpack
|
|
||||||
```
|
|
||||||
$ npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
Done! Open index.html in browser for a cat image.
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
### Notice about production mode and postcss.config.js
|
|
||||||
In *postcss.config.js* there is a check for **process.env.NODE_ENV** variable. The thing is even if you set Webpack mode to production it *won't* automatically change Node environment variable.
|
|
||||||
|
|
||||||
The simplest way to configure this is to install *cross-env* package:
|
|
||||||
```
|
|
||||||
$ npm install --save-dev cross-env
|
|
||||||
```
|
|
||||||
|
|
||||||
Then just add another npm script in *package.json* for production mode:
|
|
||||||
```javascript
|
|
||||||
"scripts": {
|
|
||||||
"build": "webpack --config webpack.config.js",
|
|
||||||
"build-production": "cross-env NODE_ENV=production webpack --config webpack.config.js"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now when you run `npm run build-production` the *process.env.NODE_ENV* variable will be production and postcss.config.js check is going to work:
|
|
||||||
```javascript
|
|
||||||
if(process.env.NODE_ENV === 'production') {
|
|
||||||
module.exports = {
|
|
||||||
plugins: [
|
|
||||||
require('autoprefixer'),
|
|
||||||
require('cssnano')
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
[From Webpack documentation](https://webpack.js.org/guides/production/):
|
|
||||||
Technically, *NODE_ENV* is a system environment variable that Node.js exposes into running scripts. It is used by convention to determine dev-vs-prod behavior by server tools, build scripts, and client-side libraries. Contrary to expectations, *process.env.NODE_ENV* **is not set to "production"** within the build script webpack.config.js. Thus, conditionals like `process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js'` within webpack configurations do not work as expected.
|
|
201
website/dist/choose-map.html
vendored
@ -1,201 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-10196481-11"></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
|
|
||||||
gtag('config', 'UA-10196481-11');
|
|
||||||
</script>
|
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="static/images/favicons/apple-icon-57x57.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="static/images/favicons/apple-icon-60x60.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="static/images/favicons/apple-icon-72x72.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="static/images/favicons/apple-icon-76x76.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="static/images/favicons/apple-icon-114x114.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="static/images/favicons/apple-icon-120x120.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="static/images/favicons/apple-icon-144x144.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="static/images/favicons/apple-icon-152x152.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="static/images/favicons/apple-icon-180x180.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="static/images/favicons/android-icon-192x192.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="static/images/favicons/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="static/images/favicons/favicon-96x96.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="static/images/favicons/favicon-16x16.png">
|
|
||||||
<link rel="manifest" href="static/images/favicons/manifest.json">
|
|
||||||
<meta name="msapplication-TileColor" content="#000000">
|
|
||||||
<meta name="msapplication-TileImage" content="static/images/favicons/ms-icon-144x144.png">
|
|
||||||
<meta name="theme-color" content="#000000">
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Choose map - WorkAdventu.re</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="main.css">
|
|
||||||
<script src="bundle.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="choose-map">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<a href="/" class="d-block mt-3 pixel-text">
|
|
||||||
<< BACK TO HOMEPAGE
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container-fluid container-lg section pt-5">
|
|
||||||
<h1 class="text-center pixel-title">CHOOSE YOUR MAP !</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col text-center">
|
|
||||||
<p>Pick a map that suits your mood! If you don't find what you need, you can always <a href="create-map.html">CREATE YOUR OWN MAP</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row no-gutters justify-content-center">
|
|
||||||
<div class="col-12 col-sm-6 col-md-4">
|
|
||||||
<div class="map-item" data-url="npeguin.github.io/skapa-map/map.json" id="map_1">
|
|
||||||
<img src="static/images/maps/creative.png">
|
|
||||||
<p>Need some inspiration? Enter our CREATIVE SPACE !</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-4">
|
|
||||||
<div class="map-item" data-url="npeguin.github.io/pub-map/map.json" id="map_2">
|
|
||||||
<img src="static/images/maps/pub.png">
|
|
||||||
<p>Too late for working ? Just GO TO THE PUB !</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-4">
|
|
||||||
<div class="map-item" data-url="npeguin.github.io/office-map/map.json" id="map_3">
|
|
||||||
<img src="static/images/maps/office.png">
|
|
||||||
<p>Want to try a SIMPLE OFFICE map ?</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-4">
|
|
||||||
<div class="map-item" data-url="npeguin.github.io/classroom-map/map.json" id="map_4">
|
|
||||||
<img src="static/images/maps/school.png">
|
|
||||||
<p>Send your kids BACK TO SCHOOL... and rest a bit ;)</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-4">
|
|
||||||
<div class="map-item" data-url="npeguin.github.io/tower-map/map.json" id="map_5">
|
|
||||||
<img src="static/images/maps/dungeon.png">
|
|
||||||
<p>Dungeons & Dragons Nostalgia ?</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-4">
|
|
||||||
<div class="map-item" data-url="npeguin.github.io/fantasy-map/map.json" id="map_6">
|
|
||||||
<img src="static/images/maps/fantasy.png">
|
|
||||||
<p>Explore a fantasy world !</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-6 col-md-4">
|
|
||||||
<div class="map-item" data-url="maps.workadventu.re/Floor0/floor0.json" id="map_7">
|
|
||||||
<img src="static/images/maps/tcm.png">
|
|
||||||
<p>Need a bigger Office? Visit us !</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col text-center">
|
|
||||||
<div class="map-item" data-url="npeguin.github.io/corridor-map/map.json" id="map_8">
|
|
||||||
<img src="static/images/maps/street.png">
|
|
||||||
<p>NO IDEA, or just want to ROAM THE STREETS ? Enter the street map and choose your own path !</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col text-center">
|
|
||||||
<p>Could not find what you need? No worries, you can always <a href="create-map.html">CREATE YOUR OWN MAP</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container-fluid container-lg section text-center" id="map-link-container" style="display: none;">
|
|
||||||
<h1 class="mb-3">YOUR MAP URL IS</h1>
|
|
||||||
<p id="wa-link" class="mb-5"></p>
|
|
||||||
<div class="row align-items-center justify-content-center">
|
|
||||||
<div class="col-sm-8 text-right mb-4 pb-sm-0">
|
|
||||||
<button class="copy-btn" onclick="copyToClipboard()">COPY MAP URL<small> TO CLIPBOARD</small></button>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4 text-center text-sm-left">
|
|
||||||
<span id="new-url">to share it !</span>
|
|
||||||
<span id="url-copied"><img src="static/images/check.png">Link copied !</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row start-area justify-content-center mt-5">
|
|
||||||
<div class="col-12 col-sm-1 d-none d-sm-block">
|
|
||||||
<img src="static/images/female-character.gif">
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-4 mb-3 mb-sm-0">
|
|
||||||
<button id="start-btn" onclick="play()">START <span>>></span></button>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-sm-1">
|
|
||||||
<img src="static/images/male-character.gif">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
var rand = '';
|
|
||||||
var characters = 'abcdefghijklmnopqrstuvwxyz';
|
|
||||||
var charactersLength = characters.length;
|
|
||||||
for ( var i = 0; i < 9; i++ ) {
|
|
||||||
rand += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
||||||
}
|
|
||||||
var id = rand.slice(0,3) + '-' + rand.slice(3,6) + '-' + rand.slice(6);
|
|
||||||
var mapLink = document.getElementById('wa-link');
|
|
||||||
var mapLinkContainer = document.getElementById('map-link-container');
|
|
||||||
|
|
||||||
function setSelectedMap(element){
|
|
||||||
var items = document.querySelectorAll(".map-item");
|
|
||||||
[].forEach.call(items, function(el) {
|
|
||||||
el.classList.remove("active");
|
|
||||||
});
|
|
||||||
element.classList.add("active");
|
|
||||||
mapLink.innerText = window.location.protocol + "//play."+window.location.host + "/_/" + id + "/" + element.dataset.url;
|
|
||||||
mapLinkContainer.style.display = 'block';
|
|
||||||
document.getElementById('new-url').style.display = 'inline';
|
|
||||||
document.getElementById('url-copied').style.display = 'none';
|
|
||||||
|
|
||||||
mapLinkContainer.scrollIntoView({
|
|
||||||
block: "start",
|
|
||||||
inline: "nearest",
|
|
||||||
behavior: "smooth"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyToClipboard() {
|
|
||||||
var aux = document.createElement("input");
|
|
||||||
aux.setAttribute("value", mapLink.innerHTML);
|
|
||||||
document.body.appendChild(aux);
|
|
||||||
aux.select();
|
|
||||||
document.execCommand("copy");
|
|
||||||
document.body.removeChild(aux);
|
|
||||||
|
|
||||||
document.getElementById('new-url').style.display = 'none';
|
|
||||||
document.getElementById('url-copied').style.display = 'inline';
|
|
||||||
setTimeout(function(){
|
|
||||||
document.getElementById('new-url').style.display = 'inline';
|
|
||||||
document.getElementById('url-copied').style.display = 'none';
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function play(){
|
|
||||||
window.location.assign(mapLink.innerText);
|
|
||||||
}
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
document.addEventListener('click', function (event) {
|
|
||||||
// If the clicked element doesn't have the right selector, bail
|
|
||||||
var mapItem = event.target.closest('.map-item');
|
|
||||||
if (mapItem === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Don't follow the link
|
|
||||||
event.preventDefault();
|
|
||||||
setSelectedMap(mapItem);
|
|
||||||
}, false);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
240
website/dist/create-map.html
vendored
@ -1,240 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html class="no-js" lang="">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-10196481-11"></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
function gtag(){dataLayer.push(arguments);}
|
|
||||||
gtag('js', new Date());
|
|
||||||
|
|
||||||
gtag('config', 'UA-10196481-11');
|
|
||||||
</script>
|
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="static/images/favicons/apple-icon-57x57.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="static/images/favicons/apple-icon-60x60.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="static/images/favicons/apple-icon-72x72.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="static/images/favicons/apple-icon-76x76.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="static/images/favicons/apple-icon-114x114.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="static/images/favicons/apple-icon-120x120.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="static/images/favicons/apple-icon-144x144.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="static/images/favicons/apple-icon-152x152.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="static/images/favicons/apple-icon-180x180.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="static/images/favicons/android-icon-192x192.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="static/images/favicons/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="static/images/favicons/favicon-96x96.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="static/images/favicons/favicon-16x16.png">
|
|
||||||
<link rel="manifest" href="static/images/favicons/manifest.json">
|
|
||||||
<meta name="msapplication-TileColor" content="#000000">
|
|
||||||
<meta name="msapplication-TileImage" content="static/images/favicons/ms-icon-144x144.png">
|
|
||||||
<meta name="theme-color" content="#000000">
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Choose map - WorkAdventu.re</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="main.css">
|
|
||||||
<script src="bundle.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="create-map">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<a href="/" class="d-block mt-3 pixel-text">
|
|
||||||
<< BACK TO HOMEPAGE
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container-fluid container-lg section pt-5">
|
|
||||||
<h1 class="text-center pixel-title">CREATE YOUR MAP !</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col text-center">
|
|
||||||
<p>Learn how to create your own map! If you want to go the easy route, you can instead <a href="choose-map.html">PICK A PREBUILT MAP</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<h2 id="tools-you-will-need" class="pixel-title">Tools you will need</h2>
|
|
||||||
<p>In order to build your own map for WorkAdventure, you need:</p>
|
|
||||||
<ul>
|
|
||||||
<li>the <a href="https://www.mapeditor.org/">Tiled editor</a> software</li>
|
|
||||||
<li>"tiles" (i.e. images) to create your map (this starter kit provides a good default tileset for offices)</li>
|
|
||||||
<li>a web-server to serve your map (this starter kit proposes to use Github static pages as a web-server which is both free and performant)</li>
|
|
||||||
</ul>
|
|
||||||
<p>WorkAdventure comes with a "map starter kit" that we recommend using to start designing your map quickly.</p>
|
|
||||||
|
|
||||||
<h2 id="getting-started" class="pixel-title">Getting started</h2>
|
|
||||||
<p>Start by <a href="https://github.com/join">creating a GitHub account</a> if you don't already have one.</p>
|
|
||||||
<p>Then, go to the <a href="https://github.com/thecodingmachine/workadventure-map-starter-kit">Github map starter kit repository page</a>
|
|
||||||
and click the <strong>"Use this template"</strong> button.</p>
|
|
||||||
<p class="text-center"><img src="docs/use_this_template.png" alt="" style="width: 70%"></p>
|
|
||||||
<p>You will be prompted to enter a repository name for your map.</p>
|
|
||||||
<p class="text-center"><img src="docs/create_repo.png" alt="" style="width: 70%"></p>
|
|
||||||
<p>Be sure to keep the repository "Public".</p>
|
|
||||||
<p>In your newly created repository, click on the <strong>Settings tab</strong> and scroll down to the <strong>GitHub Pages</strong> section.
|
|
||||||
Then select the <strong>gh-pages</strong> branch. It might already be selected, but please be sure to click on it nonetheless (otherwise
|
|
||||||
GitHub will not enable GitHub pages).</p>
|
|
||||||
<p class="text-center"><img src="docs/github_pages.png" alt="" style="width: 70%"></p>
|
|
||||||
<p>Wait a few minutes... Github will deploy a new website with the content of the repository.
|
|
||||||
The address of the website is visible in the "GitHub Pages" section.</p>
|
|
||||||
<p class="text-center"><img src="docs/website_address.png" alt="" style="width: 70%"></p>
|
|
||||||
<p>Click on the link. You should be redirected directly to WorkAdventure, on your map!</p>
|
|
||||||
|
|
||||||
<h2 id="customizing-your-map" class="pixel-title">Customizing your map</h2>
|
|
||||||
<p>Your map is now up and online, but this is still the demo map from the starter kit. You need to customize it.</p>
|
|
||||||
<h3 id="cloning-the-map" class="pixel-title">Cloning the map</h3>
|
|
||||||
<p>Start by cloning the map. If you are used to Git and GitHub, simply clone the map
|
|
||||||
to your computer using your preferred tool and <a href="#loading-the-map-in-tiled">jump to the next chapter</a>.</p>
|
|
||||||
<p>If you are new to Git, cloning the map means downloading the map to your computer.
|
|
||||||
To do this, you will need Git, or a Git compatible tool. Our advice is to use
|
|
||||||
<a href="https://desktop.github.com/">GitHub Desktop</a>. We recommend you take some time mastering
|
|
||||||
the notion of pull / commit / push as this will make uploading your maps really easier.</p>
|
|
||||||
<p>As an (easier) alternative, you can simply use the "Export" button to download the code of the map in a big Zip file.
|
|
||||||
When you want to upload your work again, you will simply drag'n'drop your files in the GitHub website.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3 id="loading-the-map-in-tiled" class="pixel-title">Loading the map in Tiled</h3>
|
|
||||||
<p>The sample map is in the file <code>map.json</code>.
|
|
||||||
You can load this file in <a href="https://www.mapeditor.org/">Tiled</a>.</p>
|
|
||||||
<p>Now, it's up to you to edit the map and write your own map.</p>
|
|
||||||
<p>Some resources regarding Tiled:</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://doc.mapeditor.org/en/stable/manual/introduction/" target="_blank">Tiled documentation</a></li>
|
|
||||||
<li><a href="https://www.gamefromscratch.com/post/2015/10/14/Tiled-Map-Editor-Tutorial-Series.aspx" target="_blank">Tiled video tutorials</a></li>
|
|
||||||
</ul>
|
|
||||||
<h2 id="about-workadventu-re-maps" class="pixel-title">About WorkAdventure maps</h2>
|
|
||||||
<p>In order to design a map that will be readable by WorkAdventure, you will have to respect some constraints.</p>
|
|
||||||
<p>In particular, you will need to:</p>
|
|
||||||
<ul>
|
|
||||||
<li>set a start position for the players</li>
|
|
||||||
<li>configure the "floor layer" (so that WorkAdventure can correctly display characters above the floor, but under the ceiling)</li>
|
|
||||||
<li>eventually, you can place exits that link to other maps</li>
|
|
||||||
<li>the "Tile Layer Format" must be set to CSV. If you started from the map starter kit as explained above,
|
|
||||||
you have nothing to do. However, if you start from scratch, please be sure to select "CSV" for the tile layer format
|
|
||||||
when creating the map. You can change this setting later in the map properties.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3 id="workadventure-maps-rules" class="pixel-title">WorkAdventure Map Rules</h3>
|
|
||||||
<p>A few things to notice:</p>
|
|
||||||
<ul>
|
|
||||||
<li>your map can have as many layers as you want</li>
|
|
||||||
<li>your map MUST contain a layer named "floorLayer" of type "objectgroup" that represents the layer on which characters will be drawn.
|
|
||||||
Every layer above the "floorLayer" will be displayed on top of the characters.</li>
|
|
||||||
<li>the tilesets in your map MUST be embedded. You cannot refer to an external typeset in a TSX file. Click the "embed tileset" button in the tileset tab to embed tileset data.</li>
|
|
||||||
<li>your map MUST be exported in JSON format. You need to use a recent version of Tiled to get JSON format export (1.3+)</li>
|
|
||||||
<li>WorkAdventure doesn't support object layers and will ignore them</li>
|
|
||||||
<li>If you are starting from a blank map, your map MUST be orthogonal and tiles size should be 32x32. </li>
|
|
||||||
</ul>
|
|
||||||
<p class="text-center"><img src="docs/tiled_screenshot_1.png" alt="" style="width: 70%"></p>
|
|
||||||
|
|
||||||
<h3 id="building-walls" class="pixel-title">Building walls and "collidable" areas</h3>
|
|
||||||
<p>By default, the characters can traverse any tiles. If you want to prevent your characeter from
|
|
||||||
going through a tile (like a wall or a desktop), you must make this tile "collidable". You can do
|
|
||||||
this by settings the <code>collides</code> property on a given tile.</p>
|
|
||||||
|
|
||||||
<p>To make a tile "collidable", you should:</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>select the relevant tileset and switch to "edit" mode:<br/>
|
|
||||||
<img src="docs/collides-1.png" alt="">
|
|
||||||
</li>
|
|
||||||
<li>right click on a tile of the tileset to select it:<br/>
|
|
||||||
<img src="docs/collides-2.png" alt="">
|
|
||||||
</li>
|
|
||||||
<li>on the left pane in the custom properties section, right click and select "Add properties":<br/>
|
|
||||||
<img src="docs/collides-3.png" alt=""><br/>
|
|
||||||
Please add a <code>collides</code> property. The type of the property must be <strong>bool</strong>.
|
|
||||||
</li>
|
|
||||||
<li>finally, check the checkbox for the <code>collides</code> property:<br/>
|
|
||||||
<img src="docs/collides-4.png" alt="">
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<p>Repeat for every tile that should be "collidable".</p>
|
|
||||||
|
|
||||||
<h3 id="defining-a-default-entry-point" class="pixel-title">Defining a default entry point</h3>
|
|
||||||
<p>In order to define a default start position, you MUST create a layer named "start" on your map.
|
|
||||||
This layer MUST contain at least one tile. The players will start on the tile of this layer.
|
|
||||||
If the layer contains many tiles selected, the players will start randomly on one of those tiles.</p>
|
|
||||||
|
|
||||||
<p class="text-center"><img src="docs/start_layer.png" alt="Start layer screenshot" style="width: 70%"></p>
|
|
||||||
|
|
||||||
<p>In the screenshot above, the start layer is made of the 2 white tiles. These tiles are
|
|
||||||
not visible to the end user because they are hidden below the "bottom" layer that displays the floor
|
|
||||||
of the map.</p>
|
|
||||||
|
|
||||||
<h3 id="defining-exits" class="pixel-title">Defining exits</h3>
|
|
||||||
<p>In order to place an exit on your scene that leads to another scene:</p>
|
|
||||||
<ul>
|
|
||||||
<li>You must create a specific layer. When a character reaches ANY tile of that layer, it will exit the scene.</li>
|
|
||||||
<li>In layer properties, you MUST add "exitUrl" property. It represents the URL of the next scene. You can put relative or absolute URLs.</li>
|
|
||||||
<li>If you want to have multiple exits, you can create many layers with name "exit". Each layer has a different key <code>exitUrl</code> and have tiles that represent exits to another scene.</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
<strong>Understanding map URLs in WorkAdventure:</strong><br/>
|
|
||||||
There are 2 kinds of URLs in WorkAdventure:
|
|
||||||
<ul>
|
|
||||||
<li>Public URLs are in the form https://play.workadventu.re/_/[instance]/[server]/[path to map]</li>
|
|
||||||
<li>Private URLs (used in paid accounts) are in the form https://play.workadventu.re/@/[organization]/[world]/[map]</li>
|
|
||||||
</ul>
|
|
||||||
Assuming your JSON map is hosted at "https://example.com/my/map.json", then you can browse your map at "https://play.workadventu.re/_/global/example.com/my/map.json".
|
|
||||||
Here, "global" is a name of an "instance" of your map. You can put anything instead of "global" here. People on the same instance of the map can see each others.
|
|
||||||
If 2 users use 2 different instances, they are on the same map, but in 2 parallel universes. They cannot see each other.
|
|
||||||
</p>
|
|
||||||
<p class="text-center"><img src="docs/exit_layer_map.png" alt="" style="width: 90%"></p>
|
|
||||||
<p>Note: in older releases of WorkAdventure, you could link to a map file directly using properties "exitSceneUrl" and "exitInstance". Those properties are now deprecated. Use "exitUrl" instead.</p>
|
|
||||||
<h3 id="defining-several-entry-points" class="pixel-title">Defining several entry points</h3>
|
|
||||||
<p>Often your map will have several exits, and therefore, several entry points. For instance, if there
|
|
||||||
is an exit by a door that leads to the garden map, when you come back from the garden you expect to
|
|
||||||
come back by the same door. Therefore, a map can have several entry points.
|
|
||||||
Those entry points are "named" (they have a name).</p>
|
|
||||||
<p>In order to create a named entry point:</p>
|
|
||||||
<ul>
|
|
||||||
<li>You must create a specific layer. When a character enters the map by this entry point, it will enter the map randomly on ANY tile of that layer.</li>
|
|
||||||
<li>In layer properties, you MUST add a boolean "startLayer" property. It should be set to true.</li>
|
|
||||||
<li>The name of the entry point is the name of the layer</li>
|
|
||||||
<li>To enter via this entry point, simply add a hash with the entry point name to the URL ("#[<em>startLayerName</em>]"). For instance: "https://workadventu.re/_/global/mymap.com/path/map.json#my-entry-point".</li>
|
|
||||||
<li>You can of course use the "#" notation in an exit scene URL (so an exit scene URL will point to a given entry scene URL)</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3 id="opening-a-website-when-walking-on-the-map" class="pixel-title">Opening a website when walking on the map</h3>
|
|
||||||
<p>On your map, you can define special zones. When a player will pass over these zones, a website will open
|
|
||||||
(as an iframe on the right side of the screen)</p>
|
|
||||||
<p>In order to create a zone that opens websites:</p>
|
|
||||||
<ul>
|
|
||||||
<li>You must create a specific layer.</li>
|
|
||||||
<li>In layer properties, you MUST add a "openWebsite" property (of type "string"). The value of the property is the URL of the website to open (the URL must start with "https://")</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3 id="opening-a-jitsi-meet-when-walking-on-the-map" class="pixel-title">Opening a Jitsi meet when walking on the map</h3>
|
|
||||||
<p>On your map, you can define special zones (meeting rooms) that will trigger the opening of a Jitsi meet. When a player will pass over these zones, a Jitsi meet will open
|
|
||||||
(as an iframe on the right side of the screen)</p>
|
|
||||||
<p>In order to create Jitsi meet zones:</p>
|
|
||||||
<ul>
|
|
||||||
<li>You must create a specific layer.</li>
|
|
||||||
<li>In layer properties, you MUST add a "jitsiRoom" property (of type "string"). The value of the property is the name of the room in Jitsi. Note: the name of the room will be prepended with the name of the instance of the map (so that different instances of the map have different rooms)</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3 id="making-a-silent-zone" class="pixel-title">Making a "silent" zone</h3>
|
|
||||||
<p>On your map, you can define special silent zones where nobody is allowed to talk.
|
|
||||||
In these zones, users will not speak to each others, even if they are next to each others.</p>
|
|
||||||
<p>In order to create a silent zone:</p>
|
|
||||||
<ul>
|
|
||||||
<li>You must create a specific layer.</li>
|
|
||||||
<li>In layer properties, you MUST add a boolean "silent" property. If the silent property is checked, the users are entering the silent zone when they walk on any tile of the layer.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3 id="pushing-the-map" class="pixel-title">Pushing the map</h3>
|
|
||||||
<p>When your changes are ready, you need to "commit" and "push" (i.e. "upload") the changes back to GitHub.
|
|
||||||
Just wait a few minutes, and your map will be propagated automatically to the GitHub pages web-server.</p>
|
|
||||||
|
|
||||||
<h3 id="need-some-help" class="pixel-title">Need some help?</h3>
|
|
||||||
<p>WorkAdventure is a young project and much needs to be said / written regarding map editing.</p>
|
|
||||||
<p>If you are facing any troubles, do not hesitate to open an "issue" in the
|
|
||||||
<a href="https://github.com/thecodingmachine/workadventure/issues" target="_blank">GitHub WorkAdventure account</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
BIN
website/dist/docs/collides-1.png
vendored
Before Width: | Height: | Size: 290 KiB |
BIN
website/dist/docs/collides-2.png
vendored
Before Width: | Height: | Size: 144 KiB |
BIN
website/dist/docs/collides-3.png
vendored
Before Width: | Height: | Size: 129 KiB |
BIN
website/dist/docs/collides-4.png
vendored
Before Width: | Height: | Size: 11 KiB |
BIN
website/dist/docs/create_repo.png
vendored
Before Width: | Height: | Size: 158 KiB |
BIN
website/dist/docs/exit_layer_map.png
vendored
Before Width: | Height: | Size: 401 KiB |
BIN
website/dist/docs/github_pages.png
vendored
Before Width: | Height: | Size: 73 KiB |
BIN
website/dist/docs/start_layer.png
vendored
Before Width: | Height: | Size: 106 KiB |
BIN
website/dist/docs/tiled_screenshot_1.png
vendored
Before Width: | Height: | Size: 26 KiB |
BIN
website/dist/docs/use_this_template.png
vendored
Before Width: | Height: | Size: 32 KiB |
BIN
website/dist/docs/website_address.png
vendored
Before Width: | Height: | Size: 44 KiB |