From f993aa4f5a9ce5420d258389e29eda741b33167a Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 17 Feb 2022 11:29:09 +0100 Subject: [PATCH 01/61] Wrap websockets with HyperExpress --- pusher/package.json | 6 +- pusher/src/App.ts | 36 +- pusher/src/Controller/AdminController.ts | 72 +- .../src/Controller/AuthenticateController.ts | 166 ++-- pusher/src/Controller/BaseController.ts | 45 - pusher/src/Controller/BaseHttpController.ts | 45 + pusher/src/Controller/DebugController.ts | 59 +- pusher/src/Controller/IoSocketController.ts | 6 +- pusher/src/Controller/MapController.ts | 84 +- .../src/Controller/OpenIdProfileController.ts | 33 +- pusher/src/Controller/PrometheusController.ts | 21 +- pusher/src/Middleware/AdminToken.ts | 22 + pusher/src/Middleware/Cors.ts | 14 + .../Model/Websocket/ExAdminSocketInterface.ts | 4 +- .../src/Model/Websocket/ExSocketInterface.ts | 3 +- pusher/src/Server/server/app.ts | 13 - pusher/src/Server/server/baseapp.ts | 109 --- pusher/src/Server/server/formdata.ts | 99 --- pusher/src/Server/server/sslapp.ts | 13 - pusher/src/Server/server/types.ts | 11 - pusher/src/Server/server/utils.ts | 36 - pusher/src/Server/sifrr.server.ts | 19 - pusher/src/Services/AdminApi.ts | 1 - pusher/src/Services/SocketManager.ts | 7 +- pusher/yarn.lock | 827 ++++++++++-------- 25 files changed, 733 insertions(+), 1018 deletions(-) delete mode 100644 pusher/src/Controller/BaseController.ts create mode 100644 pusher/src/Controller/BaseHttpController.ts create mode 100644 pusher/src/Middleware/AdminToken.ts create mode 100644 pusher/src/Middleware/Cors.ts delete mode 100644 pusher/src/Server/server/app.ts delete mode 100644 pusher/src/Server/server/baseapp.ts delete mode 100644 pusher/src/Server/server/formdata.ts delete mode 100644 pusher/src/Server/server/sslapp.ts delete mode 100644 pusher/src/Server/server/types.ts delete mode 100644 pusher/src/Server/server/utils.ts delete mode 100644 pusher/src/Server/sifrr.server.ts diff --git a/pusher/package.json b/pusher/package.json index bbef80fa..8c7d3016 100644 --- a/pusher/package.json +++ b/pusher/package.json @@ -41,22 +41,20 @@ "homepage": "https://github.com/thecodingmachine/workadventure#readme", "dependencies": { "axios": "^0.21.2", - "busboy": "^0.3.1", "circular-json": "^0.5.9", "debug": "^4.3.1", "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "grpc": "^1.24.4", + "hyper-express": "^5.8.1", "jsonwebtoken": "^8.5.1", "mkdirp": "^1.0.4", "openid-client": "^4.7.4", "prom-client": "^12.0.0", "query-string": "^6.13.3", - "uWebSockets.js": "uNetworking/uWebSockets.js#v20.4.0", "uuidv4": "^6.0.7" }, "devDependencies": { - "@types/busboy": "^0.2.3", "@types/circular-json": "^0.4.0", "@types/debug": "^4.1.5", "@types/google-protobuf": "^3.7.3", @@ -79,4 +77,4 @@ "prettier --write" ] } -} +} \ No newline at end of file diff --git a/pusher/src/App.ts b/pusher/src/App.ts index 327d493c..401e04f3 100644 --- a/pusher/src/App.ts +++ b/pusher/src/App.ts @@ -4,31 +4,31 @@ import { AuthenticateController } from "./Controller/AuthenticateController"; // import { MapController } from "./Controller/MapController"; import { PrometheusController } from "./Controller/PrometheusController"; import { DebugController } from "./Controller/DebugController"; -import { App as uwsApp } from "./Server/sifrr.server"; import { AdminController } from "./Controller/AdminController"; import { OpenIdProfileController } from "./Controller/OpenIdProfileController"; +import HyperExpress from "hyper-express"; +import { cors } from "./Middleware/Cors"; class App { - public app: uwsApp; - public ioSocketController: IoSocketController; - public authenticateController: AuthenticateController; - public mapController: MapController; - public prometheusController: PrometheusController; - private debugController: DebugController; - private adminController: AdminController; - private openIdProfileController: OpenIdProfileController; + public app: HyperExpress.compressors.TemplatedApp; constructor() { - this.app = new uwsApp(); + const webserver = new HyperExpress.Server(); + this.app = webserver.uws_instance; - //create socket controllers - this.ioSocketController = new IoSocketController(this.app); - this.authenticateController = new AuthenticateController(this.app); - this.mapController = new MapController(this.app); - this.prometheusController = new PrometheusController(this.app); - this.debugController = new DebugController(this.app); - this.adminController = new AdminController(this.app); - this.openIdProfileController = new OpenIdProfileController(this.app); + // Global middlewares + webserver.use(cors); + + // Socket controllers + new IoSocketController(this.app); + + // Http controllers + new AuthenticateController(webserver); + new MapController(webserver); + new PrometheusController(webserver); + new DebugController(webserver); + new AdminController(webserver); + new OpenIdProfileController(webserver); } } diff --git a/pusher/src/Controller/AdminController.ts b/pusher/src/Controller/AdminController.ts index a8e3b593..0512ded3 100644 --- a/pusher/src/Controller/AdminController.ts +++ b/pusher/src/Controller/AdminController.ts @@ -1,45 +1,22 @@ -import { BaseController } from "./BaseController"; -import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; -import { ADMIN_API_TOKEN } from "../Enum/EnvironmentVariable"; import { apiClientRepository } from "../Services/ApiClientRepository"; import { AdminRoomMessage, WorldFullWarningToRoomMessage, RefreshRoomPromptMessage, } from "../Messages/generated/messages_pb"; +import { adminToken } from "../Middleware/AdminToken"; +import { BaseHttpController } from "./BaseHttpController"; -export class AdminController extends BaseController { - constructor(private App: TemplatedApp) { - super(); - this.App = App; +export class AdminController extends BaseHttpController { + routes() { this.receiveGlobalMessagePrompt(); this.receiveRoomEditionPrompt(); } receiveRoomEditionPrompt() { - this.App.options("/room/refresh", (res: HttpResponse, req: HttpRequest) => { - this.addCorsHeaders(res); - res.end(); - }); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.App.post("/room/refresh", async (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/message request was aborted"); - }); - - const token = req.getHeader("admin-token"); - const body = await res.json(); - - if (ADMIN_API_TOKEN === "") { - res.writeStatus("401 Unauthorized").end("No token configured!"); - return; - } - if (token !== ADMIN_API_TOKEN) { - console.error("Admin access refused for token: " + token); - res.writeStatus("401 Unauthorized").end("Incorrect token"); - return; - } + this.app.post("/room/refresh", { middlewares: [adminToken] }, async (req, res) => { + const body = await req.json(); try { if (typeof body.roomId !== "string") { @@ -58,41 +35,18 @@ export class AdminController extends BaseController { }); }); } catch (err) { - this.errorToResponse(err, res); + this.castErrorToResponse(err, res); return; } - res.writeStatus("200"); - res.end("ok"); + res.send("ok"); }); } receiveGlobalMessagePrompt() { - this.App.options("/message", (res: HttpResponse, req: HttpRequest) => { - this.addCorsHeaders(res); - res.end(); - }); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.App.post("/message", async (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/message request was aborted"); - }); - - const token = req.getHeader("admin-token"); - const body = await res.json(); - - if (ADMIN_API_TOKEN === "") { - res.writeStatus("401 Unauthorized").end("No token configured!"); - res.end(); - return; - } - if (token !== ADMIN_API_TOKEN) { - console.error("Admin access refused for token: " + token); - res.writeStatus("401 Unauthorized").end("Incorrect token"); - res.end(); - return; - } + this.app.post("/message", { middlewares: [adminToken] }, async (req, res) => { + const body = await req.json(); try { if (typeof body.text !== "string") { @@ -133,13 +87,11 @@ export class AdminController extends BaseController { }) ); } catch (err) { - this.errorToResponse(err, res); + this.castErrorToResponse(err, res); return; } - res.writeStatus("200"); - this.addCorsHeaders(res); - res.end("ok"); + res.send("ok"); }); } } diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 89d3adf3..9a081461 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -1,20 +1,18 @@ import { v4 } from "uuid"; -import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; -import { BaseController } from "./BaseController"; +import { BaseHttpController } from "./BaseHttpController"; import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; -import { DISABLE_ANONYMOUS, FRONT_URL } from "../Enum/EnvironmentVariable"; +import { DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; import { RegisterData } from "../Messages/JsonMessages/RegisterData"; export interface TokenInterface { userUuid: string; } -export class AuthenticateController extends BaseController { - constructor(private App: TemplatedApp) { - super(); +export class AuthenticateController extends BaseHttpController { + routes() { this.openIDLogin(); this.openIDCallback(); this.register(); @@ -24,13 +22,9 @@ export class AuthenticateController extends BaseController { openIDLogin() { //eslint-disable-next-line @typescript-eslint/no-misused-promises - this.App.get("/login-screen", async (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/message request was aborted"); - }); - + this.app.get("/login-screen", async (req, res) => { try { - const { nonce, state, playUri, redirect } = parse(req.getQuery()); + const { nonce, state, playUri, redirect } = parse(req.path_query); if (!state || !nonce) { throw new Error("missing state and nonce URL parameters"); } @@ -41,24 +35,22 @@ export class AuthenticateController extends BaseController { playUri as string | undefined, redirect as string | undefined ); - res.writeStatus("302"); - res.writeHeader("Location", loginUri); - return res.end(); + res.status(302); + res.setHeader("Location", loginUri); + return res; } catch (e) { console.error("openIDLogin => e", e); - return this.errorToResponse(e, res); + this.castErrorToResponse(e, res); + return; } }); } openIDCallback() { //eslint-disable-next-line @typescript-eslint/no-misused-promises - this.App.get("/login-callback", async (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/message request was aborted"); - }); - const IPAddress = req.getHeader("x-forwarded-for"); - const { code, nonce, token, playUri } = parse(req.getQuery()); + this.app.get("/login-callback", async (req, res) => { + const IPAddress = req.header("x-forwarded-for"); + const { code, nonce, token, playUri } = parse(req.path_query); try { //verify connected by token if (token != undefined) { @@ -77,23 +69,16 @@ export class AuthenticateController extends BaseController { //if not nonce and code, user connected in anonymous //get data with identifier and return token if (!code && !nonce) { - res.writeStatus("200"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - return res.end(JSON.stringify({ ...resUserData, authToken: token })); + return res.json(JSON.stringify({ ...resUserData, authToken: token })); } console.error("Token cannot to be check on OpenId provider"); - res.writeStatus("500"); - res.writeHeader("Access-Control-Allow-Origin", FRONT_URL); - res.end("User cannot to be connected on openid provider"); + res.status(500); + res.send("User cannot to be connected on openid provider"); return; } const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); - res.writeStatus("200"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - return res.end(JSON.stringify({ ...resCheckTokenAuth, ...resUserData, authToken: token })); + return res.json({ ...resCheckTokenAuth, ...resUserData, authToken: token }); } catch (err) { console.info("User was not connected", err); } @@ -106,9 +91,8 @@ export class AuthenticateController extends BaseController { } catch (err) { //if no access on openid provider, return error console.error("User cannot to be connected on OpenId provider => ", err); - res.writeStatus("500"); - res.writeHeader("Access-Control-Allow-Origin", FRONT_URL); - res.end("User cannot to be connected on openid provider"); + res.status(500); + res.send("User cannot to be connected on openid provider"); return; } const email = userInfo.email || userInfo.sub; @@ -121,23 +105,16 @@ export class AuthenticateController extends BaseController { //This is very important to create User Local in LocalStorage in WorkAdventure const data = await this.getUserByUserIdentifier(email, playUri as string, IPAddress); - res.writeStatus("200"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - return res.end(JSON.stringify({ ...data, authToken })); + return res.json({ ...data, authToken }); } catch (e) { console.error("openIDCallback => ERROR", e); - return this.errorToResponse(e, res); + return this.castErrorToResponse(e, res); } }); // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.App.get("/logout-callback", async (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/message request was aborted"); - }); - - const { token } = parse(req.getQuery()); + this.app.get("/logout-callback", async (req, res) => { + const { token } = parse(req.path_query); try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); @@ -147,29 +124,17 @@ export class AuthenticateController extends BaseController { await openIDClient.logoutUser(authTokenData.accessToken); } catch (error) { console.error("openIDCallback => logout-callback", error); - } finally { - res.writeStatus("200"); - this.addCorsHeaders(res); - // eslint-disable-next-line no-unsafe-finally - return res.end(); } + + return res; }); } //Try to login with an admin token private register() { - this.App.options("/register", (res: HttpResponse, req: HttpRequest) => { - this.addCorsHeaders(res); - - res.end(); - }); - - this.App.post("/register", (res: HttpResponse, req: HttpRequest) => { + this.app.post("/register", (req, res) => { (async () => { - res.onAborted(() => { - console.warn("Login request was aborted"); - }); - const param = await res.json(); + const param = await req.json(); //todo: what to do if the organizationMemberToken is already used? const organizationMemberToken: string | null = param.organizationMemberToken; @@ -184,23 +149,18 @@ export class AuthenticateController extends BaseController { const textures = data.textures; const authToken = jwtTokenManager.createAuthToken(email || userUuid); - res.writeStatus("200 OK"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - res.end( - JSON.stringify({ - authToken, - userUuid, - email, - roomUrl, - mapUrlStart, - organizationMemberToken, - textures, - } as RegisterData) - ); + res.json({ + authToken, + userUuid, + email, + roomUrl, + mapUrlStart, + organizationMemberToken, + textures, + } as RegisterData); } catch (e) { console.error("register => ERROR", e); - this.errorToResponse(e, res); + this.castErrorToResponse(e, res); } })(); }); @@ -208,44 +168,25 @@ export class AuthenticateController extends BaseController { //permit to login on application. Return token to connect on Websocket IO. private anonymLogin() { - this.App.options("/anonymLogin", (res: HttpResponse, req: HttpRequest) => { - this.addCorsHeaders(res); - res.end(); - }); - - this.App.post("/anonymLogin", (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("Login request was aborted"); - }); - + this.app.post("/anonymLogin", (req, res) => { if (DISABLE_ANONYMOUS) { - res.writeStatus("403 FORBIDDEN"); - res.end(); + res.status(403); + return res; } else { const userUuid = v4(); const authToken = jwtTokenManager.createAuthToken(userUuid); - res.writeStatus("200 OK"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - res.end( - JSON.stringify({ - authToken, - userUuid, - }) - ); + return res.json({ + authToken, + userUuid, + }); } }); } profileCallback() { - //eslint-disable-next-line @typescript-eslint/no-misused-promises - // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.App.get("/profile-callback", async (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/message request was aborted"); - }); - const { token } = parse(req.getQuery()); + this.app.get("/profile-callback", async (req, res) => { + const { token } = parse(req.path_query); try { //verify connected by token if (token != undefined) { @@ -257,18 +198,17 @@ export class AuthenticateController extends BaseController { await openIDClient.checkTokenAuth(authTokenData.accessToken); //get login profile - res.writeStatus("302"); - res.writeHeader("Location", adminApi.getProfileUrl(authTokenData.accessToken)); - this.addCorsHeaders(res); - // eslint-disable-next-line no-unsafe-finally - return res.end(); + res.status(302); + res.setHeader("Location", adminApi.getProfileUrl(authTokenData.accessToken)); + return; } catch (error) { - return this.errorToResponse(error, res); + this.castErrorToResponse(error, res); + return; } } } catch (error) { console.error("profileCallback => ERROR", error); - this.errorToResponse(error, res); + this.castErrorToResponse(error, res); } }); } diff --git a/pusher/src/Controller/BaseController.ts b/pusher/src/Controller/BaseController.ts deleted file mode 100644 index f5465e1b..00000000 --- a/pusher/src/Controller/BaseController.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { HttpResponse } from "uWebSockets.js"; -import { FRONT_URL } from "../Enum/EnvironmentVariable"; - -export class BaseController { - protected addCorsHeaders(res: HttpResponse): void { - res.writeHeader("access-control-allow-headers", "Origin, X-Requested-With, Content-Type, Accept"); - res.writeHeader("access-control-allow-methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); - res.writeHeader("access-control-allow-origin", FRONT_URL); - } - - /** - * 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 { - if (e && e.message) { - let url = e?.config?.url; - if (url !== undefined) { - url = " for URL: " + url; - } else { - url = ""; - } - console.error("ERROR: " + e.message + url); - } else if (typeof e === "string") { - console.error(e); - } - if (e.stack) { - console.error(e.stack); - } - if (e.response) { - res.writeStatus(e.response.status + " " + e.response.statusText); - this.addCorsHeaders(res); - res.end( - "An error occurred: " + - e.response.status + - " " + - (e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText) - ); - } else { - res.writeStatus("500 Internal Server Error"); - this.addCorsHeaders(res); - res.end("An error occurred"); - } - } -} diff --git a/pusher/src/Controller/BaseHttpController.ts b/pusher/src/Controller/BaseHttpController.ts new file mode 100644 index 00000000..454c92db --- /dev/null +++ b/pusher/src/Controller/BaseHttpController.ts @@ -0,0 +1,45 @@ +import { Server } from "hyper-express"; +import Response from "hyper-express/types/components/http/Response"; +import axios from "axios"; + +export class BaseHttpController { + constructor(protected app: Server) { + this.routes(); + } + + protected routes() { + /* Define routes on children */ + } + + protected castErrorToResponse(e: unknown, res: Response): void { + if (e instanceof Error) { + let url: string | undefined; + if (axios.isAxiosError(e)) { + url = e.config.url; + if (url !== undefined) { + url = " for URL: " + url; + } else { + url = ""; + } + } + + console.error("ERROR: " + e.message + url); + console.error(e.stack); + } else if (typeof e === "string") { + console.error(e); + } + + if (axios.isAxiosError(e) && e.response) { + res.status(e.response.status); + res.send( + "An error occurred: " + + e.response.status + + " " + + (e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText) + ); + } else { + res.status(500); + res.send("An error occurred"); + } + } +} diff --git a/pusher/src/Controller/DebugController.ts b/pusher/src/Controller/DebugController.ts index 26b229b6..c8bc6430 100644 --- a/pusher/src/Controller/DebugController.ts +++ b/pusher/src/Controller/DebugController.ts @@ -1,51 +1,42 @@ import { ADMIN_API_TOKEN } from "../Enum/EnvironmentVariable"; -import { IoSocketController } from "_Controller/IoSocketController"; import { stringify } from "circular-json"; -import { HttpRequest, HttpResponse } from "uWebSockets.js"; import { parse } from "query-string"; -import { App } from "../Server/sifrr.server"; import { socketManager } from "../Services/SocketManager"; +import { BaseHttpController } from "./BaseHttpController"; -export class DebugController { - constructor(private App: App) { - this.getDump(); - } - - getDump() { - this.App.get("/dump", (res: HttpResponse, req: HttpRequest) => { - const query = parse(req.getQuery()); +export class DebugController extends BaseHttpController { + routes() { + this.app.get("/dump", (req, res) => { + const query = parse(req.path_query); if (ADMIN_API_TOKEN === "") { - return res.writeStatus("401 Unauthorized").end("No token configured!"); + return res.status(401).send("No token configured!"); } if (query.token !== ADMIN_API_TOKEN) { - return res.writeStatus("401 Unauthorized").end("Invalid token sent!"); + return res.status(401).send("Invalid token sent!"); } const worlds = Object.fromEntries(socketManager.getWorlds().entries()); - return res - .writeStatus("200 OK") - .writeHeader("Content-Type", "application/json") - .end( - stringify(worlds, (key: unknown, value: unknown) => { - if (value instanceof Map) { - const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - for (const [mapKey, mapValue] of value.entries()) { - obj[mapKey] = mapValue; - } - return obj; - } else if (value instanceof Set) { - const obj: Array = []; - for (const [setKey, setValue] of value.entries()) { - obj.push(setValue); - } - return obj; - } else { - return value; + return res.json( + stringify(worlds, (key: unknown, value: unknown) => { + if (value instanceof Map) { + const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any + for (const [mapKey, mapValue] of value.entries()) { + obj[mapKey] = mapValue; } - }) - ); + return obj; + } else if (value instanceof Set) { + const obj: Array = []; + for (const [setKey, setValue] of value.entries()) { + obj.push(setValue); + } + return obj; + } else { + return value; + } + }) + ); }); } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 6db53403..eb8f4e75 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -1,5 +1,5 @@ import { CharacterLayer, ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." -import { GameRoomPolicyTypes, PusherRoom } from "../Model/PusherRoom"; +import { GameRoomPolicyTypes } from "../Model/PusherRoom"; import { PointInterface } from "../Model/Websocket/PointInterface"; import { SetPlayerDetailsMessage, @@ -23,7 +23,6 @@ import { VariableMessage, } from "../Messages/generated/messages_pb"; import { UserMovesMessage } from "../Messages/generated/messages_pb"; -import { TemplatedApp } from "uWebSockets.js"; import { parse } from "query-string"; import { AdminSocketTokenData, jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenManager"; import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; @@ -36,11 +35,12 @@ import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; import { isAdminMessageInterface } from "../Model/Websocket/Admin/AdminMessages"; import Axios from "axios"; import { InvalidTokenError } from "../Controller/InvalidTokenError"; +import HyperExpress from "hyper-express"; export class IoSocketController { private nextUserId: number = 1; - constructor(private readonly app: TemplatedApp) { + constructor(private readonly app: HyperExpress.compressors.TemplatedApp) { this.ioConnection(); if (ADMIN_SOCKETS_TOKEN) { this.adminRoomSocket(); diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index eae205f9..cf86c1b1 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -1,41 +1,21 @@ -import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; -import { BaseController } from "./BaseController"; -import { parse } from "query-string"; import { adminApi } from "../Services/AdminApi"; -import { ADMIN_API_URL, DISABLE_ANONYMOUS, FRONT_URL } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_URL, DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; import { GameRoomPolicyTypes } from "../Model/PusherRoom"; import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; -import { socketManager } from "../Services/SocketManager"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; -import { v4 } from "uuid"; import { InvalidTokenError } from "./InvalidTokenError"; +import { parse } from "query-string"; +import { BaseHttpController } from "./BaseHttpController"; -export class MapController extends BaseController { - constructor(private App: TemplatedApp) { - super(); - this.App = App; - this.getMapUrl(); - } - +export class MapController extends BaseHttpController { // Returns a map mapping map name to file name of the map - getMapUrl() { - this.App.options("/map", (res: HttpResponse, req: HttpRequest) => { - this.addCorsHeaders(res); - res.end(); - }); - - this.App.get("/map", (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/map request was aborted"); - }); - - const query = parse(req.getQuery()); - + routes() { + this.app.get("/map", (req, res) => { + const query = parse(req.path_query); if (typeof query.playUri !== "string") { console.error("Expected playUri parameter in /map endpoint"); - res.writeStatus("400 Bad request"); - this.addCorsHeaders(res); - res.end("Expected playUri parameter"); + res.status(400); + res.send("Expected playUri parameter"); return; } @@ -45,30 +25,23 @@ export class MapController extends BaseController { const match = /\/_\/[^/]+\/(.+)/.exec(roomUrl.pathname); if (!match) { - res.writeStatus("404 Not Found"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - res.end(JSON.stringify({})); + res.status(404); + res.json({}); return; } const mapUrl = roomUrl.protocol + "//" + match[1]; - res.writeStatus("200 OK"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - res.end( - JSON.stringify({ - mapUrl, - policy_type: GameRoomPolicyTypes.ANONYMOUS_POLICY, - roomSlug: null, // Deprecated - group: null, - tags: [], - textures: [], - contactPage: null, - authenticationMandatory: DISABLE_ANONYMOUS, - } as MapDetailsData) - ); + res.json({ + mapUrl, + policy_type: GameRoomPolicyTypes.ANONYMOUS_POLICY, + roomSlug: null, // Deprecated + group: null, + tags: [], + textures: [], + contactPage: null, + authenticationMandatory: DISABLE_ANONYMOUS, + } as MapDetailsData); return; } @@ -90,12 +63,12 @@ export class MapController extends BaseController { } catch (e) { if (e instanceof InvalidTokenError) { // The token was not good, redirect user on login page - res.writeStatus("401 Unauthorized"); - res.writeHeader("Access-Control-Allow-Origin", FRONT_URL); - res.end("Token decrypted error"); + res.status(401); + res.send("Token decrypted error"); return; } else { - return this.errorToResponse(e, res); + this.castErrorToResponse(e, res); + return; } } } @@ -106,12 +79,9 @@ export class MapController extends BaseController { mapDetails.authenticationMandatory = true; } - res.writeStatus("200 OK"); - this.addCorsHeaders(res); - res.writeHeader("Content-Type", "application/json"); - res.end(JSON.stringify(mapDetails)); + res.json(mapDetails); } catch (e) { - this.errorToResponse(e, res); + this.castErrorToResponse(e, res); } })(); }); diff --git a/pusher/src/Controller/OpenIdProfileController.ts b/pusher/src/Controller/OpenIdProfileController.ts index 8c7b4a4b..1abfcf79 100644 --- a/pusher/src/Controller/OpenIdProfileController.ts +++ b/pusher/src/Controller/OpenIdProfileController.ts @@ -1,26 +1,13 @@ -import { BaseController } from "./BaseController"; -import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; -import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; -import { adminApi } from "../Services/AdminApi"; import { OPID_CLIENT_ISSUER } from "../Enum/EnvironmentVariable"; -import { IntrospectionResponse } from "openid-client"; +import { BaseHttpController } from "./BaseHttpController"; -export class OpenIdProfileController extends BaseController { - constructor(private App: TemplatedApp) { - super(); - this.profileOpenId(); - } - - profileOpenId() { +export class OpenIdProfileController extends BaseHttpController { + routes() { //eslint-disable-next-line @typescript-eslint/no-misused-promises - this.App.get("/profile", async (res: HttpResponse, req: HttpRequest) => { - res.onAborted(() => { - console.warn("/message request was aborted"); - }); - - const { accessToken } = parse(req.getQuery()); + this.app.get("/profile", async (req, res) => { + const { accessToken } = parse(req.path_query); if (!accessToken) { throw Error("Access token expected cannot to be check on Hydra"); } @@ -29,7 +16,7 @@ export class OpenIdProfileController extends BaseController { if (!resCheckTokenAuth.email) { throw new Error("Email was not found"); } - res.end( + res.send( this.buildHtml( OPID_CLIENT_ISSUER, resCheckTokenAuth.email as string, @@ -38,7 +25,7 @@ export class OpenIdProfileController extends BaseController { ); } catch (error) { console.error("profileCallback => ERROR", error); - this.errorToResponse(error, res); + this.castErrorToResponse(error, res); } }); } @@ -64,13 +51,13 @@ export class OpenIdProfileController extends BaseController {
- +
Profile validated by domain: ${domain} -
+
- Your email: ${email} + Your email: ${email}
diff --git a/pusher/src/Controller/PrometheusController.ts b/pusher/src/Controller/PrometheusController.ts index 7fff3981..9ee851d5 100644 --- a/pusher/src/Controller/PrometheusController.ts +++ b/pusher/src/Controller/PrometheusController.ts @@ -1,18 +1,23 @@ -import { App } from "../Server/sifrr.server"; -import { HttpRequest, HttpResponse } from "uWebSockets.js"; import { register, collectDefaultMetrics } from "prom-client"; +import { Server } from "hyper-express"; +import { BaseHttpController } from "./BaseHttpController"; +import Request from "hyper-express/types/components/http/Request"; +import Response from "hyper-express/types/components/http/Response"; -export class PrometheusController { - constructor(private App: App) { +export class PrometheusController extends BaseHttpController { + constructor(app: Server) { + super(app); collectDefaultMetrics({ gcDurationBuckets: [0.001, 0.01, 0.1, 1, 2, 5], // These are the default buckets. }); - - this.App.get("/metrics", this.metrics.bind(this)); } - private metrics(res: HttpResponse, req: HttpRequest): void { - res.writeHeader("Content-Type", register.contentType); + routes() { + this.app.get("/metrics", this.metrics.bind(this)); + } + + private metrics(req: Request, res: Response): void { + res.setHeader("Content-Type", register.contentType); res.end(register.metrics()); } } diff --git a/pusher/src/Middleware/AdminToken.ts b/pusher/src/Middleware/AdminToken.ts new file mode 100644 index 00000000..6f5505ef --- /dev/null +++ b/pusher/src/Middleware/AdminToken.ts @@ -0,0 +1,22 @@ +import Request from "hyper-express/types/components/http/Request"; +import Response from "hyper-express/types/components/http/Response"; +import { MiddlewareNext, MiddlewarePromise } from "hyper-express/types/components/router/Router"; +import { ADMIN_API_TOKEN } from "../Enum/EnvironmentVariable"; + +export function adminToken(req: Request, res: Response, next?: MiddlewareNext): MiddlewarePromise { + const token = req.header("admin-token"); + + if (ADMIN_API_TOKEN === "") { + res.status(401).end("No token configured!"); + return; + } + if (token !== ADMIN_API_TOKEN) { + console.error("Admin access refused for token: " + token); + res.status(401).end("Incorrect token"); + return; + } + + if (next) { + next(); + } +} diff --git a/pusher/src/Middleware/Cors.ts b/pusher/src/Middleware/Cors.ts new file mode 100644 index 00000000..29c8379d --- /dev/null +++ b/pusher/src/Middleware/Cors.ts @@ -0,0 +1,14 @@ +import Request from "hyper-express/types/components/http/Request"; +import Response from "hyper-express/types/components/http/Response"; +import { MiddlewareNext, MiddlewarePromise } from "hyper-express/types/components/router/Router"; +import { FRONT_URL } from "../Enum/EnvironmentVariable"; + +export function cors(req: Request, res: Response, next?: MiddlewareNext): MiddlewarePromise { + res.setHeader("access-control-allow-headers", "Origin, X-Requested-With, Content-Type, Accept"); + res.setHeader("access-control-allow-methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); + res.setHeader("access-control-allow-origin", FRONT_URL); + + if (next) { + next(); + } +} diff --git a/pusher/src/Model/Websocket/ExAdminSocketInterface.ts b/pusher/src/Model/Websocket/ExAdminSocketInterface.ts index 572bd0fe..663953ef 100644 --- a/pusher/src/Model/Websocket/ExAdminSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExAdminSocketInterface.ts @@ -9,13 +9,13 @@ import { ServerToClientMessage, SubMessage, } from "../../Messages/generated/messages_pb"; -import { WebSocket } from "uWebSockets.js"; +import { compressors } from "hyper-express"; import { ClientDuplexStream } from "grpc"; import { Zone } from "_Model/Zone"; export type AdminConnection = ClientDuplexStream; -export interface ExAdminSocketInterface extends WebSocket { +export interface ExAdminSocketInterface extends compressors.WebSocket { adminConnection: AdminConnection; disconnecting: boolean; } diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 47eba2dd..13045a11 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -12,6 +12,7 @@ import { WebSocket } from "uWebSockets.js"; import { ClientDuplexStream } from "grpc"; import { Zone } from "_Model/Zone"; import { CharacterTexture } from "../../Messages/JsonMessages/CharacterTexture"; +import { compressors } from "hyper-express"; export type BackConnection = ClientDuplexStream; @@ -20,7 +21,7 @@ export interface CharacterLayer { url: string | undefined; } -export interface ExSocketInterface extends WebSocket, Identificable { +export interface ExSocketInterface extends compressors.WebSocket, Identificable { token: string; roomId: string; //userId: number; // A temporary (autoincremented) identifier for this user diff --git a/pusher/src/Server/server/app.ts b/pusher/src/Server/server/app.ts deleted file mode 100644 index 4c422d5c..00000000 --- a/pusher/src/Server/server/app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { App as _App, AppOptions } from "uWebSockets.js"; -import BaseApp from "./baseapp"; -import { extend } from "./utils"; -import { UwsApp } from "./types"; - -class App extends (_App) { - constructor(options: AppOptions = {}) { - super(options); // eslint-disable-line constructor-super - extend(this, new BaseApp()); - } -} - -export default App; diff --git a/pusher/src/Server/server/baseapp.ts b/pusher/src/Server/server/baseapp.ts deleted file mode 100644 index 6d973ac7..00000000 --- a/pusher/src/Server/server/baseapp.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Readable } from "stream"; -import { us_listen_socket_close, TemplatedApp, HttpResponse, HttpRequest } from "uWebSockets.js"; - -import formData from "./formdata"; -import { stob } from "./utils"; -import { Handler } from "./types"; -import { join } from "path"; - -const contTypes = ["application/x-www-form-urlencoded", "multipart/form-data"]; -const noOp = () => true; - -const handleBody = (res: HttpResponse, req: HttpRequest) => { - const contType = req.getHeader("content-type"); - - res.bodyStream = function () { - const stream = new Readable(); - stream._read = noOp; // eslint-disable-line @typescript-eslint/unbound-method - - this.onData((ab: ArrayBuffer, isLast: boolean) => { - // uint and then slicing is bit faster than slice and then uint - stream.push(new Uint8Array(ab.slice((ab as any).byteOffset, ab.byteLength))); // eslint-disable-line @typescript-eslint/no-explicit-any - if (isLast) { - stream.push(null); - } - }); - - return stream; - }; - - res.body = () => stob(res.bodyStream()); - - if (contType.includes("application/json")) res.json = async () => JSON.parse(await res.body()); - if (contTypes.map((t) => contType.includes(t)).includes(true)) res.formData = formData.bind(res, contType); -}; - -class BaseApp { - _sockets = new Map(); - ws!: TemplatedApp["ws"]; - get!: TemplatedApp["get"]; - _post!: TemplatedApp["post"]; - _put!: TemplatedApp["put"]; - _patch!: TemplatedApp["patch"]; - _listen!: TemplatedApp["listen"]; - - post(pattern: string, handler: Handler) { - if (typeof handler !== "function") throw Error(`handler should be a function, given ${typeof handler}.`); - this._post(pattern, (res, req) => { - handleBody(res, req); - handler(res, req); - }); - return this; - } - - put(pattern: string, handler: Handler) { - if (typeof handler !== "function") throw Error(`handler should be a function, given ${typeof handler}.`); - this._put(pattern, (res, req) => { - handleBody(res, req); - - handler(res, req); - }); - return this; - } - - patch(pattern: string, handler: Handler) { - if (typeof handler !== "function") throw Error(`handler should be a function, given ${typeof handler}.`); - this._patch(pattern, (res, req) => { - handleBody(res, req); - - handler(res, req); - }); - return this; - } - - listen(h: string | number, p: Function | number = noOp, cb?: Function) { - if (typeof p === "number" && typeof h === "string") { - this._listen(h, p, (socket) => { - this._sockets.set(p, socket); - if (cb === undefined) { - throw new Error("cb undefined"); - } - cb(socket); - }); - } else if (typeof h === "number" && typeof p === "function") { - this._listen(h, (socket) => { - this._sockets.set(h, socket); - p(socket); - }); - } else { - throw Error("Argument types: (host: string, port: number, cb?: Function) | (port: number, cb?: Function)"); - } - - return this; - } - - close(port: null | number = null) { - if (port) { - this._sockets.has(port) && us_listen_socket_close(this._sockets.get(port)); - this._sockets.delete(port); - } else { - this._sockets.forEach((app) => { - us_listen_socket_close(app); - }); - this._sockets.clear(); - } - return this; - } -} - -export default BaseApp; diff --git a/pusher/src/Server/server/formdata.ts b/pusher/src/Server/server/formdata.ts deleted file mode 100644 index 66e51db4..00000000 --- a/pusher/src/Server/server/formdata.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { createWriteStream } from "fs"; -import { join, dirname } from "path"; -import Busboy from "busboy"; -import mkdirp from "mkdirp"; - -function formData( - contType: string, - options: busboy.BusboyConfig & { - abortOnLimit?: boolean; - tmpDir?: string; - onFile?: ( - fieldname: string, - file: NodeJS.ReadableStream, - filename: string, - encoding: string, - mimetype: string - ) => string; - onField?: (fieldname: string, value: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any - filename?: (oldName: string) => string; - } = {} -) { - console.log("Enter form data"); - options.headers = { - "content-type": contType, - }; - - return new Promise((resolve, reject) => { - const busb = new Busboy(options); - const ret = {}; - - this.bodyStream().pipe(busb); - - busb.on("limit", () => { - if (options.abortOnLimit) { - reject(Error("limit")); - } - }); - - busb.on("file", function (fieldname, file, filename, encoding, mimetype) { - const value: { filePath: string | undefined; filename: string; encoding: string; mimetype: string } = { - filename, - encoding, - mimetype, - filePath: undefined, - }; - - if (typeof options.tmpDir === "string") { - if (typeof options.filename === "function") filename = options.filename(filename); - const fileToSave = join(options.tmpDir, filename); - mkdirp(dirname(fileToSave)); - - file.pipe(createWriteStream(fileToSave)); - value.filePath = fileToSave; - } - if (typeof options.onFile === "function") { - value.filePath = options.onFile(fieldname, file, filename, encoding, mimetype) || value.filePath; - } - - setRetValue(ret, fieldname, value); - }); - - busb.on("field", function (fieldname, value) { - if (typeof options.onField === "function") options.onField(fieldname, value); - - setRetValue(ret, fieldname, value); - }); - - busb.on("finish", function () { - resolve(ret); - }); - - busb.on("error", reject); - }); -} - -function setRetValue( - ret: { [x: string]: any }, // eslint-disable-line @typescript-eslint/no-explicit-any - fieldname: string, - value: { filename: string; encoding: string; mimetype: string; filePath?: string } | any // eslint-disable-line @typescript-eslint/no-explicit-any -) { - if (fieldname.endsWith("[]")) { - fieldname = fieldname.slice(0, fieldname.length - 2); - if (Array.isArray(ret[fieldname])) { - ret[fieldname].push(value); - } else { - ret[fieldname] = [value]; - } - } else { - if (Array.isArray(ret[fieldname])) { - ret[fieldname].push(value); - } else if (ret[fieldname]) { - ret[fieldname] = [ret[fieldname], value]; - } else { - ret[fieldname] = value; - } - } -} - -export default formData; diff --git a/pusher/src/Server/server/sslapp.ts b/pusher/src/Server/server/sslapp.ts deleted file mode 100644 index 80df0e4a..00000000 --- a/pusher/src/Server/server/sslapp.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { SSLApp as _SSLApp, AppOptions } from "uWebSockets.js"; -import BaseApp from "./baseapp"; -import { extend } from "./utils"; -import { UwsApp } from "./types"; - -class SSLApp extends (_SSLApp) { - constructor(options: AppOptions) { - super(options); // eslint-disable-line constructor-super - extend(this, new BaseApp()); - } -} - -export default SSLApp; diff --git a/pusher/src/Server/server/types.ts b/pusher/src/Server/server/types.ts deleted file mode 100644 index afc21d17..00000000 --- a/pusher/src/Server/server/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AppOptions, TemplatedApp, HttpResponse, HttpRequest } from "uWebSockets.js"; - -export type UwsApp = { - (options: AppOptions): TemplatedApp; - new (options: AppOptions): TemplatedApp; - prototype: TemplatedApp; -}; - -export type Handler = (res: HttpResponse, req: HttpRequest) => void; - -export {}; diff --git a/pusher/src/Server/server/utils.ts b/pusher/src/Server/server/utils.ts deleted file mode 100644 index dc813064..00000000 --- a/pusher/src/Server/server/utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ReadStream } from "fs"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function extend(who: any, from: any, overwrite = true) { - const ownProps = Object.getOwnPropertyNames(Object.getPrototypeOf(from)).concat(Object.keys(from)); - ownProps.forEach((prop) => { - if (prop === "constructor" || from[prop] === undefined) return; - if (who[prop] && overwrite) { - who[`_${prop}`] = who[prop]; - } - if (typeof from[prop] === "function") who[prop] = from[prop].bind(who); - else who[prop] = from[prop]; - }); -} - -function stob(stream: ReadStream): Promise { - return new Promise((resolve) => { - const buffers: Buffer[] = []; - stream.on("data", buffers.push.bind(buffers)); - - stream.on("end", () => { - switch (buffers.length) { - case 0: - resolve(Buffer.allocUnsafe(0)); - break; - case 1: - resolve(buffers[0]); - break; - default: - resolve(Buffer.concat(buffers)); - } - }); - }); -} - -export { extend, stob }; diff --git a/pusher/src/Server/sifrr.server.ts b/pusher/src/Server/sifrr.server.ts deleted file mode 100644 index 4ef03721..00000000 --- a/pusher/src/Server/sifrr.server.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { parse } from "query-string"; -import { HttpRequest } from "uWebSockets.js"; -import App from "./server/app"; -import SSLApp from "./server/sslapp"; -import * as types from "./server/types"; - -const getQuery = (req: HttpRequest) => { - return parse(req.getQuery()); -}; - -export { App, SSLApp, getQuery }; -export * from "./server/types"; - -export default { - App, - SSLApp, - getQuery, - ...types, -}; diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index c72a6ba8..d3b4d414 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -1,6 +1,5 @@ import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import Axios from "axios"; -import { GameRoomPolicyTypes } from "_Model/PusherRoom"; import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; import { MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { RoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 30fe761f..b67c359d 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -53,6 +53,7 @@ import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface" import { WebSocket } from "uWebSockets.js"; import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; +import { compressors } from "hyper-express"; const debug = Debug("socket"); @@ -619,7 +620,7 @@ export class SocketManager implements ZoneEventListener { emitInBatch(listener, subMessage); } - public emitWorldFullMessage(client: WebSocket) { + public emitWorldFullMessage(client: compressors.WebSocket) { const errorMessage = new WorldFullMessage(); const serverToClientMessage = new ServerToClientMessage(); @@ -630,7 +631,7 @@ export class SocketManager implements ZoneEventListener { } } - public emitTokenExpiredMessage(client: WebSocket) { + public emitTokenExpiredMessage(client: compressors.WebSocket) { const errorMessage = new TokenExpiredMessage(); const serverToClientMessage = new ServerToClientMessage(); @@ -641,7 +642,7 @@ export class SocketManager implements ZoneEventListener { } } - public emitConnexionErrorMessage(client: WebSocket, message: string) { + public emitConnexionErrorMessage(client: compressors.WebSocket, message: string) { const errorMessage = new WorldConnexionMessage(); errorMessage.setMessage(message); diff --git a/pusher/yarn.lock b/pusher/yarn.lock index 8992a72c..f5f2884d 100644 --- a/pusher/yarn.lock +++ b/pusher/yarn.lock @@ -3,40 +3,40 @@ "@babel/code-frame@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: - "@babel/highlight" "^7.14.5" + "@babel/highlight" "^7.16.7" -"@babel/helper-validator-identifier@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" - integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== +"@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: - "@babel/helper-validator-identifier" "^7.14.5" + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" "@mapbox/node-pre-gyp@^1.0.4": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" - integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== + version "1.0.8" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz#32abc8a5c624bc4e46c43d84dfb8b26d33a96f58" + integrity sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg== dependencies: detect-libc "^1.0.3" https-proxy-agent "^5.0.0" make-dir "^3.1.0" - node-fetch "^2.6.1" + node-fetch "^2.6.5" nopt "^5.0.0" - npmlog "^4.1.2" + npmlog "^5.0.1" rimraf "^3.0.2" - semver "^7.3.4" - tar "^6.1.0" + semver "^7.3.5" + tar "^6.1.11" "@panva/asn1.js@^1.0.0": version "1.0.0" @@ -44,9 +44,9 @@ integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw== "@sindresorhus/is@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5" - integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.4.0.tgz#e277e5bdbdf7cb1e20d320f02f5e2ed113cd3185" + integrity sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ== "@szmarczak/http-timer@^4.0.5": version "4.0.6" @@ -55,17 +55,25 @@ dependencies: defer-to-connect "^2.0.0" -"@types/busboy@^0.2.3": - version "0.2.4" - resolved "https://registry.yarnpkg.com/@types/busboy/-/busboy-0.2.4.tgz#19922f8c7076ad6d47b2565da8c0a94c88776315" - integrity sha512-f+ZCVjlcN8JW/zf3iR0GqO4gjOUlltMTtZjn+YR1mlK+MVu6esTiIecO0/GQlmYQPQLdBnc7+5vG3Xb+SkvFLw== +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/busboy@^0.3.1": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@types/busboy/-/busboy-0.3.2.tgz#2f29b017513415399c42632ae6a7cfcb1409b79c" + integrity sha512-iEvdm9Z9KdSs/ozuh1Z7ZsXrOl8F4M/CLMXPZHr3QuJ4d6Bjn+HBMC5EMKpwpAo8oi8iK9GZfFoHaIMrrZgwVw== dependencies: "@types/node" "*" "@types/bytebuffer@^5.0.40": - version "5.0.42" - resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.42.tgz#1c602a77942d34c5c0879ad75c58d5d8c07dfb3b" - integrity sha512-lEgKojWUAc/MG2t649oZS5AfYFP2xRNPoDuwDBlBMjHXd8MaGPgFgtCXUK7inZdBOygmVf10qxc1Us8GXC96aw== + version "5.0.43" + resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.43.tgz#b5259fca1412106bcee0cabfbf7c104846d06738" + integrity sha512-vQnTYvy4LpSojHjKdmg4nXFI1BAiYPvZ/k3ouczZAQnbDprk1xqxJiFmFHyy8y6MuUq3slz5erNMtn6n87uVKw== dependencies: "@types/long" "*" "@types/node" "*" @@ -85,20 +93,48 @@ resolved "https://registry.yarnpkg.com/@types/circular-json/-/circular-json-0.4.0.tgz#7401f7e218cfe87ad4c43690da5658b9acaf51be" integrity sha512-7+kYB7x5a7nFWW1YPBh3KxhwKfiaI4PbZ1RvzBU91LZy7lWJO822CI+pqzSre/DZ7KsCuMKdHnLHHFu8AyXbQg== +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + "@types/debug@^4.1.5": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.6.tgz#0b7018723084918a865eff99249c490505df2163" - integrity sha512-7fDOJFA/x8B+sO1901BmHlf5dE1cxBU8mRXj8QOEDnn16hhGJv/IHxJtZhvsabZsIMn0eLIyeOKAeqSNJJYTpA== + version "4.1.7" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" + integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== + dependencies: + "@types/ms" "*" "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== +"@types/express-serve-static-core@^4.17.18": + version "4.17.28" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@^4.17.13": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/google-protobuf@^3.7.3": - version "3.15.3" - resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.3.tgz#054fb37aecb34d7dec826e1ce2b40cc27ec3d06a" - integrity sha512-MDpu7lit927cdLtBzTPUFjXGANFUnu5ThPqjygY8XmCyI/oDlIA0jAi4sffGOxYaLK2CCxAuU9wGxsgAQbA6FQ== + version "3.15.5" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.5.tgz#644b2be0f5613b1f822c70c73c6b0e0b5b5fa2ad" + integrity sha512-6bgv24B+A2bo9AfzReeg5StdiijKzwwnRflA8RLd1V4Yv995LeTmo0z69/MPbBDFSiZWdZHQygLo/ccXhMEDgw== "@types/http-cache-semantics@*": version "4.0.1" @@ -113,26 +149,26 @@ http-status-codes "*" "@types/jasmine@^3.5.10": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.8.1.tgz#8feebf4035d1e4c6a6ed4d27f3bbd285d8d0da91" - integrity sha512-ioRNoJvv0eXL1c9BZKpnywZWb5YflhaSiF3IOp9deyoh30MOwkB3bNuzi4UW76EFEhcmqpoEpdWhcUAAilomTw== + version "3.10.3" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.10.3.tgz#a89798b3d5a8bd23ca56e855a9aee3e5a93bdaaa" + integrity sha512-SWyMrjgdAUHNQmutvDcKablrJhkDLy4wunTme8oYLjKp41GnHGxMRXr2MQMvy/qy8H3LdzwQk9gH4hZ6T++H8g== "@types/json-schema@^7.0.3": - version "7.0.8" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" - integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== "@types/jsonwebtoken@^8.3.8": - version "8.5.4" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz#50ccaf0aa6f5d7b9956e70fe323b76e582991913" - integrity sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg== + version "8.5.8" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz#01b39711eb844777b7af1d1f2b4cf22fda1c0c44" + integrity sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A== dependencies: "@types/node" "*" "@types/keyv@*": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.2.tgz#5d97bb65526c20b6e0845f6b0d2ade4f28604ee5" - integrity sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41" + integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== dependencies: "@types/node" "*" @@ -141,6 +177,11 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/mkdirp@^1.0.1": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.2.tgz#8d0bad7aa793abe551860be1f7ae7f3198c16666" @@ -148,16 +189,36 @@ dependencies: "@types/node" "*" +"@types/ms@*": + version "0.7.31" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + "@types/node@*": - version "16.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" - integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== + version "17.0.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074" + integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== + +"@types/node@^16.11.6": + version "16.11.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.25.tgz#bb812b58bacbd060ce85921250d8b4ca553cd4a2" + integrity sha512-NrTwfD7L1RTc2qrHQD4RTTy4p0CO2LatKBEKEds3CaVuhoM/+DJzmWZl5f+ikR8cm8F5mfJxK+9rQq07gRiSjQ== "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -165,6 +226,14 @@ dependencies: "@types/node" "*" +"@types/serve-static@*": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/strip-bom@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" @@ -235,6 +304,14 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +accepts@^1.3.7: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + acorn-jsx@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -287,20 +364,15 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" @@ -324,18 +396,18 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== dependencies: delegates "^1.0.0" - readable-stream "^2.0.6" + readable-stream "^3.6.0" arg@^4.1.0: version "4.1.3" @@ -368,9 +440,9 @@ astral-regex@^2.0.0: integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== axios@^0.21.2: - version "0.21.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.2.tgz#21297d5084b2aeeb422f5d38e7be4fbb82239017" - integrity sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg== + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: follow-redirects "^1.14.0" @@ -410,16 +482,16 @@ buffer-equal-constant-time@1.0.1: integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -busboy@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" - integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== +busboy@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.4.0.tgz#7bb6ea83e672516ab62b7c3c418b5942f88b45bb" + integrity sha512-TytIELfX6IPn1OClqcBz0NFE6+JT9e3iW0ZpgnEl7ffsfDxvRZGHfPaSHGbrI443nSV3GutCDWuqLB6yHY92Ew== dependencies: - dicer "0.3.0" + streamsearch "^1.1.0" bytebuffer@~5: version "5.0.1" @@ -433,7 +505,7 @@ cacheable-lookup@^5.0.3: resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== -cacheable-request@^7.0.1: +cacheable-request@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== @@ -465,10 +537,10 @@ chalk@^2.0.0, chalk@^2.1.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.1.0, chalk@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -479,9 +551,9 @@ chardet@^0.7.0: integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== chokidar@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -515,7 +587,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-truncate@^2.1.0: +cli-truncate@2.1.0, cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== @@ -573,40 +645,55 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +colorette@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + +colorette@^2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" + integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== colour@~0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g= -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^8.2.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -console-control-strings@^1.0.0, console-control-strings@~1.1.0: +console-control-strings@^1.0.0, console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cookie-signature@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.1.0.tgz#cc94974f91fb9a9c1bb485e95fc2b7f4b120aff2" + integrity sha512-Alvs19Vgq07eunykd3Xy2jF0/qSNv2u7KDbAek9H5liV1UMijbqFs5cycZvv5dVsvseT/U4H8/7/w8Koh35C4A== -cosmiconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== +cookie@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -639,10 +726,10 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== +debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" @@ -663,15 +750,10 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= - deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== defer-to-connect@^2.0.0: version "2.0.1" @@ -688,13 +770,6 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -dicer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" - integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== - dependencies: - streamsearch "0.1.2" - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -861,16 +936,16 @@ estraverse@^4.1.1: integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -execa@^5.0.0: +execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -981,24 +1056,25 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== dependencies: - aproba "^1.0.3" + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" generic-type-guard@^3.2.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.4.1.tgz#0896dc018de915c890562a34763858076e4676da" - integrity sha512-sXce0Lz3Wfy2rR1W8O8kUemgEriTeG1x8shqSJeWGb0FwJu2qBEkB1M2qXbdSLmpgDnHcIXo0Dj/1VLNJkK/QA== + version "3.5.0" + resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.5.0.tgz#39de9f8fceee65d79e7540959f0e7b23210c07b6" + integrity sha512-OpgXv/sbRobhFboaSyN/Tsh97Sxt5pcfLLxCiYZgYIIWFFp+kn2EzAXiaQZKEVRlq1rOE/zh8cYhJXEwplbJiQ== get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" @@ -1025,9 +1101,9 @@ glob-parent@^5.0.0, glob-parent@~5.1.2: is-glob "^4.0.1" glob@^7.0.5, glob@^7.1.3, glob@^7.1.6: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1044,21 +1120,21 @@ globals@^12.1.0: type-fest "^0.8.1" google-protobuf@^3.13.0: - version "3.17.3" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.17.3.tgz#f87595073545a77946c8f0b67c302c5f7646d700" - integrity sha512-OVPzcSWIAJ+d5yiHyeaLrdufQtrvaBrF4JQg+z8ynTkbO3uFcujqXszTumqg1cGsAsjkWnI+M5B1xZ19yR4Wyg== + version "3.19.4" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.19.4.tgz#8d32c3e34be9250956f28c0fb90955d13f311888" + integrity sha512-OIPNCxsG2lkIvf+P5FNfJ/Km95CsXOBecS9ZcAU6m2Rq3svc0Apl9nB3GMDNKfQ9asNv4KjyAqGwPQFrVle3Yg== got@^11.8.0: - version "11.8.2" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" - integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== + version "11.8.3" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" + integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg== dependencies: "@sindresorhus/is" "^4.0.0" "@szmarczak/http-timer" "^4.0.5" "@types/cacheable-request" "^6.0.1" "@types/responselike" "^1.0.0" cacheable-lookup "^5.0.3" - cacheable-request "^7.0.1" + cacheable-request "^7.0.2" decompress-response "^6.0.0" http2-wrapper "^1.0.0-beta.5.2" lowercase-keys "^2.0.0" @@ -1066,9 +1142,9 @@ got@^11.8.0: responselike "^2.0.0" grpc@^1.24.4: - version "1.24.10" - resolved "https://registry.yarnpkg.com/grpc/-/grpc-1.24.10.tgz#4cafa5f366e6d64440c3c46e134add47b205a293" - integrity sha512-mTR+P5IL3WO3oCgNwxKFE5ksXEJfCYP+dk0aIbjB494f7OnHTmssU5r9vznsSq3+cdLcxAzGFskOj5CaPwi8KA== + version "1.24.11" + resolved "https://registry.yarnpkg.com/grpc/-/grpc-1.24.11.tgz#7039da9f6f22ce35168535a6d5dda618398a5966" + integrity sha512-8/AQdFCzCeCDWW3SoaMNp6ccbRvTQEH1O1u1uFtt29eWsg5gSZCJ3m6fbkduEIh3smY7WAPP+LgVJ5n3nZRxcA== dependencies: "@mapbox/node-pre-gyp" "^1.0.4" "@types/bytebuffer" "^5.0.40" @@ -1087,7 +1163,7 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-unicode@^2.0.0: +has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= @@ -1105,9 +1181,9 @@ http-cache-semantics@^4.0.0: integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-status-codes@*: - version "2.1.4" - resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.1.4.tgz#453d99b4bd9424254c4f6a9a3a03715923052798" - integrity sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg== + version "2.2.0" + resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.2.0.tgz#bb2efe63d941dfc2be18e15f703da525169622be" + integrity sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng== http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" @@ -1130,6 +1206,25 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +hyper-express@^5.8.1: + version "5.8.1" + resolved "https://registry.yarnpkg.com/hyper-express/-/hyper-express-5.8.1.tgz#1ca8568852bac4148e1e512df7fccad11cabaeee" + integrity sha512-e/B0/IvKK6SKquz6Tbh50GzyTeNd3DazNHktu0pX4UblJCPsD9TWxh+np/3AIvSqpm3SSi0ohIcCfiVWxgxOfg== + dependencies: + "@types/busboy" "^0.3.1" + "@types/express" "^4.17.13" + "@types/node" "^16.11.6" + accepts "^1.3.7" + busboy "^1.0.0" + cookie "^0.4.1" + cookie-signature "^1.1.0" + mime-types "^2.1.33" + query-string "^7.0.1" + range-parser "^1.2.1" + type-is "^1.6.18" + uWebSockets.js "github:uNetworking/uWebSockets.js#v20.6.0" + uid-safe "^2.1.5" + iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -1168,7 +1263,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@~2.0.3: +inherits@2, inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1209,10 +1304,10 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" - integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== +is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -1239,9 +1334,9 @@ is-fullwidth-code-point@^3.0.0: integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" @@ -1261,37 +1356,27 @@ is-regexp@^1.0.0: integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -jasmine-core@~3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.8.0.tgz#815399aae5aa5d9beeb1262805f981b99ffc9bf0" - integrity sha512-zl0nZWDrmbCiKns0NcjkFGYkVTGCPUgoHypTaj+G2AzaWus7QGoXARSlYsSle2VRpSdfJmM+hzmFKzQNhF2kHg== +jasmine-core@~3.99.0: + version "3.99.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.99.0.tgz#99a3da0d38ba2de82614d9198b7b1bc1c32a5960" + integrity sha512-+ZDaJlEfRopINQqgE+hvzRyDIQDeKfqqTvF8RzXsvU1yE3pBDRud2+Qfh9WvGgRpuzqxyQJVI6Amy5XQ11r/3w== jasmine@^3.5.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.8.0.tgz#4497bc797eede7ca9de18179aedd4cf50245d8dc" - integrity sha512-kdQ3SfcNpMbbMdgJPLyFe9IksixdnrgYaCJapP9sS0aLgdWdIZADNXEr+11Zafxm1VDfRSC5ZL4fzXT0bexzXw== + version "3.99.0" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.99.0.tgz#7cc7aeda7ade2d57694fc818a374f778cbb4ea62" + integrity sha512-YIThBuHzaIIcjxeuLmPD40SjxkEcc8i//sGMDKCgkRMVgIwRJf5qyExtlJpQeh7pkeoBSOe6lQEdg+/9uKg9mw== dependencies: glob "^7.1.6" - jasmine-core "~3.8.0" + jasmine-core "~3.99.0" jose@^2.0.5: version "2.0.5" @@ -1367,9 +1452,9 @@ jws@^3.2.2: safe-buffer "^5.0.1" keyv@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" - integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== + version "4.1.1" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.1.1.tgz#02c538bfdbd2a9308cc932d4096f05ae42bfa06a" + integrity sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ== dependencies: json-buffer "3.0.1" @@ -1389,41 +1474,41 @@ levn@^0.3.0, levn@~0.3.0: type-check "~0.3.2" lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lint-staged@^11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.0.1.tgz#1b8ae8ed5a52ed87252db95fe008c2618c85f55a" - integrity sha512-RkTA1ulE6jAGFskxpGAwxfVRXjHp7D9gFg/+KMARUWMPiVFP0t28Em2u0gL8sA0w3/ck3TC57F2v2RNeQ5XPnw== + version "11.2.6" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.2.6.tgz#f477b1af0294db054e5937f171679df63baa4c43" + integrity sha512-Vti55pUnpvPE0J9936lKl0ngVeTdSZpEdTNhASbkaWX7J5R9OEifo1INBGQuGW4zmy6OG+TcWPJ3m5yuy5Q8Tg== dependencies: - chalk "^4.1.1" - cli-truncate "^2.1.0" - commander "^7.2.0" - cosmiconfig "^7.0.0" - debug "^4.3.1" - dedent "^0.7.0" + cli-truncate "2.1.0" + colorette "^1.4.0" + commander "^8.2.0" + cosmiconfig "^7.0.1" + debug "^4.3.2" enquirer "^2.3.6" - execa "^5.0.0" - listr2 "^3.8.2" - log-symbols "^4.1.0" + execa "^5.1.1" + listr2 "^3.12.2" micromatch "^4.0.4" normalize-path "^3.0.0" please-upgrade-node "^3.2.0" string-argv "0.3.1" - stringify-object "^3.3.0" + stringify-object "3.3.0" + supports-color "8.1.1" -listr2@^3.8.2: - version "3.10.0" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.10.0.tgz#58105a53ed7fa1430d1b738c6055ef7bb006160f" - integrity sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw== +listr2@^3.12.2: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== dependencies: cli-truncate "^2.1.0" - colorette "^1.2.2" + colorette "^2.0.16" log-update "^4.0.0" p-map "^4.0.0" - rxjs "^6.6.7" + rfdc "^1.3.0" + rxjs "^7.5.1" through "^2.3.8" wrap-ansi "^7.0.0" @@ -1477,14 +1562,6 @@ lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - log-update@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" @@ -1524,6 +1601,11 @@ make-error@^1.1.1, make-error@^1.3.6: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -1537,6 +1619,18 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +mime-db@1.51.0: + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== + +mime-types@^2.1.33, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== + dependencies: + mime-db "1.51.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -1553,9 +1647,9 @@ mimic-response@^3.1.0: integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" @@ -1565,9 +1659,9 @@ minimist@^1.2.5: integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minipass@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + version "3.1.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== dependencies: yallist "^4.0.0" @@ -1607,21 +1701,26 @@ mute-stream@0.0.8: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.13.2: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-fetch@^2.6.1: +node-fetch@^2.6.5: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -1652,22 +1751,22 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -object-assign@^4.1.0: +object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -1697,9 +1796,9 @@ onetime@^5.1.0, onetime@^5.1.2: mimic-fn "^2.1.0" openid-client@^4.7.4: - version "4.7.4" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-4.7.4.tgz#bd9978456d53d38adb89856b14a8fbd094f7732e" - integrity sha512-n+RURXYuR0bBZo9i0pn+CXZSyg5JYQ1nbwEwPQvLE7EcJt/vMZ2iIMjLehl5DvCN53XUoPVZs9KAE5r6d9fxsw== + version "4.9.1" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-4.9.1.tgz#4f00a9d1566c0fa08f0dd5986cf0e6b1e5d14186" + integrity sha512-DYUF07AHjI3QDKqKbn2F7RqozT4hyi4JvmpodLrq0HHoNP7t/AjeG/uqiBK1/N2PZSAQEThVjDLHSmJN4iqu/w== dependencies: aggregate-error "^3.1.0" got "^11.8.0" @@ -1782,7 +1881,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -1793,9 +1892,9 @@ path-type@^4.0.0: integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== please-upgrade-node@^3.2.0: version "3.2.0" @@ -1810,14 +1909,9 @@ prelude-ls@~1.1.2: integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prettier@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" - integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== progress@^2.0.0: version "2.0.3" @@ -1864,23 +1958,39 @@ query-string@^6.13.3: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" +query-string@^7.0.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" + integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -readable-stream@^2.0.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +random-bytes@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= + +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" readdirp@~3.6.0: version "3.6.0" @@ -1900,9 +2010,9 @@ regexpp@^3.0.0: integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== resolve-alpn@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28" - integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA== + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== resolve-from@^4.0.0: version "4.0.0" @@ -1910,12 +2020,13 @@ resolve-from@^4.0.0: integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve@^1.0.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" responselike@^2.0.0: version "2.0.0" @@ -1932,6 +2043,11 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -1958,23 +2074,25 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -rxjs@^6.6.0, rxjs@^6.6.7: +rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1: +rxjs@^7.5.1: + version "7.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" + integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== + dependencies: + tslib "^2.1.0" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -1995,14 +2113,14 @@ semver@^6.0.0, semver@^6.1.2: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.4: +semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" -set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -2032,9 +2150,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== slice-ansi@^2.1.0: version "2.1.0" @@ -2064,9 +2182,9 @@ slice-ansi@^4.0.0: is-fullwidth-code-point "^3.0.0" source-map-support@^0.5.12, source-map-support@^0.5.17: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -2086,10 +2204,10 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -streamsearch@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== strict-uri-encode@^2.0.0: version "2.0.0" @@ -2110,13 +2228,14 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" string-width@^3.0.0: version "3.1.0" @@ -2127,23 +2246,14 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" + safe-buffer "~5.2.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -stringify-object@^3.3.0: +stringify-object@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -2159,13 +2269,6 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -2173,12 +2276,12 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" @@ -2200,6 +2303,13 @@ strip-json-comments@^3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2214,6 +2324,11 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -2224,10 +2339,10 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tar@^6.1.0: - version "6.1.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.10.tgz#8a320a74475fba54398fa136cd9883aa8ad11175" - integrity sha512-kvvfiVvjGMxeUNB6MyYv5z7vhfFRwbwCXJAeL0/lnbrttBVqcMOnpHUf0X42LrPMR8mMpgapkJMchFH4FSHzNA== +tar@^6.1.11: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -2320,6 +2435,11 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.17.1: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -2344,14 +2464,29 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -typescript@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" - integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== +type-is@^1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" -uWebSockets.js@uNetworking/uWebSockets.js#v20.4.0: - version "20.4.0" - resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/65f39bdff763be3883e6cf18e433dd4fec155845" +typescript@^4.5.2: + version "4.5.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" + integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== + +"uWebSockets.js@github:uNetworking/uWebSockets.js#v20.6.0": + version "20.6.0" + resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/a58e810e47a23696410f6073c8c905dc38f75da5" + +uid-safe@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== + dependencies: + random-bytes "~1.0.0" uri-js@^4.2.2: version "4.4.1" @@ -2360,7 +2495,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@~1.0.1: +util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -2371,9 +2506,9 @@ uuid@8.3.2: integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuidv4@*, uuidv4@^6.0.7: - version "6.2.11" - resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-6.2.11.tgz#34d5a03324eb38296b87ae523a64233b5286cc27" - integrity sha512-OTS4waH9KplrXNADKo+Q1kT9AHWr8DaC0S5F54RQzEwcUaEzBEWQQlJyDUw/u1bkRhJyqkqhLD4M4lbFbV+89g== + version "6.2.12" + resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-6.2.12.tgz#e8c1d1d733c3fa4963d4610b8a3a09b4ec58ca96" + integrity sha512-UnN4ThIYWhv3ZUE8UwDnnCvh4JafCNu+sQkxmLyjCVwK3rjLfkg3DYiEv6oCMDIAIVEDP4INg4kX/C5hKaRzZA== dependencies: "@types/uuid" "8.3.1" uuid "8.3.2" @@ -2410,12 +2545,12 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: - string-width "^1.0.2 || 2" + string-width "^1.0.2 || 2 || 3 || 4" window-size@^0.1.4: version "0.1.4" From 2161a40e05650c9af6e76557b272f319514ac99e Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 17 Feb 2022 15:02:11 +0100 Subject: [PATCH 02/61] Add endpoints on pusher to resolve wokas --- docs/dev/README.md | 2 + docs/dev/wokas.md | 30 + front/src/Connexion/ConnectionManager.ts | 10 + pusher/data/woka.json | 1555 +++++++++++++++++ pusher/src/App.ts | 2 + .../src/Controller/AuthenticateController.ts | 2 +- pusher/src/Controller/WokaListController.ts | 47 + pusher/src/Enum/PlayerTextures.ts | 64 + pusher/src/Middleware/HasToken.ts | 16 + pusher/src/Services/AdminWokaService.ts | 71 + pusher/src/Services/LocalWokaService.ts | 64 + pusher/src/Services/WokaServiceInterface.ts | 18 + 12 files changed, 1880 insertions(+), 1 deletion(-) create mode 100644 docs/dev/wokas.md create mode 100644 pusher/data/woka.json create mode 100644 pusher/src/Controller/WokaListController.ts create mode 100644 pusher/src/Enum/PlayerTextures.ts create mode 100644 pusher/src/Middleware/HasToken.ts create mode 100644 pusher/src/Services/AdminWokaService.ts create mode 100644 pusher/src/Services/LocalWokaService.ts create mode 100644 pusher/src/Services/WokaServiceInterface.ts diff --git a/docs/dev/README.md b/docs/dev/README.md index d05c4884..4a0fdbcc 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -13,4 +13,6 @@ Check out the [contributing guide](../../CONTRIBUTING.md) ## Front documentation +- [How to add translations](how-to-translate.md) - [How to add new functions in the scripting API](contributing-to-scripting-api.md) +- [About Wokas](wokas.md) diff --git a/docs/dev/wokas.md b/docs/dev/wokas.md new file mode 100644 index 00000000..e59e082d --- /dev/null +++ b/docs/dev/wokas.md @@ -0,0 +1,30 @@ +# About Wokas + +Wokas are made of a set of layers (for custom wokas), or of only 1 layers (if selected from the first screen) + +Internally, each layer has: + +- a name +- a URL + +## Connection to a map + +When a user connects to a map, it sends, as a web-socket parameter, the list of layer **names**. + +The pusher is in charge of converting those layer names into the URLs. This way, a client cannot send any random +URL to the pusher. + +When the pusher receives the layer names, it validates these names and sends back the URLs + sends the names+urls to the back. +If the layers cannot be validated, the websocket connections sends an error message and closes. The user is sent back to the "choose your Woka" screen. + +## Getting the list of available Wokas + +The pusher can send the list of available Wokas to the user. +It can actually query the admin for this list, if needed (= if an admin is configured) + +## In the pusher + +The pusher contains a classes in charge of managing the Wokas: + +- `LocalWokaService`: used when no admin is connected. Returns a hard-coded list of Wokas (stored in `pusher/data/woka.json`). +- `AdminWokaService`: used to delegate the list of Wokas to the admin. diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 05d0255d..391da7bf 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -220,6 +220,16 @@ class ConnectionManager { if (this.localUser.textures.length === 0) { this.localUser.textures = this._currentRoom.textures; } else { + // TODO: the local store should NOT be used as a buffer for all the texture we were authorized to have. Bad idea. + // Instead, it is the responsibility of the ADMIN to return the EXACT list of textures we can have in a given context + // + this list can change over time or over rooms. + + // 1- a room could forbid a particular dress code. In this case, the user MUST change its skin. + // 2- a room can allow "external skins from other maps" => important: think about fediverse! => switch to URLs? (with a whitelist mechanism?) => but what about NFTs? + + // Note: stocker des URL dans le localstorage pour les utilisateurs actuels: mauvaise idée (empêche de mettre l'URL à jour dans le futur) => en même temps, problème avec le portage de user d'un serveur à l'autre + // Réfléchir à une notion de "character server" ?? + this._currentRoom.textures.forEach((newTexture) => { const alreadyExistTexture = this.localUser.textures.find((c) => newTexture.id === c.id); if (this.localUser.textures.findIndex((c) => newTexture.id === c.id) !== -1) { diff --git a/pusher/data/woka.json b/pusher/data/woka.json new file mode 100644 index 00000000..ca036647 --- /dev/null +++ b/pusher/data/woka.json @@ -0,0 +1,1555 @@ +{ + "woka": { + "collections": [ + { + "name": "default", + "position": 0, + "textures": [ + { + "id": "male1", + "name": "male1", + "url": "resources/characters/pipoya/Male 01-1.png" + }, + { + "id": "male2", + "name": "male2", + "url": "resources/characters/pipoya/Male 02-2.png" + }, + { + "id": "male3", + "name": "male3", + "url": "resources/characters/pipoya/Male 03-4.png" + }, + { + "id": "male4", + "name": "male4", + "url": "resources/characters/pipoya/Male 09-1.png" + }, + { + "id": "male5", + "name": "male5", + "url": "resources/characters/pipoya/Male 10-3.png" + }, + { + "id": "male6", + "name": "male6", + "url": "resources/characters/pipoya/Male 17-2.png" + }, + { + "id": "male7", + "name": "male7", + "url": "resources/characters/pipoya/Male 18-1.png" + }, + { + "id": "male8", + "name": "male8", + "url": "resources/characters/pipoya/Male 16-4.png" + }, + { + "id": "male9", + "name": "male9", + "url": "resources/characters/pipoya/Male 07-2.png" + }, + { + "id": "male10", + "name": "male10", + "url": "resources/characters/pipoya/Male 05-3.png" + }, + { + "id": "male11", + "name": "male11", + "url": "resources/characters/pipoya/Teacher male 02.png" + }, + { + "id": "male12", + "name": "male12", + "url": "resources/characters/pipoya/su4 Student male 12.png" + }, + { + "id": "female1", + "name": "female1", + "url": "resources/characters/pipoya/Female 01-1.png" + }, + { + "id": "female2", + "name": "female2", + "url": "resources/characters/pipoya/Female 02-2.png" + }, + { + "id": "female3", + "name": "female3", + "url": "resources/characters/pipoya/Female 03-4.png" + }, + { + "id": "female4", + "name": "female4", + "url": "resources/characters/pipoya/Female 09-1.png" + }, + { + "id": "female5", + "name": "female5", + "url": "resources/characters/pipoya/Female 10-3.png" + }, + { + "id": "female6", + "name": "female6", + "url": "resources/characters/pipoya/Female 17-2.png" + }, + { + "id": "female7", + "name": "female7", + "url": "resources/characters/pipoya/Female 18-1.png" + }, + { + "id": "female8", + "name": "female8", + "url": "resources/characters/pipoya/Female 16-4.png" + }, + { + "id": "female9", + "name": "female9", + "url": "resources/characters/pipoya/Female 07-2.png" + }, + { + "id": "female10", + "name": "female10", + "url": "resources/characters/pipoya/Female 05-3.png" + }, + { + "id": "female11", + "name": "female11", + "url": "resources/characters/pipoya/Teacher fmale 02.png" + }, + { + "id": "female12", + "name": "female12", + "url": "resources/characters/pipoya/su4 Student fmale 12.png" + } + ] + } + ] + }, + "body": { + "required": true, + "collections": [ + { + "name": "default", + "position": 0, + "textures": [ + { + "id": "body1", + "name": "body1", + "url": "resources/customisation/character_color/character_color0.png" + }, + { + "id": "body2", + "name": "body2", + "url": "resources/customisation/character_color/character_color1.png" + }, + { + "id": "body3", + "name": "body3", + "url": "resources/customisation/character_color/character_color2.png" + }, + { + "id": "body4", + "name": "body4", + "url": "resources/customisation/character_color/character_color3.png" + }, + { + "id": "body5", + "name": "body5", + "url": "resources/customisation/character_color/character_color4.png" + }, + { + "id": "body6", + "name": "body6", + "url": "resources/customisation/character_color/character_color5.png" + }, + { + "id": "body7", + "name": "body7", + "url": "resources/customisation/character_color/character_color6.png" + }, + { + "id": "body8", + "name": "body8", + "url": "resources/customisation/character_color/character_color7.png" + }, + { + "id": "body9", + "name": "body9", + "url": "resources/customisation/character_color/character_color8.png" + }, + { + "id": "body10", + "name": "body10", + "url": "resources/customisation/character_color/character_color9.png" + }, + { + "id": "body11", + "name": "body11", + "url": "resources/customisation/character_color/character_color10.png" + }, + { + "id": "body12", + "name": "body12", + "url": "resources/customisation/character_color/character_color11.png" + }, + { + "id": "body13", + "name": "body13", + "url": "resources/customisation/character_color/character_color12.png" + }, + { + "id": "body14", + "name": "body14", + "url": "resources/customisation/character_color/character_color13.png" + }, + { + "id": "body15", + "name": "body15", + "url": "resources/customisation/character_color/character_color14.png" + }, + { + "id": "body16", + "name": "body16", + "url": "resources/customisation/character_color/character_color15.png" + }, + { + "id": "body17", + "name": "body17", + "url": "resources/customisation/character_color/character_color16.png" + }, + { + "id": "body18", + "name": "body18", + "url": "resources/customisation/character_color/character_color17.png" + }, + { + "id": "body19", + "name": "body19", + "url": "resources/customisation/character_color/character_color18.png" + }, + { + "id": "body20", + "name": "body20", + "url": "resources/customisation/character_color/character_color19.png" + }, + { + "id": "body21", + "name": "body21", + "url": "resources/customisation/character_color/character_color20.png" + }, + { + "id": "body22", + "name": "body22", + "url": "resources/customisation/character_color/character_color21.png" + }, + { + "id": "body23", + "name": "body23", + "url": "resources/customisation/character_color/character_color22.png" + }, + { + "id": "body24", + "name": "body24", + "url": "resources/customisation/character_color/character_color23.png" + }, + { + "id": "body25", + "name": "body25", + "url": "resources/customisation/character_color/character_color24.png" + }, + { + "id": "body26", + "name": "body26", + "url": "resources/customisation/character_color/character_color25.png" + }, + { + "id": "body27", + "name": "body27", + "url": "resources/customisation/character_color/character_color26.png" + }, + { + "id": "body28", + "name": "body28", + "url": "resources/customisation/character_color/character_color27.png" + }, + { + "id": "body29", + "name": "body29", + "url": "resources/customisation/character_color/character_color28.png" + }, + { + "id": "body30", + "name": "body30", + "url": "resources/customisation/character_color/character_color29.png" + }, + { + "id": "body31", + "name": "body31", + "url": "resources/customisation/character_color/character_color30.png" + }, + { + "id": "body32", + "name": "body32", + "url": "resources/customisation/character_color/character_color31.png" + }, + { + "id": "body33", + "name": "body33", + "url": "resources/customisation/character_color/character_color32.png" + } + ] + } + ] + }, + "eyes": { + "required": true, + "collections": [ + { + "name": "default", + "position": 0, + "textures": [ + { + "id": "eyes1", + "name": "eyes1", + "url": "resources/customisation/character_eyes/character_eyes1.png" + }, + { + "id": "eyes2", + "name": "eyes2", + "url": "resources/customisation/character_eyes/character_eyes2.png" + }, + { + "id": "eyes3", + "name": "eyes3", + "url": "resources/customisation/character_eyes/character_eyes3.png" + }, + { + "id": "eyes4", + "name": "eyes4", + "url": "resources/customisation/character_eyes/character_eyes4.png" + }, + { + "id": "eyes5", + "name": "eyes5", + "url": "resources/customisation/character_eyes/character_eyes5.png" + }, + { + "id": "eyes6", + "name": "eyes6", + "url": "resources/customisation/character_eyes/character_eyes6.png" + }, + { + "id": "eyes7", + "name": "eyes7", + "url": "resources/customisation/character_eyes/character_eyes7.png" + }, + { + "id": "eyes8", + "name": "eyes8", + "url": "resources/customisation/character_eyes/character_eyes8.png" + }, + { + "id": "eyes9", + "name": "eyes9", + "url": "resources/customisation/character_eyes/character_eyes9.png" + }, + { + "id": "eyes10", + "name": "eyes10", + "url": "resources/customisation/character_eyes/character_eyes10.png" + }, + { + "id": "eyes11", + "name": "eyes11", + "url": "resources/customisation/character_eyes/character_eyes11.png" + }, + { + "id": "eyes12", + "name": "eyes12", + "url": "resources/customisation/character_eyes/character_eyes12.png" + }, + { + "id": "eyes13", + "name": "eyes13", + "url": "resources/customisation/character_eyes/character_eyes13.png" + }, + { + "id": "eyes14", + "name": "eyes14", + "url": "resources/customisation/character_eyes/character_eyes14.png" + }, + { + "id": "eyes15", + "name": "eyes15", + "url": "resources/customisation/character_eyes/character_eyes15.png" + }, + { + "id": "eyes16", + "name": "eyes16", + "url": "resources/customisation/character_eyes/character_eyes16.png" + }, + { + "id": "eyes17", + "name": "eyes17", + "url": "resources/customisation/character_eyes/character_eyes17.png" + }, + { + "id": "eyes18", + "name": "eyes18", + "url": "resources/customisation/character_eyes/character_eyes18.png" + }, + { + "id": "eyes19", + "name": "eyes19", + "url": "resources/customisation/character_eyes/character_eyes19.png" + }, + { + "id": "eyes20", + "name": "eyes20", + "url": "resources/customisation/character_eyes/character_eyes20.png" + }, + { + "id": "eyes21", + "name": "eyes21", + "url": "resources/customisation/character_eyes/character_eyes21.png" + }, + { + "id": "eyes22", + "name": "eyes22", + "url": "resources/customisation/character_eyes/character_eyes22.png" + }, + { + "id": "eyes23", + "name": "eyes23", + "url": "resources/customisation/character_eyes/character_eyes23.png" + }, + { + "id": "eyes24", + "name": "eyes24", + "url": "resources/customisation/character_eyes/character_eyes24.png" + }, + { + "id": "eyes25", + "name": "eyes25", + "url": "resources/customisation/character_eyes/character_eyes25.png" + }, + { + "id": "eyes26", + "name": "eyes26", + "url": "resources/customisation/character_eyes/character_eyes26.png" + }, + { + "id": "eyes27", + "name": "eyes27", + "url": "resources/customisation/character_eyes/character_eyes27.png" + }, + { + "id": "eyes28", + "name": "eyes28", + "url": "resources/customisation/character_eyes/character_eyes28.png" + }, + { + "id": "eyes29", + "name": "eyes29", + "url": "resources/customisation/character_eyes/character_eyes29.png" + }, + { + "id": "eyes30", + "name": "eyes30", + "url": "resources/customisation/character_eyes/character_eyes30.png" + } + ] + } + ] + }, + "hair": { + "collections": [ + { + "name": "default", + "position": 0, + "textures": [ + { + "id": "hair1", + "name": "hair1", + "url": "resources/customisation/character_hairs/character_hairs0.png" + }, + { + "id": "hair2", + "name": "hair2", + "url": "resources/customisation/character_hairs/character_hairs1.png" + }, + { + "id": "hair3", + "name": "hair3", + "url": "resources/customisation/character_hairs/character_hairs2.png" + }, + { + "id": "hair4", + "name": "hair4", + "url": "resources/customisation/character_hairs/character_hairs3.png" + }, + { + "id": "hair5", + "name": "hair5", + "url": "resources/customisation/character_hairs/character_hairs4.png" + }, + { + "id": "hair6", + "name": "hair6", + "url": "resources/customisation/character_hairs/character_hairs5.png" + }, + { + "id": "hair7", + "name": "hair7", + "url": "resources/customisation/character_hairs/character_hairs6.png" + }, + { + "id": "hair8", + "name": "hair8", + "url": "resources/customisation/character_hairs/character_hairs7.png" + }, + { + "id": "hair9", + "name": "hair9", + "url": "resources/customisation/character_hairs/character_hairs8.png" + }, + { + "id": "hair10", + "name": "hair10", + "url": "resources/customisation/character_hairs/character_hairs9.png" + }, + { + "id": "hair11", + "name": "hair11", + "url": "resources/customisation/character_hairs/character_hairs10.png" + }, + { + "id": "hair12", + "name": "hair12", + "url": "resources/customisation/character_hairs/character_hairs11.png" + }, + { + "id": "hair13", + "name": "hair13", + "url": "resources/customisation/character_hairs/character_hairs12.png" + }, + { + "id": "hair14", + "name": "hair14", + "url": "resources/customisation/character_hairs/character_hairs13.png" + }, + { + "id": "hair15", + "name": "hair15", + "url": "resources/customisation/character_hairs/character_hairs14.png" + }, + { + "id": "hair16", + "name": "hair16", + "url": "resources/customisation/character_hairs/character_hairs15.png" + }, + { + "id": "hair17", + "name": "hair17", + "url": "resources/customisation/character_hairs/character_hairs16.png" + }, + { + "id": "hair18", + "name": "hair18", + "url": "resources/customisation/character_hairs/character_hairs17.png" + }, + { + "id": "hair19", + "name": "hair19", + "url": "resources/customisation/character_hairs/character_hairs18.png" + }, + { + "id": "hair20", + "name": "hair20", + "url": "resources/customisation/character_hairs/character_hairs19.png" + }, + { + "id": "hair21", + "name": "hair21", + "url": "resources/customisation/character_hairs/character_hairs20.png" + }, + { + "id": "hair22", + "name": "hair22", + "url": "resources/customisation/character_hairs/character_hairs21.png" + }, + { + "id": "hair23", + "name": "hair23", + "url": "resources/customisation/character_hairs/character_hairs22.png" + }, + { + "id": "hair24", + "name": "hair24", + "url": "resources/customisation/character_hairs/character_hairs23.png" + }, + { + "id": "hair25", + "name": "hair25", + "url": "resources/customisation/character_hairs/character_hairs24.png" + }, + { + "id": "hair26", + "name": "hair26", + "url": "resources/customisation/character_hairs/character_hairs25.png" + }, + { + "id": "hair27", + "name": "hair27", + "url": "resources/customisation/character_hairs/character_hairs26.png" + }, + { + "id": "hair28", + "name": "hair28", + "url": "resources/customisation/character_hairs/character_hairs27.png" + }, + { + "id": "hair29", + "name": "hair29", + "url": "resources/customisation/character_hairs/character_hairs28.png" + }, + { + "id": "hair30", + "name": "hair30", + "url": "resources/customisation/character_hairs/character_hairs29.png" + }, + { + "id": "hair31", + "name": "hair31", + "url": "resources/customisation/character_hairs/character_hairs30.png" + }, + { + "id": "hair32", + "name": "hair32", + "url": "resources/customisation/character_hairs/character_hairs31.png" + }, + { + "id": "hair33", + "name": "hair33", + "url": "resources/customisation/character_hairs/character_hairs32.png" + }, + { + "id": "hair34", + "name": "hair34", + "url": "resources/customisation/character_hairs/character_hairs33.png" + }, + { + "id": "hair35", + "name": "hair35", + "url": "resources/customisation/character_hairs/character_hairs34.png" + }, + { + "id": "hair36", + "name": "hair36", + "url": "resources/customisation/character_hairs/character_hairs35.png" + }, + { + "id": "hair37", + "name": "hair37", + "url": "resources/customisation/character_hairs/character_hairs36.png" + }, + { + "id": "hair38", + "name": "hair38", + "url": "resources/customisation/character_hairs/character_hairs37.png" + }, + { + "id": "hair39", + "name": "hair39", + "url": "resources/customisation/character_hairs/character_hairs38.png" + }, + { + "id": "hair40", + "name": "hair40", + "url": "resources/customisation/character_hairs/character_hairs39.png" + }, + { + "id": "hair41", + "name": "hair41", + "url": "resources/customisation/character_hairs/character_hairs40.png" + }, + { + "id": "hair42", + "name": "hair42", + "url": "resources/customisation/character_hairs/character_hairs41.png" + }, + { + "id": "hair43", + "name": "hair43", + "url": "resources/customisation/character_hairs/character_hairs42.png" + }, + { + "id": "hair44", + "name": "hair44", + "url": "resources/customisation/character_hairs/character_hairs43.png" + }, + { + "id": "hair45", + "name": "hair45", + "url": "resources/customisation/character_hairs/character_hairs44.png" + }, + { + "id": "hair46", + "name": "hair46", + "url": "resources/customisation/character_hairs/character_hairs45.png" + }, + { + "id": "hair47", + "name": "hair47", + "url": "resources/customisation/character_hairs/character_hairs46.png" + }, + { + "id": "hair48", + "name": "hair48", + "url": "resources/customisation/character_hairs/character_hairs47.png" + }, + { + "id": "hair49", + "name": "hair49", + "url": "resources/customisation/character_hairs/character_hairs48.png" + }, + { + "id": "hair50", + "name": "hair50", + "url": "resources/customisation/character_hairs/character_hairs49.png" + }, + { + "id": "hair51", + "name": "hair51", + "url": "resources/customisation/character_hairs/character_hairs50.png" + }, + { + "id": "hair52", + "name": "hair52", + "url": "resources/customisation/character_hairs/character_hairs51.png" + }, + { + "id": "hair53", + "name": "hair53", + "url": "resources/customisation/character_hairs/character_hairs52.png" + }, + { + "id": "hair54", + "name": "hair54", + "url": "resources/customisation/character_hairs/character_hairs53.png" + }, + { + "id": "hair55", + "name": "hair55", + "url": "resources/customisation/character_hairs/character_hairs54.png" + }, + { + "id": "hair56", + "name": "hair56", + "url": "resources/customisation/character_hairs/character_hairs55.png" + }, + { + "id": "hair57", + "name": "hair57", + "url": "resources/customisation/character_hairs/character_hairs56.png" + }, + { + "id": "hair58", + "name": "hair58", + "url": "resources/customisation/character_hairs/character_hairs57.png" + }, + { + "id": "hair59", + "name": "hair59", + "url": "resources/customisation/character_hairs/character_hairs58.png" + }, + { + "id": "hair60", + "name": "hair60", + "url": "resources/customisation/character_hairs/character_hairs59.png" + }, + { + "id": "hair61", + "name": "hair61", + "url": "resources/customisation/character_hairs/character_hairs60.png" + }, + { + "id": "hair62", + "name": "hair62", + "url": "resources/customisation/character_hairs/character_hairs61.png" + }, + { + "id": "hair63", + "name": "hair63", + "url": "resources/customisation/character_hairs/character_hairs62.png" + }, + { + "id": "hair64", + "name": "hair64", + "url": "resources/customisation/character_hairs/character_hairs63.png" + }, + { + "id": "hair65", + "name": "hair65", + "url": "resources/customisation/character_hairs/character_hairs64.png" + }, + { + "id": "hair66", + "name": "hair66", + "url": "resources/customisation/character_hairs/character_hairs65.png" + }, + { + "id": "hair67", + "name": "hair67", + "url": "resources/customisation/character_hairs/character_hairs66.png" + }, + { + "id": "hair68", + "name": "hair68", + "url": "resources/customisation/character_hairs/character_hairs67.png" + }, + { + "id": "hair69", + "name": "hair69", + "url": "resources/customisation/character_hairs/character_hairs68.png" + }, + { + "id": "hair70", + "name": "hair70", + "url": "resources/customisation/character_hairs/character_hairs69.png" + }, + { + "id": "hair71", + "name": "hair71", + "url": "resources/customisation/character_hairs/character_hairs70.png" + }, + { + "id": "hair72", + "name": "hair72", + "url": "resources/customisation/character_hairs/character_hairs71.png" + }, + { + "id": "hair73", + "name": "hair73", + "url": "resources/customisation/character_hairs/character_hairs72.png" + }, + { + "id": "hair74", + "name": "hair74", + "url": "resources/customisation/character_hairs/character_hairs73.png" + } + ] + } + ] + }, + "clothes": { + "collections": [ + { + "name": "default", + "position": 0, + "textures": [ + { + "id": "clothes1", + "name": "clothes1", + "url": "resources/customisation/character_clothes/character_clothes0.png" + }, + { + "id": "clothes2", + "name": "clothes2", + "url": "resources/customisation/character_clothes/character_clothes1.png" + }, + { + "id": "clothes3", + "name": "clothes3", + "url": "resources/customisation/character_clothes/character_clothes2.png" + }, + { + "id": "clothes4", + "name": "clothes4", + "url": "resources/customisation/character_clothes/character_clothes3.png" + }, + { + "id": "clothes5", + "name": "clothes5", + "url": "resources/customisation/character_clothes/character_clothes4.png" + }, + { + "id": "clothes6", + "name": "clothes6", + "url": "resources/customisation/character_clothes/character_clothes5.png" + }, + { + "id": "clothes7", + "name": "clothes7", + "url": "resources/customisation/character_clothes/character_clothes6.png" + }, + { + "id": "clothes8", + "name": "clothes8", + "url": "resources/customisation/character_clothes/character_clothes7.png" + }, + { + "id": "clothes9", + "name": "clothes9", + "url": "resources/customisation/character_clothes/character_clothes8.png" + }, + { + "id": "clothes10", + "name": "clothes10", + "url": "resources/customisation/character_clothes/character_clothes9.png" + }, + { + "id": "clothes11", + "name": "clothes11", + "url": "resources/customisation/character_clothes/character_clothes10.png" + }, + { + "id": "clothes12", + "name": "clothes12", + "url": "resources/customisation/character_clothes/character_clothes11.png" + }, + { + "id": "clothes13", + "name": "clothes13", + "url": "resources/customisation/character_clothes/character_clothes12.png" + }, + { + "id": "clothes14", + "name": "clothes14", + "url": "resources/customisation/character_clothes/character_clothes13.png" + }, + { + "id": "clothes15", + "name": "clothes15", + "url": "resources/customisation/character_clothes/character_clothes14.png" + }, + { + "id": "clothes16", + "name": "clothes16", + "url": "resources/customisation/character_clothes/character_clothes15.png" + }, + { + "id": "clothes17", + "name": "clothes17", + "url": "resources/customisation/character_clothes/character_clothes16.png" + }, + { + "id": "clothes18", + "name": "clothes18", + "url": "resources/customisation/character_clothes/character_clothes17.png" + }, + { + "id": "clothes19", + "name": "clothes19", + "url": "resources/customisation/character_clothes/character_clothes18.png" + }, + { + "id": "clothes20", + "name": "clothes20", + "url": "resources/customisation/character_clothes/character_clothes19.png" + }, + { + "id": "clothes21", + "name": "clothes21", + "url": "resources/customisation/character_clothes/character_clothes20.png" + }, + { + "id": "clothes22", + "name": "clothes22", + "url": "resources/customisation/character_clothes/character_clothes21.png" + }, + { + "id": "clothes23", + "name": "clothes23", + "url": "resources/customisation/character_clothes/character_clothes22.png" + }, + { + "id": "clothes24", + "name": "clothes24", + "url": "resources/customisation/character_clothes/character_clothes23.png" + }, + { + "id": "clothes25", + "name": "clothes25", + "url": "resources/customisation/character_clothes/character_clothes24.png" + }, + { + "id": "clothes26", + "name": "clothes26", + "url": "resources/customisation/character_clothes/character_clothes25.png" + }, + { + "id": "clothes27", + "name": "clothes27", + "url": "resources/customisation/character_clothes/character_clothes26.png" + }, + { + "id": "clothes28", + "name": "clothes28", + "url": "resources/customisation/character_clothes/character_clothes27.png" + }, + { + "id": "clothes29", + "name": "clothes29", + "url": "resources/customisation/character_clothes/character_clothes28.png" + }, + { + "id": "clothes30", + "name": "clothes30", + "url": "resources/customisation/character_clothes/character_clothes29.png" + }, + { + "id": "clothes31", + "name": "clothes31", + "url": "resources/customisation/character_clothes/character_clothes30.png" + }, + { + "id": "clothes32", + "name": "clothes32", + "url": "resources/customisation/character_clothes/character_clothes31.png" + }, + { + "id": "clothes33", + "name": "clothes33", + "url": "resources/customisation/character_clothes/character_clothes32.png" + }, + { + "id": "clothes34", + "name": "clothes34", + "url": "resources/customisation/character_clothes/character_clothes33.png" + }, + { + "id": "clothes35", + "name": "clothes35", + "url": "resources/customisation/character_clothes/character_clothes34.png" + }, + { + "id": "clothes36", + "name": "clothes36", + "url": "resources/customisation/character_clothes/character_clothes35.png" + }, + { + "id": "clothes37", + "name": "clothes37", + "url": "resources/customisation/character_clothes/character_clothes36.png" + }, + { + "id": "clothes38", + "name": "clothes38", + "url": "resources/customisation/character_clothes/character_clothes37.png" + }, + { + "id": "clothes39", + "name": "clothes39", + "url": "resources/customisation/character_clothes/character_clothes38.png" + }, + { + "id": "clothes40", + "name": "clothes40", + "url": "resources/customisation/character_clothes/character_clothes39.png" + }, + { + "id": "clothes41", + "name": "clothes41", + "url": "resources/customisation/character_clothes/character_clothes40.png" + }, + { + "id": "clothes42", + "name": "clothes42", + "url": "resources/customisation/character_clothes/character_clothes41.png" + }, + { + "id": "clothes43", + "name": "clothes43", + "url": "resources/customisation/character_clothes/character_clothes42.png" + }, + { + "id": "clothes44", + "name": "clothes44", + "url": "resources/customisation/character_clothes/character_clothes43.png" + }, + { + "id": "clothes45", + "name": "clothes45", + "url": "resources/customisation/character_clothes/character_clothes44.png" + }, + { + "id": "clothes46", + "name": "clothes46", + "url": "resources/customisation/character_clothes/character_clothes45.png" + }, + { + "id": "clothes47", + "name": "clothes47", + "url": "resources/customisation/character_clothes/character_clothes46.png" + }, + { + "id": "clothes48", + "name": "clothes48", + "url": "resources/customisation/character_clothes/character_clothes47.png" + }, + { + "id": "clothes49", + "name": "clothes49", + "url": "resources/customisation/character_clothes/character_clothes48.png" + }, + { + "id": "clothes50", + "name": "clothes50", + "url": "resources/customisation/character_clothes/character_clothes49.png" + }, + { + "id": "clothes51", + "name": "clothes51", + "url": "resources/customisation/character_clothes/character_clothes50.png" + }, + { + "id": "clothes52", + "name": "clothes52", + "url": "resources/customisation/character_clothes/character_clothes51.png" + }, + { + "id": "clothes53", + "name": "clothes53", + "url": "resources/customisation/character_clothes/character_clothes52.png" + }, + { + "id": "clothes54", + "name": "clothes54", + "url": "resources/customisation/character_clothes/character_clothes53.png" + }, + { + "id": "clothes55", + "name": "clothes55", + "url": "resources/customisation/character_clothes/character_clothes54.png" + }, + { + "id": "clothes56", + "name": "clothes56", + "url": "resources/customisation/character_clothes/character_clothes55.png" + }, + { + "id": "clothes57", + "name": "clothes57", + "url": "resources/customisation/character_clothes/character_clothes56.png" + }, + { + "id": "clothes58", + "name": "clothes58", + "url": "resources/customisation/character_clothes/character_clothes57.png" + }, + { + "id": "clothes59", + "name": "clothes59", + "url": "resources/customisation/character_clothes/character_clothes58.png" + }, + { + "id": "clothes60", + "name": "clothes60", + "url": "resources/customisation/character_clothes/character_clothes59.png" + }, + { + "id": "clothes61", + "name": "clothes61", + "url": "resources/customisation/character_clothes/character_clothes60.png" + }, + { + "id": "clothes62", + "name": "clothes62", + "url": "resources/customisation/character_clothes/character_clothes61.png" + }, + { + "id": "clothes63", + "name": "clothes63", + "url": "resources/customisation/character_clothes/character_clothes62.png" + }, + { + "id": "clothes64", + "name": "clothes64", + "url": "resources/customisation/character_clothes/character_clothes63.png" + }, + { + "id": "clothes65", + "name": "clothes65", + "url": "resources/customisation/character_clothes/character_clothes64.png" + }, + { + "id": "clothes66", + "name": "clothes66", + "url": "resources/customisation/character_clothes/character_clothes65.png" + }, + { + "id": "clothes67", + "name": "clothes67", + "url": "resources/customisation/character_clothes/character_clothes66.png" + }, + { + "id": "clothes68", + "name": "clothes68", + "url": "resources/customisation/character_clothes/character_clothes67.png" + }, + { + "id": "clothes69", + "name": "clothes69", + "url": "resources/customisation/character_clothes/character_clothes68.png" + }, + { + "id": "clothes70", + "name": "clothes70", + "url": "resources/customisation/character_clothes/character_clothes69.png" + }, + { + "id": "clothes_pride_shirt", + "name": "clothes_pride_shirt", + "url": "resources/customisation/character_clothes/pride_shirt.png" + }, + { + "id": "clothes_black_hoodie", + "name": "clothes_black_hoodie", + "url": "resources/customisation/character_clothes/black_hoodie.png" + }, + { + "id": "clothes_white_hoodie", + "name": "clothes_white_hoodie", + "url": "resources/customisation/character_clothes/white_hoodie.png" + }, + { + "id": "clothes_engelbert", + "name": "clothes_engelbert", + "url": "resources/customisation/character_clothes/engelbert.png" + } + ] + } + ] + }, + "hat": { + "collections": [ + { + "name": "default", + "position": 0, + "textures": [ + { + "id": "hat1", + "name": "hat1", + "url": "resources/customisation/character_hats/character_hats1.png" + }, + { + "id": "hat2", + "name": "hat2", + "url": "resources/customisation/character_hats/character_hats2.png" + }, + { + "id": "hat3", + "name": "hat3", + "url": "resources/customisation/character_hats/character_hats3.png" + }, + { + "id": "hat4", + "name": "hat4", + "url": "resources/customisation/character_hats/character_hats4.png" + }, + { + "id": "hat5", + "name": "hat5", + "url": "resources/customisation/character_hats/character_hats5.png" + }, + { + "id": "hat6", + "name": "hat6", + "url": "resources/customisation/character_hats/character_hats6.png" + }, + { + "id": "hat7", + "name": "hat7", + "url": "resources/customisation/character_hats/character_hats7.png" + }, + { + "id": "hat8", + "name": "hat8", + "url": "resources/customisation/character_hats/character_hats8.png" + }, + { + "id": "hat9", + "name": "hat9", + "url": "resources/customisation/character_hats/character_hats9.png" + }, + { + "id": "hat10", + "name": "hat10", + "url": "resources/customisation/character_hats/character_hats10.png" + }, + { + "id": "hat11", + "name": "hat11", + "url": "resources/customisation/character_hats/character_hats11.png" + }, + { + "id": "hat12", + "name": "hat12", + "url": "resources/customisation/character_hats/character_hats12.png" + }, + { + "id": "hat13", + "name": "hat13", + "url": "resources/customisation/character_hats/character_hats13.png" + }, + { + "id": "hat14", + "name": "hat14", + "url": "resources/customisation/character_hats/character_hats14.png" + }, + { + "id": "hat15", + "name": "hat15", + "url": "resources/customisation/character_hats/character_hats15.png" + }, + { + "id": "hat16", + "name": "hat16", + "url": "resources/customisation/character_hats/character_hats16.png" + }, + { + "id": "hat17", + "name": "hat17", + "url": "resources/customisation/character_hats/character_hats17.png" + }, + { + "id": "hat18", + "name": "hat18", + "url": "resources/customisation/character_hats/character_hats18.png" + }, + { + "id": "hat19", + "name": "hat19", + "url": "resources/customisation/character_hats/character_hats19.png" + }, + { + "id": "hat20", + "name": "hat20", + "url": "resources/customisation/character_hats/character_hats20.png" + }, + { + "id": "hat21", + "name": "hat21", + "url": "resources/customisation/character_hats/character_hats21.png" + }, + { + "id": "hat22", + "name": "hat22", + "url": "resources/customisation/character_hats/character_hats22.png" + }, + { + "id": "hat23", + "name": "hat23", + "url": "resources/customisation/character_hats/character_hats23.png" + }, + { + "id": "hat24", + "name": "hat24", + "url": "resources/customisation/character_hats/character_hats24.png" + }, + { + "id": "hat25", + "name": "hat25", + "url": "resources/customisation/character_hats/character_hats25.png" + }, + { + "id": "hat26", + "name": "hat26", + "url": "resources/customisation/character_hats/character_hats26.png" + }, + { + "id": "tinfoil_hat1", + "name": "tinfoil_hat1", + "url": "resources/customisation/character_hats/tinfoil_hat1.png" + } + ] + } + ] + }, + "accessory": { + "required": true, + "collections": [ + { + "name": "default", + "position": 0, + "textures": [ + { + "id": "accessory1", + "name": "accessory1", + "url": "resources/customisation/character_accessories/character_accessories1.png" + }, + { + "id": "accessory2", + "name": "accessory2", + "url": "resources/customisation/character_accessories/character_accessories2.png" + }, + { + "id": "accessory3", + "name": "accessory3", + "url": "resources/customisation/character_accessories/character_accessories3.png" + }, + { + "id": "accessory4", + "name": "accessory4", + "url": "resources/customisation/character_accessories/character_accessories4.png" + }, + { + "id": "accessory5", + "name": "accessory5", + "url": "resources/customisation/character_accessories/character_accessories5.png" + }, + { + "id": "accessory6", + "name": "accessory6", + "url": "resources/customisation/character_accessories/character_accessories6.png" + }, + { + "id": "accessory7", + "name": "accessory7", + "url": "resources/customisation/character_accessories/character_accessories7.png" + }, + { + "id": "accessory8", + "name": "accessory8", + "url": "resources/customisation/character_accessories/character_accessories8.png" + }, + { + "id": "accessory9", + "name": "accessory9", + "url": "resources/customisation/character_accessories/character_accessories9.png" + }, + { + "id": "accessory10", + "name": "accessory10", + "url": "resources/customisation/character_accessories/character_accessories10.png" + }, + { + "id": "accessory11", + "name": "accessory11", + "url": "resources/customisation/character_accessories/character_accessories11.png" + }, + { + "id": "accessory12", + "name": "accessory12", + "url": "resources/customisation/character_accessories/character_accessories12.png" + }, + { + "id": "accessory13", + "name": "accessory13", + "url": "resources/customisation/character_accessories/character_accessories13.png" + }, + { + "id": "accessory14", + "name": "accessory14", + "url": "resources/customisation/character_accessories/character_accessories14.png" + }, + { + "id": "accessory15", + "name": "accessory15", + "url": "resources/customisation/character_accessories/character_accessories15.png" + }, + { + "id": "accessory16", + "name": "accessory16", + "url": "resources/customisation/character_accessories/character_accessories16.png" + }, + { + "id": "accessory17", + "name": "accessory17", + "url": "resources/customisation/character_accessories/character_accessories17.png" + }, + { + "id": "accessory18", + "name": "accessory18", + "url": "resources/customisation/character_accessories/character_accessories18.png" + }, + { + "id": "accessory19", + "name": "accessory19", + "url": "resources/customisation/character_accessories/character_accessories19.png" + }, + { + "id": "accessory20", + "name": "accessory20", + "url": "resources/customisation/character_accessories/character_accessories20.png" + }, + { + "id": "accessory21", + "name": "accessory21", + "url": "resources/customisation/character_accessories/character_accessories21.png" + }, + { + "id": "accessory22", + "name": "accessory22", + "url": "resources/customisation/character_accessories/character_accessories22.png" + }, + { + "id": "accessory23", + "name": "accessory23", + "url": "resources/customisation/character_accessories/character_accessories23.png" + }, + { + "id": "accessory24", + "name": "accessory24", + "url": "resources/customisation/character_accessories/character_accessories24.png" + }, + { + "id": "accessory25", + "name": "accessory25", + "url": "resources/customisation/character_accessories/character_accessories25.png" + }, + { + "id": "accessory26", + "name": "accessory26", + "url": "resources/customisation/character_accessories/character_accessories26.png" + }, + { + "id": "accessory27", + "name": "accessory27", + "url": "resources/customisation/character_accessories/character_accessories27.png" + }, + { + "id": "accessory28", + "name": "accessory28", + "url": "resources/customisation/character_accessories/character_accessories28.png" + }, + { + "id": "accessory29", + "name": "accessory29", + "url": "resources/customisation/character_accessories/character_accessories29.png" + }, + { + "id": "accessory30", + "name": "accessory30", + "url": "resources/customisation/character_accessories/character_accessories30.png" + }, + { + "id": "accessory31", + "name": "accessory31", + "url": "resources/customisation/character_accessories/character_accessories31.png" + }, + { + "id": "accessory32", + "name": "accessory32", + "url": "resources/customisation/character_accessories/character_accessories32.png" + }, + { + "id": "accessory_mate_bottle", + "name": "accessory_mate_bottle", + "url": "resources/customisation/character_accessories/mate_bottle1.png" + }, + { + "id": "accessory_mask", + "name": "accessory_mask", + "url": "resources/customisation/character_accessories/mask.png" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/pusher/src/App.ts b/pusher/src/App.ts index 401e04f3..a20f40b8 100644 --- a/pusher/src/App.ts +++ b/pusher/src/App.ts @@ -6,6 +6,7 @@ import { PrometheusController } from "./Controller/PrometheusController"; import { DebugController } from "./Controller/DebugController"; import { AdminController } from "./Controller/AdminController"; import { OpenIdProfileController } from "./Controller/OpenIdProfileController"; +import { WokaListController } from "./Controller/WokaListController"; import HyperExpress from "hyper-express"; import { cors } from "./Middleware/Cors"; @@ -29,6 +30,7 @@ class App { new DebugController(webserver); new AdminController(webserver); new OpenIdProfileController(webserver); + new WokaListController(webserver); } } diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 9a081461..56613d46 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -69,7 +69,7 @@ export class AuthenticateController extends BaseHttpController { //if not nonce and code, user connected in anonymous //get data with identifier and return token if (!code && !nonce) { - return res.json(JSON.stringify({ ...resUserData, authToken: token })); + return res.json({ ...resUserData, authToken: token }); } console.error("Token cannot to be check on OpenId provider"); res.status(500); diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts new file mode 100644 index 00000000..516d9865 --- /dev/null +++ b/pusher/src/Controller/WokaListController.ts @@ -0,0 +1,47 @@ +import { hasToken } from "../Middleware/HasToken"; +import { BaseHttpController } from "./BaseHttpController"; +import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; +import { adminWokaService } from "..//Services/AdminWokaService"; +import { localWokaService } from "..//Services/LocalWokaService"; +import { WokaServiceInterface } from "src/Services/WokaServiceInterface"; +import { Server } from "hyper-express"; + +export class WokaListController extends BaseHttpController { + private wokaService: WokaServiceInterface; + + constructor(app: Server) { + super(app); + this.wokaService = ADMIN_API_URL ? adminWokaService : localWokaService; + } + + routes() { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + this.app.get("/woka-list", { middlewares: [hasToken] }, async (req, res) => { + const token = req.header("Authorization"); + const wokaList = await this.wokaService.getWokaList(token); + + if (!wokaList) { + return res.status(500).send("Error on getting woka list"); + } + + return res.status(200).json(wokaList); + }); + + // eslint-disable-next-line @typescript-eslint/no-misused-promises + this.app.post("/woka-details", async (req, res) => { + const body = await req.json(); + if (!body || !body.textureIds) { + return res.status(400); + } + + const textureIds = body.textureIds; + const wokaDetails = await this.wokaService.fetchWokaDetails(textureIds); + + if (!wokaDetails) { + return res.json({ details: [] }); + } + + return res.json(wokaDetails); + }); + } +} diff --git a/pusher/src/Enum/PlayerTextures.ts b/pusher/src/Enum/PlayerTextures.ts new file mode 100644 index 00000000..9b597cbc --- /dev/null +++ b/pusher/src/Enum/PlayerTextures.ts @@ -0,0 +1,64 @@ +import * as tg from "generic-type-guard"; + +//The list of all the player textures, both the default models and the partial textures used for customization + +export const isWokaTexture = new tg.IsInterface() + .withProperties({ + id: tg.isString, + name: tg.isString, + url: tg.isString, + position: tg.isNumber, + }) + .withOptionalProperties({ + tags: tg.isArray(tg.isString), + tintable: tg.isBoolean, + }) + .get(); + +export type WokaTexture = tg.GuardedType; + +export const isWokaTextureCollection = new tg.IsInterface() + .withProperties({ + name: tg.isString, + position: tg.isNumber, + textures: tg.isArray(isWokaTexture), + }) + .get(); + +export type WokaTextureCollection = tg.GuardedType; + +export const isWokaPartType = new tg.IsInterface() + .withProperties({ + collections: tg.isArray(isWokaTextureCollection), + }) + .withOptionalProperties({ + required: tg.isBoolean, + }) + .get(); + +export type WokaPartType = tg.GuardedType; + +export const isWokaList = new tg.IsInterface().withStringIndexSignature(isWokaPartType).get(); + +export type WokaList = tg.GuardedType; + +export const wokaPartNames = ["woka", "body", "eyes", "hair", "clothes", "hat", "accessory"]; + +export const isWokaDetail = new tg.IsInterface() + .withProperties({ + id: tg.isString, + }) + .withOptionalProperties({ + texture: tg.isString, + }) + .get(); + +export type WokaDetail = tg.GuardedType; + +export const isWokaDetailsResult = new tg.IsInterface() + .withProperties({ + details: tg.isArray(isWokaDetail), + }) + .get(); + +export type WokaDetailsResult = tg.GuardedType; diff --git a/pusher/src/Middleware/HasToken.ts b/pusher/src/Middleware/HasToken.ts new file mode 100644 index 00000000..4b06419c --- /dev/null +++ b/pusher/src/Middleware/HasToken.ts @@ -0,0 +1,16 @@ +import Request from "hyper-express/types/components/http/Request"; +import Response from "hyper-express/types/components/http/Response"; +import { MiddlewareNext, MiddlewarePromise } from "hyper-express/types/components/router/Router"; + +export function hasToken(req: Request, res: Response, next?: MiddlewareNext): MiddlewarePromise { + const authorizationHeader = req.header("Authorization"); + + if (!authorizationHeader) { + res.status(401).send("Undefined authorization header"); + return; + } + + if (next) { + next(); + } +} diff --git a/pusher/src/Services/AdminWokaService.ts b/pusher/src/Services/AdminWokaService.ts new file mode 100644 index 00000000..0598fb2a --- /dev/null +++ b/pusher/src/Services/AdminWokaService.ts @@ -0,0 +1,71 @@ +import axios from "axios"; +import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable"; +import { isWokaDetailsResult, isWokaList, WokaDetailsResult, WokaList } from "../Enum/PlayerTextures"; +import { WokaServiceInterface } from "./WokaServiceInterface"; + +class AdminWokaService implements WokaServiceInterface { + /** + * Returns the list of all available Wokas for the current user. + */ + getWokaList(token: string): Promise { + return axios + .get(`${ADMIN_API_URL}/api/woka-list/${token}`, { + headers: { Authorization: `${ADMIN_API_TOKEN}` }, + }) + .then((res) => { + if (isWokaList(res.data)) { + throw new Error("Bad response format provided by woka list endpoint"); + } + return res.data; + }) + .catch((err) => { + console.error(`Cannot get woka list from admin API with token: ${token}`, err); + return undefined; + }); + } + + /** + * Returns the URL of all the images for the given texture ids. + * + * Key: texture id + * Value: URL + * + * If one of the textures cannot be found, undefined is returned + */ + fetchWokaDetails(textureIds: string[]): Promise { + return axios + .post( + `${ADMIN_API_URL}/api/woka-details`, + { + textureIds, + }, + { + headers: { Authorization: `${ADMIN_API_TOKEN}` }, + } + ) + .then((res) => { + if (isWokaDetailsResult(res.data)) { + throw new Error("Bad response format provided by woka detail endpoint"); + } + + const result: WokaDetailsResult = res.data; + if (result.details.length !== textureIds.length) { + return undefined; + } + + for (const detail of result.details) { + if (!detail.texture) { + return undefined; + } + } + + return res.data; + }) + .catch((err) => { + console.error(`Cannot get woka details from admin API with ids: ${textureIds}`, err); + return undefined; + }); + } +} + +export const adminWokaService = new AdminWokaService(); diff --git a/pusher/src/Services/LocalWokaService.ts b/pusher/src/Services/LocalWokaService.ts new file mode 100644 index 00000000..3356c48d --- /dev/null +++ b/pusher/src/Services/LocalWokaService.ts @@ -0,0 +1,64 @@ +import { WokaDetail, WokaDetailsResult, WokaList, wokaPartNames } from "../Enum/PlayerTextures"; +import { WokaServiceInterface } from "./WokaServiceInterface"; + +class LocalWokaService implements WokaServiceInterface { + /** + * Returns the list of all available Wokas & Woka Parts for the current user. + */ + async getWokaList(token: string): Promise { + const wokaData: WokaList = await require("../../data/woka.json"); + if (!wokaData) { + return undefined; + } + return wokaData; + } + + /** + * Returns the URL of all the images for the given texture ids. + * + * Key: texture id + * Value: URL + * + * If one of the textures cannot be found, undefined is returned (and the user should be redirected to Woka choice page!) + */ + async fetchWokaDetails(textureIds: string[]): Promise { + const wokaData: WokaList = await require("../../data/woka.json"); + const textures = new Map(); + const searchIds = new Set(textureIds); + + for (const part of wokaPartNames) { + const wokaPartType = wokaData[part]; + if (!wokaPartType) { + continue; + } + + for (const collection of wokaPartType.collections) { + for (const id of searchIds) { + const texture = collection.textures.find((texture) => texture.id === id); + + if (texture) { + textures.set(id, texture.url); + searchIds.delete(id); + } + } + } + } + + if (textureIds.length !== textures.size) { + return undefined; + } + + const details: WokaDetail[] = []; + + textures.forEach((value, key) => { + details.push({ + id: key, + texture: value, + }); + }); + + return { details }; + } +} + +export const localWokaService = new LocalWokaService(); diff --git a/pusher/src/Services/WokaServiceInterface.ts b/pusher/src/Services/WokaServiceInterface.ts new file mode 100644 index 00000000..71ee7202 --- /dev/null +++ b/pusher/src/Services/WokaServiceInterface.ts @@ -0,0 +1,18 @@ +import { WokaDetailsResult, WokaList } from "../Enum/PlayerTextures"; + +export interface WokaServiceInterface { + /** + * Returns the list of all available Wokas for the current user. + */ + getWokaList(token: string): Promise; + + /** + * Returns the URL of all the images for the given texture ids. + * + * Key: texture id + * Value: URL + * + * If one of the textures cannot be found, undefined is returned (and the user should be redirected to Woka choice page!) + */ + fetchWokaDetails(textureIds: string[]): Promise; +} From 80761804a774f803f34540482a1da0c369b5eb06 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 22 Feb 2022 14:32:47 +0100 Subject: [PATCH 03/61] getting textures urls from pusher --- front/src/Phaser/Entity/Character.ts | 2 +- front/src/Phaser/Entity/PlayerTextures.ts | 505 +++--------------- .../Entity/PlayerTexturesLoadingManager.ts | 12 +- front/src/Phaser/Login/EntryScene.ts | 21 +- pusher/src/Controller/WokaListController.ts | 2 +- pusher/src/Middleware/Cors.ts | 7 +- 6 files changed, 112 insertions(+), 437 deletions(-) diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index d1fc91c7..fa12ae96 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -10,7 +10,7 @@ import type { GameScene } from "../Game/GameScene"; import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes"; import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js"; import { isSilentStore } from "../../Stores/MediaStore"; -import { lazyLoadPlayerCharacterTextures, loadAllDefaultModels } from "./PlayerTexturesLoadingManager"; +import { lazyLoadPlayerCharacterTextures } from "./PlayerTexturesLoadingManager"; import { TexturesHelper } from "../Helpers/TexturesHelper"; import type { PictureStore } from "../../Stores/PictureStore"; import { Unsubscriber, Writable, writable } from "svelte/store"; diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index d4376c66..9c820681 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -10,440 +10,93 @@ export interface BodyResourceDescriptionInterface { 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" }, +enum PlayerTexturesKey { + Accessory = "accessory", + Body = "body", + Clothes = "clothes", + Eyes = "eyes", + Hair = "hair", + Hat = "hat", + Woka = "woka", +} - 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" }, -}; +type PlayerTexturesMetadata = Record; -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" }, -}; +interface PlayerTexturesCategory { + collections: PlayerTexturesCollection[]; + required?: boolean; +} -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" }, -}; +interface PlayerTexturesCollection { + name: string; + position: number; + textures: PlayerTexturesRecord[]; +} -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" }, -}; +interface PlayerTexturesRecord { + id: string; + name: string; + url: string; +} -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 class PlayerTextures { + public static PLAYER_RESOURCES: BodyResourceDescriptionListInterface; + public static COLOR_RESOURCES: BodyResourceDescriptionListInterface; + public static EYES_RESOURCES: BodyResourceDescriptionListInterface; + public static HAIR_RESOURCES: BodyResourceDescriptionListInterface; + public static CLOTHES_RESOURCES: BodyResourceDescriptionListInterface; + public static HATS_RESOURCES: BodyResourceDescriptionListInterface; + public static ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface; + public static LAYERS: BodyResourceDescriptionListInterface[]; -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" }, -}; + public static loadPlayerTexturesMetadata(url: string): Promise { + return new Promise((resolve, reject) => { + fetch(url, { + method: "GET", + headers: { + Host: "pusher.workadventure.localhost", + }, + }) + .then((response) => response.json()) + .then((data: PlayerTexturesMetadata) => { + this.mapTexturesMetadataIntoResources(data); + resolve(true); + }) + .catch((reason) => { + reject(reason); + }); + }); + } -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" }, -}; + private static mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { + this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); + this.COLOR_RESOURCES = this.getMappedResources(metadata.body); + this.EYES_RESOURCES = this.getMappedResources(metadata.eyes); + this.HAIR_RESOURCES = this.getMappedResources(metadata.hair); + this.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); + this.HATS_RESOURCES = this.getMappedResources(metadata.hat); + this.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); -export const LAYERS: BodyResourceDescriptionListInterface[] = [ - COLOR_RESOURCES, - EYES_RESOURCES, - HAIR_RESOURCES, - CLOTHES_RESOURCES, - HATS_RESOURCES, - ACCESSORIES_RESOURCES, -]; + this.LAYERS = [ + this.COLOR_RESOURCES, + this.EYES_RESOURCES, + this.HAIR_RESOURCES, + this.CLOTHES_RESOURCES, + this.HATS_RESOURCES, + this.ACCESSORIES_RESOURCES, + ]; + } + + private static getMappedResources(category: PlayerTexturesCategory): BodyResourceDescriptionListInterface { + const resources: BodyResourceDescriptionListInterface = {}; + for (const collection of category.collections) { + for (const texture of collection.textures) { + resources[texture.id] = { name: texture.name, img: texture.url }; + } + } + return resources; + } +} export const OBJECTS: BodyResourceDescriptionInterface[] = [ { name: "teleportation", img: "resources/objects/teleportation.png" }, diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 40e68427..39be830d 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -1,6 +1,6 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import type { CharacterTexture } from "../../Connexion/LocalUser"; -import { BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES } from "./PlayerTextures"; +import { BodyResourceDescriptionInterface, PlayerTextures } from "./PlayerTextures"; import CancelablePromise from "cancelable-promise"; export interface FrameConfig { @@ -10,7 +10,7 @@ export interface FrameConfig { export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => { const returnArray: BodyResourceDescriptionInterface[][] = []; - LAYERS.forEach((layer) => { + PlayerTextures.LAYERS.forEach((layer) => { const layerArray: BodyResourceDescriptionInterface[] = []; Object.values(layer).forEach((textureDescriptor) => { layerArray.push(textureDescriptor); @@ -21,7 +21,7 @@ export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterf return returnArray; }; export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => { - const returnArray = Object.values(PLAYER_RESOURCES); + const returnArray = Object.values(PlayerTextures.PLAYER_RESOURCES); returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => { load.spritesheet(playerResource.name, playerResource.img, { frameWidth: 32, frameHeight: 32 }); }); @@ -84,11 +84,11 @@ export const getRessourceDescriptor = ( return textureKey; } const textureName: string = typeof textureKey === "string" ? textureKey : textureKey.name; - const playerResource = PLAYER_RESOURCES[textureName]; + const playerResource = PlayerTextures.PLAYER_RESOURCES[textureName]; if (playerResource !== undefined) return playerResource; - for (let i = 0; i < LAYERS.length; i++) { - const playerResource = LAYERS[i][textureName]; + for (let i = 0; i < PlayerTextures.LAYERS.length; i++) { + const playerResource = PlayerTextures.LAYERS[i][textureName]; if (playerResource !== undefined) return playerResource; } throw new Error("Could not find a data for texture " + textureName); diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index f3ab3a08..f560f44f 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -7,6 +7,7 @@ import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene"; import LL from "../../i18n/i18n-svelte"; import { get } from "svelte/store"; import { localeDetector } from "../../i18n/locales"; +import { PlayerTextures } from "../Entity/PlayerTextures"; export const EntrySceneName = "EntryScene"; @@ -15,6 +16,9 @@ export const EntrySceneName = "EntryScene"; * and to route to the next correct scene. */ export class EntryScene extends Scene { + private texturesMetadataLoaded: boolean = false; + private localeLoaded: boolean = false; + constructor() { super({ key: EntrySceneName, @@ -27,9 +31,24 @@ export class EntryScene extends Scene { // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap this.load.bitmapFont(ReconnectingTextures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 }); + + void PlayerTextures.loadPlayerTexturesMetadata("http://pusher.workadventure.localhost/woka-list").then( + (success) => { + this.texturesMetadataLoaded = success; + } + ); } - create() { + create() {} + + public update(): void { + if (this.texturesMetadataLoaded && !this.localeLoaded) { + this.localeLoaded = true; + this.loadLocale(); + } + } + + private loadLocale(): void { localeDetector() .then(() => { gameManager diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts index 516d9865..851aa341 100644 --- a/pusher/src/Controller/WokaListController.ts +++ b/pusher/src/Controller/WokaListController.ts @@ -16,7 +16,7 @@ export class WokaListController extends BaseHttpController { routes() { // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.app.get("/woka-list", { middlewares: [hasToken] }, async (req, res) => { + this.app.get("/woka-list", {}, async (req, res) => { const token = req.header("Authorization"); const wokaList = await this.wokaService.getWokaList(token); diff --git a/pusher/src/Middleware/Cors.ts b/pusher/src/Middleware/Cors.ts index 29c8379d..876ac3a4 100644 --- a/pusher/src/Middleware/Cors.ts +++ b/pusher/src/Middleware/Cors.ts @@ -4,9 +4,12 @@ import { MiddlewareNext, MiddlewarePromise } from "hyper-express/types/component import { FRONT_URL } from "../Enum/EnvironmentVariable"; export function cors(req: Request, res: Response, next?: MiddlewareNext): MiddlewarePromise { - res.setHeader("access-control-allow-headers", "Origin, X-Requested-With, Content-Type, Accept"); + res.setHeader( + "access-control-allow-headers", + "Origin, X-Requested-With, Content-Type, Accept, Authorization, Pragma, Cache-Control" + ); res.setHeader("access-control-allow-methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); - res.setHeader("access-control-allow-origin", FRONT_URL); + res.setHeader("access-control-allow-origin", "*"); if (next) { next(); From 3b4f06d659aba0a42211a2ef66dbfe2eed766a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 21 Feb 2022 19:05:56 +0100 Subject: [PATCH 04/61] Adding OpenAPI documentation for the pusher. The pusher now exposes a "/openapi" endpoint and a "/swagger-ui/" endpoint. --- docker-compose.yaml | 1 + docs/dev/README.md | 5 + pusher/package.json | 6 +- pusher/src/App.ts | 5 + pusher/src/Controller/AdminController.ts | 55 ++++++ .../src/Controller/AuthenticateController.ts | 167 +++++++++++++++++- pusher/src/Controller/MapController.ts | 88 +++++++++ pusher/src/Controller/SwaggerController.ts | 63 +++++++ pusher/src/Enum/EnvironmentVariable.ts | 3 + pusher/yarn.lock | 160 ++++++++++++++++- 10 files changed, 547 insertions(+), 6 deletions(-) create mode 100644 pusher/src/Controller/SwaggerController.ts diff --git a/docker-compose.yaml b/docker-compose.yaml index 2bbc6c0a..86b79c15 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -86,6 +86,7 @@ services: OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS + ENABLE_OPENAPI_ENDPOINT: "true" volumes: - ./pusher:/usr/src/app labels: diff --git a/docs/dev/README.md b/docs/dev/README.md index 4a0fdbcc..fae0ffd0 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -16,3 +16,8 @@ Check out the [contributing guide](../../CONTRIBUTING.md) - [How to add translations](how-to-translate.md) - [How to add new functions in the scripting API](contributing-to-scripting-api.md) - [About Wokas](wokas.md) + +## Pusher documentation + +The Pusher is exposing its HTTP API as "OpenAPI" endpoint. +You can browse this API at `http://pusher.workadventure.localhost/swagger-ui/`. diff --git a/pusher/package.json b/pusher/package.json index 8c7d3016..fc2d1bf0 100644 --- a/pusher/package.json +++ b/pusher/package.json @@ -62,13 +62,17 @@ "@types/jasmine": "^3.5.10", "@types/jsonwebtoken": "^8.3.8", "@types/mkdirp": "^1.0.1", + "@types/swagger-jsdoc": "^6.0.1", "@types/uuidv4": "^5.0.0", "@typescript-eslint/eslint-plugin": "^2.26.0", "@typescript-eslint/parser": "^2.26.0", "eslint": "^6.8.0", "jasmine": "^3.5.0", "lint-staged": "^11.0.0", + "live-directory": "^2.3.2", "prettier": "^2.3.1", + "swagger-jsdoc": "^6.1.0", + "swagger-ui-dist": "^4.5.1", "ts-node-dev": "^1.1.8", "typescript": "^4.5.2" }, @@ -77,4 +81,4 @@ "prettier --write" ] } -} \ No newline at end of file +} diff --git a/pusher/src/App.ts b/pusher/src/App.ts index a20f40b8..f63bca1e 100644 --- a/pusher/src/App.ts +++ b/pusher/src/App.ts @@ -7,8 +7,10 @@ import { DebugController } from "./Controller/DebugController"; import { AdminController } from "./Controller/AdminController"; import { OpenIdProfileController } from "./Controller/OpenIdProfileController"; import { WokaListController } from "./Controller/WokaListController"; +import { SwaggerController } from "./Controller/SwaggerController"; import HyperExpress from "hyper-express"; import { cors } from "./Middleware/Cors"; +import { ENABLE_OPENAPI_ENDPOINT } from "./Enum/EnvironmentVariable"; class App { public app: HyperExpress.compressors.TemplatedApp; @@ -31,6 +33,9 @@ class App { new AdminController(webserver); new OpenIdProfileController(webserver); new WokaListController(webserver); + if (ENABLE_OPENAPI_ENDPOINT) { + new SwaggerController(webserver); + } } } diff --git a/pusher/src/Controller/AdminController.ts b/pusher/src/Controller/AdminController.ts index 0512ded3..7b288ab2 100644 --- a/pusher/src/Controller/AdminController.ts +++ b/pusher/src/Controller/AdminController.ts @@ -13,6 +13,27 @@ export class AdminController extends BaseHttpController { this.receiveRoomEditionPrompt(); } + /** + * @openapi + * /room/refresh: + * post: + * description: Forces anyone out of the room. The request must be authenticated with the "admin-token" header. + * parameters: + * - name: "admin-token" + * in: "header" + * required: true + * type: "string" + * description: TODO - move this to a classic "Authorization" header! + * - name: "roomId" + * in: "body" + * description: "The ID (full URL) to the room" + * required: true + * type: "string" + * responses: + * 200: + * description: Will always return "ok". + * example: "ok" + */ receiveRoomEditionPrompt() { // eslint-disable-next-line @typescript-eslint/no-misused-promises this.app.post("/room/refresh", { middlewares: [adminToken] }, async (req, res) => { @@ -43,6 +64,40 @@ export class AdminController extends BaseHttpController { }); } + /** + * @openapi + * /message: + * post: + * description: Sends a message (or a world full message) to a number of rooms. + * parameters: + * - name: "admin-token" + * in: "header" + * required: true + * type: "string" + * description: TODO - move this to a classic "Authorization" header! + * - name: "text" + * in: "body" + * description: "The text of the message" + * required: true + * type: "string" + * - name: "type" + * in: "body" + * description: Either "capacity" or "message + * required: true + * type: "string" + * - name: "targets" + * in: "body" + * description: The list of room IDs to target + * required: true + * type: array + * items: + * type: string + * example: "https://play.workadventu.re/@/foo/bar/baz" + * responses: + * 200: + * description: Will always return "ok". + * example: "ok" + */ receiveGlobalMessagePrompt() { // eslint-disable-next-line @typescript-eslint/no-misused-promises this.app.post("/message", { middlewares: [adminToken] }, async (req, res) => { diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 56613d46..f6d8e41f 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -21,6 +21,37 @@ export class AuthenticateController extends BaseHttpController { } openIDLogin() { + /** + * @openapi + * /login-screen: + * get: + * description: Redirects the user to the OpenID login screen + * parameters: + * - name: "nonce" + * in: "query" + * description: "todo" + * required: true + * type: "string" + * - name: "state" + * in: "query" + * description: "todo" + * required: true + * type: "string" + * - name: "playUri" + * in: "query" + * description: "todo" + * required: false + * type: "string" + * - name: "redirect" + * in: "query" + * description: "todo" + * required: false + * type: "string" + * responses: + * 302: + * description: Redirects the user to the OpenID login screen + * + */ //eslint-disable-next-line @typescript-eslint/no-misused-promises this.app.get("/login-screen", async (req, res) => { try { @@ -47,6 +78,37 @@ export class AuthenticateController extends BaseHttpController { } openIDCallback() { + /** + * @openapi + * /login-callback: + * get: + * description: TODO + * parameters: + * - name: "nonce" + * in: "query" + * description: "todo" + * required: true + * type: "string" + * - name: "state" + * in: "query" + * description: "todo" + * required: true + * type: "string" + * - name: "playUri" + * in: "query" + * description: "todo" + * required: false + * type: "string" + * - name: "redirect" + * in: "query" + * description: "todo" + * required: false + * type: "string" + * responses: + * 200: + * description: TODO + * + */ //eslint-disable-next-line @typescript-eslint/no-misused-promises this.app.get("/login-callback", async (req, res) => { const IPAddress = req.header("x-forwarded-for"); @@ -112,6 +174,22 @@ export class AuthenticateController extends BaseHttpController { } }); + /** + * @openapi + * /logout-callback: + * get: + * description: TODO + * parameters: + * - name: "token" + * in: "query" + * description: "todo" + * required: false + * type: "string" + * responses: + * 200: + * description: TODO + * + */ // eslint-disable-next-line @typescript-eslint/no-misused-promises this.app.get("/logout-callback", async (req, res) => { const { token } = parse(req.path_query); @@ -130,7 +208,56 @@ export class AuthenticateController extends BaseHttpController { }); } - //Try to login with an admin token + /** + * @openapi + * /register: + * post: + * description: Try to login with an admin token + * parameters: + * - name: "organizationMemberToken" + * in: "body" + * description: "A token allowing a user to connect to a given world" + * required: true + * type: "string" + * responses: + * 200: + * description: The details of the logged user + * content: + * application/json: + * schema: + * type: object + * properties: + * authToken: + * type: string + * description: A unique identification JWT token + * userUuid: + * type: string + * description: Unique user ID + * email: + * type: string|null + * description: The email of the user + * example: john.doe@example.com + * roomUrl: + * type: string + * description: The room URL to connect to + * example: https://play.workadventu.re/@/foo/bar/baz + * organizationMemberToken: + * type: string|null + * description: TODO- unclear. It seems to be sent back from the request? + * example: ??? + * mapUrlStart: + * type: string + * description: TODO- unclear. I cannot find any use of this + * example: ??? + * textures: + * type: string + * description: TODO - document this is still needed + * example: ??? + * messages: + * type: array + * description: The list of messages to be displayed when the user logs? + * example: ??? + */ private register() { this.app.post("/register", (req, res) => { (async () => { @@ -166,7 +293,28 @@ export class AuthenticateController extends BaseHttpController { }); } - //permit to login on application. Return token to connect on Websocket IO. + /** + * @openapi + * /anonymLogin: + * post: + * description: Generates an "anonymous" JWT token allowing to connect to WorkAdventure anonymously. + * responses: + * 200: + * description: The details of the logged user + * content: + * application/json: + * schema: + * type: object + * properties: + * authToken: + * type: string + * description: A unique identification JWT token + * userUuid: + * type: string + * description: Unique user ID + * 403: + * description: Anonymous login is disabled at the configuration level (environment variable DISABLE_ANONYMOUS = true) + */ private anonymLogin() { this.app.post("/anonymLogin", (req, res) => { if (DISABLE_ANONYMOUS) { @@ -183,6 +331,21 @@ export class AuthenticateController extends BaseHttpController { }); } + /** + * @openapi + * /profile-callback: + * get: + * description: ??? + * parameters: + * - name: "token" + * in: "query" + * description: "A JWT authentication token ???" + * required: true + * type: "string" + * responses: + * 302: + * description: Redirects the user to the profile screen of the admin + */ profileCallback() { // eslint-disable-next-line @typescript-eslint/no-misused-promises this.app.get("/profile-callback", async (req, res) => { diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index cf86c1b1..1e709cc0 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -10,6 +10,94 @@ import { BaseHttpController } from "./BaseHttpController"; export class MapController extends BaseHttpController { // Returns a map mapping map name to file name of the map routes() { + /** + * @openapi + * /map: + * get: + * description: Returns a map mapping map name to file name of the map + * produces: + * - "application/json" + * parameters: + * - name: "playUri" + * in: "query" + * description: "The full URL of WorkAdventure to load this map" + * required: true + * type: "string" + * - name: "authToken" + * in: "query" + * description: "The authentication token" + * required: true + * type: "string" + * responses: + * 200: + * description: The details of the map + * content: + * application/json: + * schema: + * type: object + * required: + * - mapUrl + * - policy_type + * - tags + * - textures + * - authenticationMandatory + * - roomSlug + * - contactPage + * - group + * properties: + * mapUrl: + * type: string + * description: The full URL to the JSON map file + * example: https://myuser.github.io/myrepo/map.json + * policy_type: + * type: integer + * description: ANONYMOUS_POLICY = 1, MEMBERS_ONLY_POLICY = 2, USE_TAGS_POLICY= 3 + * example: 1 + * tags: + * type: array + * description: The list of tags required to enter this room + * items: + * type: string + * example: speaker + * textures: + * type: array + * description: The list of public textures for this map (TODO remove this) + * items: + * type: object + * properties: + * todo: + * type: string + * authenticationMandatory: + * type: boolean|null + * description: Whether the authentication is mandatory or not for this map. + * example: true + * roomSlug: + * type: string + * description: The slug of the room + * deprecated: true + * example: foo + * contactPage: + * type: string|null + * description: The URL to the contact page + * example: https://mycompany.com/contact-us + * group: + * type: string|null + * description: The group this room is part of (maps the notion of "world" in WorkAdventure SAAS) + * example: myorg/myworld + * iframeAuthentication: + * type: string|null + * description: The URL of the authentication Iframe + * example: https://mycompany.com/authc + * expireOn: + * type: string|undefined + * description: The date (in ISO 8601 format) at which the room will expire + * example: 2022-11-05T08:15:30-05:00 + * canReport: + * type: boolean|undefined + * description: Whether the "report" feature is enabled or not on this room + * example: true + * + */ this.app.get("/map", (req, res) => { const query = parse(req.path_query); if (typeof query.playUri !== "string") { diff --git a/pusher/src/Controller/SwaggerController.ts b/pusher/src/Controller/SwaggerController.ts new file mode 100644 index 00000000..616247ed --- /dev/null +++ b/pusher/src/Controller/SwaggerController.ts @@ -0,0 +1,63 @@ +import swaggerJsdoc from "swagger-jsdoc"; +import { BaseHttpController } from "./BaseHttpController"; +// @ts-ignore +import LiveDirectory from "live-directory"; +import * as fs from "fs"; + +export class SwaggerController extends BaseHttpController { + routes() { + this.app.get("/openapi", (req, res) => { + const options = { + swaggerDefinition: { + openapi: "3.0.0", + info: { + title: "WorkAdventure Pusher", + version: "1.0.0", + }, + }, + apis: ["./src/Controller/*.ts"], + }; + + res.json(swaggerJsdoc(options)); + }); + + // Create a LiveDirectory instance to virtualize directory with our assets + const LiveAssets = new LiveDirectory({ + path: __dirname + "/../../node_modules/swagger-ui-dist", // We want to provide the system path to the folder. Avoid using relative paths. + keep: { + extensions: [".css", ".js", ".json", ".png", ".jpg", ".jpeg", ".html"], // We only want to serve files with these extensions + }, + ignore: (path: string) => { + return path.startsWith("."); // We want to ignore dotfiles for safety + }, + }); + + // Create static serve route to serve index.html + this.app.get("/swagger-ui/", (request, response) => { + fs.readFile(__dirname + "/../../node_modules/swagger-ui-dist/index.html", "utf8", function (err, data) { + if (err) { + return response.status(500).send(err.message); + } + const result = data.replace(/https:\/\/petstore.swagger.io\/v2\/swagger.json/g, "/openapi"); + + response.send(result); + + return; + }); + }); + + // Create static serve route to serve frontend assets + this.app.get("/swagger-ui/*", (request, response) => { + // Strip away '/assets' from the request path to get asset relative path + // Lookup LiveFile instance from our LiveDirectory instance. + const path = request.path.replace("/swagger-ui", ""); + const file = LiveAssets.get(path); + + // Return a 404 if no asset/file exists on the derived path + if (file === undefined) return response.status(404).send(""); + + // Set appropriate mime-type and serve file buffer as response body + return response.type(file.extension).send(file.buffer); + }); + } +} diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index b3415a82..5cf521f8 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -20,6 +20,9 @@ export const OPID_CLIENT_REDIRECT_URL = process.env.OPID_CLIENT_REDIRECT_URL || export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile"; export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true"; +// If set to the string "true", the /openapi route will return the OpenAPI definition and the swagger-ui/ route will display the documentation +export const ENABLE_OPENAPI_ENDPOINT = process.env.ENABLE_OPENAPI_ENDPOINT === "true"; + export { SECRET_KEY, API_URL, diff --git a/pusher/yarn.lock b/pusher/yarn.lock index f5f2884d..9254be50 100644 --- a/pusher/yarn.lock +++ b/pusher/yarn.lock @@ -2,6 +2,38 @@ # yarn lockfile v1 +"@apidevtools/json-schema-ref-parser@^9.0.6": + version "9.0.9" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b" + integrity sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.6" + call-me-maybe "^1.0.1" + js-yaml "^4.1.0" + +"@apidevtools/openapi-schemas@^2.0.4": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17" + integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== + +"@apidevtools/swagger-methods@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" + integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== + +"@apidevtools/swagger-parser@10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.2.tgz#f4145afb7c3a3bafe0376f003b5c3bdeae17a952" + integrity sha512-JFxcEyp8RlNHgBCE98nwuTkZT6eNFPc1aosWV6wPcQph72TSEEu1k3baJD4/x1qznU+JiDdz8F5pTwabZh+Dhg== + dependencies: + "@apidevtools/json-schema-ref-parser" "^9.0.6" + "@apidevtools/openapi-schemas" "^2.0.4" + "@apidevtools/swagger-methods" "^3.0.2" + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + z-schema "^4.2.3" + "@babel/code-frame@^7.0.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" @@ -23,6 +55,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + "@mapbox/node-pre-gyp@^1.0.4": version "1.0.8" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz#32abc8a5c624bc4e46c43d84dfb8b26d33a96f58" @@ -153,7 +190,7 @@ resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.10.3.tgz#a89798b3d5a8bd23ca56e855a9aee3e5a93bdaaa" integrity sha512-SWyMrjgdAUHNQmutvDcKablrJhkDLy4wunTme8oYLjKp41GnHGxMRXr2MQMvy/qy8H3LdzwQk9gH4hZ6T++H8g== -"@types/json-schema@^7.0.3": +"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.6": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== @@ -244,6 +281,11 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/swagger-jsdoc@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.1.tgz#94a99aca0356cb64ad2a6eb903ed034703453801" + integrity sha512-+MUpcbyxD528dECUBCEVm6abNuORdbuGjbrUdHDeAQ+rkPuo2a+L4N02WJHF3bonSSE6SJ3dUJwF2V6+cHnf0w== + "@types/uuid@8.3.1": version "8.3.1" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" @@ -421,6 +463,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + ascli@~1: version "1.0.1" resolved "https://registry.yarnpkg.com/ascli/-/ascli-1.0.1.tgz#bcfa5974a62f18e81cabaeb49732ab4a88f906bc" @@ -518,6 +565,11 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -550,7 +602,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^3.5.1: +chokidar@^3.5.1, chokidar@^3.5.2: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -665,6 +717,16 @@ colour@~0.7.1: resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g= +commander@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" + integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== + +commander@^2.7.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + commander@^8.2.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -775,7 +837,7 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -doctrine@^3.0.0: +doctrine@3.0.0, doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== @@ -945,6 +1007,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -1100,6 +1167,18 @@ glob-parent@^5.0.0, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.5, glob@^7.1.3, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -1398,6 +1477,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -1512,6 +1598,14 @@ listr2@^3.12.2: through "^2.3.8" wrap-ansi "^7.0.0" +live-directory@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/live-directory/-/live-directory-2.3.2.tgz#17945a1386ed439af24228c802f6546c0fa85a9b" + integrity sha512-s/QBuRkngjzUU8kVkrklqT/2/je4GYE45HiVZ8WwFNTvswXknlsC5vdgv4ycOrL/76CBMjrG7rySFpX8nX80gg== + dependencies: + chokidar "^3.5.2" + etag "^1.8.1" + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -1522,6 +1616,11 @@ lodash.clone@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y= +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -1532,6 +1631,11 @@ lodash.isboolean@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" @@ -1552,6 +1656,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -2329,6 +2438,30 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +swagger-jsdoc@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/swagger-jsdoc/-/swagger-jsdoc-6.1.0.tgz#c2b86321f2c4dde8947b418fe8a4bc94431d5522" + integrity sha512-xgep5M8Gq31MxpCbQLvJZpNqHfGPfI+sILCzujZbEXIQp2COtkZgoGASs0gacRs4xHmLDH+GuMGdorPITSG4tA== + dependencies: + commander "6.2.0" + doctrine "3.0.0" + glob "7.1.6" + lodash.mergewith "^4.6.2" + swagger-parser "10.0.2" + yaml "2.0.0-1" + +swagger-parser@10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/swagger-parser/-/swagger-parser-10.0.2.tgz#d7f18faa09c9c145e938977c9bd6c3435998b667" + integrity sha512-9jHkHM+QXyLGFLk1DkXBwV+4HyNm0Za3b8/zk/+mjr8jgOSiqm3FOTHBSDsBjtn9scdL+8eWcHdupp2NLM8tDw== + dependencies: + "@apidevtools/swagger-parser" "10.0.2" + +swagger-ui-dist@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-4.5.1.tgz#3a71f053784ad3b781e7454743064ce3fa7522b4" + integrity sha512-52iaT+VRsT4EnE2PHrAdccRYd25Vllt9WQLH8ijkQzXAHUe6lgoEEweTGdE63utFlqaJt8JEPUcRc8x04vUyPg== + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -2518,6 +2651,11 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +validator@^13.6.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -2615,6 +2753,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@2.0.0-1: + version "2.0.0-1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-1.tgz#8c3029b3ee2028306d5bcf396980623115ff8d18" + integrity sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" @@ -2637,3 +2780,14 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +z-schema@^4.2.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-4.2.4.tgz#73102a49512179b12a8ec50b1daa676b984da6e4" + integrity sha512-YvBeW5RGNeNzKOUJs3rTL4+9rpcvHXt5I051FJbOcitV8bl40pEfcG0Q+dWSwS0/BIYrMZ/9HHoqLllMkFhD0w== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.6.0" + optionalDependencies: + commander "^2.7.1" From 52e15eccfaebe649ab9960b99bbdabfbdc540e1f Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 22 Feb 2022 14:44:54 +0100 Subject: [PATCH 05/61] revert FRONT_URL --- front/src/Phaser/Entity/PlayerTextures.ts | 1 + pusher/src/Middleware/Cors.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index 9c820681..bc484ceb 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -53,6 +53,7 @@ export class PlayerTextures { return new Promise((resolve, reject) => { fetch(url, { method: "GET", + // mode: 'no-cors', headers: { Host: "pusher.workadventure.localhost", }, diff --git a/pusher/src/Middleware/Cors.ts b/pusher/src/Middleware/Cors.ts index 876ac3a4..54fd32ad 100644 --- a/pusher/src/Middleware/Cors.ts +++ b/pusher/src/Middleware/Cors.ts @@ -9,7 +9,7 @@ export function cors(req: Request, res: Response, next?: MiddlewareNext): Middle "Origin, X-Requested-With, Content-Type, Accept, Authorization, Pragma, Cache-Control" ); res.setHeader("access-control-allow-methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); - res.setHeader("access-control-allow-origin", "*"); + res.setHeader("access-control-allow-origin", FRONT_URL); if (next) { next(); From 063de6d1ca9d94b862db0912bf5971e0206c9684 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 23 Feb 2022 17:34:00 +0100 Subject: [PATCH 06/61] playerTextures metadata is being loaded via Phaser.Loader --- front/src/Phaser/Entity/PlayerTextures.ts | 54 +++++++------------ front/src/Phaser/Login/EntryScene.ts | 17 ++---- .../src/Phaser/Login/SelectCharacterScene.ts | 33 +++++++----- 3 files changed, 43 insertions(+), 61 deletions(-) diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index bc484ceb..34e7f433 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -1,5 +1,7 @@ //The list of all the player textures, both the default models and the partial textures used for customization +import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; + export interface BodyResourceDescriptionListInterface { [key: string]: BodyResourceDescriptionInterface; } @@ -49,46 +51,30 @@ export class PlayerTextures { public static ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface; public static LAYERS: BodyResourceDescriptionListInterface[]; - public static loadPlayerTexturesMetadata(url: string): Promise { - return new Promise((resolve, reject) => { - fetch(url, { - method: "GET", - // mode: 'no-cors', - headers: { - Host: "pusher.workadventure.localhost", - }, - }) - .then((response) => response.json()) - .then((data: PlayerTexturesMetadata) => { - this.mapTexturesMetadataIntoResources(data); - resolve(true); - }) - .catch((reason) => { - reject(reason); - }); - }); + public loadPlayerTexturesMetadata(metadata: PlayerTexturesMetadata): void { + this.mapTexturesMetadataIntoResources(metadata); } - private static mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { - this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); - this.COLOR_RESOURCES = this.getMappedResources(metadata.body); - this.EYES_RESOURCES = this.getMappedResources(metadata.eyes); - this.HAIR_RESOURCES = this.getMappedResources(metadata.hair); - this.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); - this.HATS_RESOURCES = this.getMappedResources(metadata.hat); - this.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); + private mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { + PlayerTextures.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); + PlayerTextures.COLOR_RESOURCES = this.getMappedResources(metadata.body); + PlayerTextures.EYES_RESOURCES = this.getMappedResources(metadata.eyes); + PlayerTextures.HAIR_RESOURCES = this.getMappedResources(metadata.hair); + PlayerTextures.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); + PlayerTextures.HATS_RESOURCES = this.getMappedResources(metadata.hat); + PlayerTextures.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); - this.LAYERS = [ - this.COLOR_RESOURCES, - this.EYES_RESOURCES, - this.HAIR_RESOURCES, - this.CLOTHES_RESOURCES, - this.HATS_RESOURCES, - this.ACCESSORIES_RESOURCES, + PlayerTextures.LAYERS = [ + PlayerTextures.COLOR_RESOURCES, + PlayerTextures.EYES_RESOURCES, + PlayerTextures.HAIR_RESOURCES, + PlayerTextures.CLOTHES_RESOURCES, + PlayerTextures.HATS_RESOURCES, + PlayerTextures.ACCESSORIES_RESOURCES, ]; } - private static getMappedResources(category: PlayerTexturesCategory): BodyResourceDescriptionListInterface { + private getMappedResources(category: PlayerTexturesCategory): BodyResourceDescriptionListInterface { const resources: BodyResourceDescriptionListInterface = {}; for (const collection of category.collections) { for (const texture of collection.textures) { diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index f560f44f..d86e3a2e 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -8,6 +8,7 @@ import LL from "../../i18n/i18n-svelte"; import { get } from "svelte/store"; import { localeDetector } from "../../i18n/locales"; import { PlayerTextures } from "../Entity/PlayerTextures"; +import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; export const EntrySceneName = "EntryScene"; @@ -16,7 +17,6 @@ export const EntrySceneName = "EntryScene"; * and to route to the next correct scene. */ export class EntryScene extends Scene { - private texturesMetadataLoaded: boolean = false; private localeLoaded: boolean = false; constructor() { @@ -31,21 +31,10 @@ export class EntryScene extends Scene { // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap this.load.bitmapFont(ReconnectingTextures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 }); - - void PlayerTextures.loadPlayerTexturesMetadata("http://pusher.workadventure.localhost/woka-list").then( - (success) => { - this.texturesMetadataLoaded = success; - } - ); } - create() {} - - public update(): void { - if (this.texturesMetadataLoaded && !this.localeLoaded) { - this.localeLoaded = true; - this.loadLocale(); - } + create() { + this.loadLocale(); } private loadLocale(): void { diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 1fceee39..cfa63bfd 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -5,7 +5,7 @@ import { CustomizeSceneName } from "./CustomizeScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { loadAllDefaultModels } from "../Entity/PlayerTexturesLoadingManager"; import { Loader } from "../Components/Loader"; -import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; +import { BodyResourceDescriptionInterface, PlayerTextures } from "../Entity/PlayerTextures"; import { AbstractCharacterScene } from "./AbstractCharacterScene"; import { areCharacterLayersValid } from "../../Connexion/LocalUser"; import { touchScreenManager } from "../../Touch/TouchScreenManager"; @@ -14,6 +14,7 @@ import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterSt import { waScaleManager } from "../Services/WaScaleManager"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; +import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; //todo: put this constants in a dedicated file export const SelectCharacterSceneName = "SelectCharacterScene"; @@ -32,28 +33,34 @@ export class SelectCharacterScene extends AbstractCharacterScene { protected lazyloadingAttempt = true; //permit to update texture loaded after renderer private loader: Loader; + private playerTextures: PlayerTextures; constructor() { super({ key: SelectCharacterSceneName, }); this.loader = new Loader(this); + this.playerTextures = new PlayerTextures(); } preload() { - this.loadSelectSceneCharacters() - .then((bodyResourceDescriptions) => { - bodyResourceDescriptions.forEach((bodyResourceDescription) => { - this.playerModels.push(bodyResourceDescription); - }); - this.lazyloadingAttempt = true; - }) - .catch((e) => console.error(e)); - this.playerModels = loadAllDefaultModels(this.load); - this.lazyloadingAttempt = false; + this.load.json("woka-list", `${PUSHER_URL}/woka-list`); + this.load.on("filecomplete-json-woka-list", () => { + this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get("woka-list")); + this.loadSelectSceneCharacters() + .then((bodyResourceDescriptions) => { + bodyResourceDescriptions.forEach((bodyResourceDescription) => { + this.playerModels.push(bodyResourceDescription); + }); + this.lazyloadingAttempt = true; + }) + .catch((e) => console.error(e)); + this.playerModels = loadAllDefaultModels(this.load); + this.lazyloadingAttempt = false; - //this function must stay at the end of preload function - this.loader.addLoader(); + //this function must stay at the end of preload function + this.loader.addLoader(); + }); } create() { From d65fe0ee2609c3618f16c3288caadaf87e9acea0 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 23 Feb 2022 17:51:29 +0100 Subject: [PATCH 07/61] fetch textures every time character or customize scene is open --- front/src/Phaser/Login/CustomizeScene.ts | 53 +++++++++++-------- .../src/Phaser/Login/SelectCharacterScene.ts | 12 +++-- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index e95eab60..8b0e4222 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -5,7 +5,7 @@ import Sprite = Phaser.GameObjects.Sprite; import { gameManager } from "../Game/GameManager"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { Loader } from "../Components/Loader"; -import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; +import { BodyResourceDescriptionInterface, PlayerTextures } from "../Entity/PlayerTextures"; import { AbstractCharacterScene } from "./AbstractCharacterScene"; import { areCharacterLayersValid } from "../../Connexion/LocalUser"; import { SelectCharacterSceneName } from "./SelectCharacterScene"; @@ -15,6 +15,7 @@ import { CustomizedCharacter } from "../Entity/CustomizedCharacter"; import { get } from "svelte/store"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; +import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; export const CustomizeSceneName = "CustomizeScene"; @@ -31,36 +32,44 @@ export class CustomizeScene extends AbstractCharacterScene { private moveVertically: number = 0; private loader: Loader; + private playerTextures: PlayerTextures; constructor() { super({ key: CustomizeSceneName, }); this.loader = new Loader(this); + this.playerTextures = new PlayerTextures(); } preload() { - this.loadCustomSceneSelectCharacters() - .then((bodyResourceDescriptions) => { - bodyResourceDescriptions.forEach((bodyResourceDescription) => { - if ( - bodyResourceDescription.level == undefined || - bodyResourceDescription.level < 0 || - bodyResourceDescription.level > 5 - ) { - throw new Error("Texture level is null"); - } - this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription); - }); - this.lazyloadingAttempt = true; - }) - .catch((e) => console.error(e)); + const wokaMetadataKey = "woka-list"; + this.cache.json.remove(wokaMetadataKey); + this.load.json(wokaMetadataKey, `${PUSHER_URL}/${wokaMetadataKey}`); + this.load.once(`filecomplete-json-${wokaMetadataKey}`, () => { + this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get(wokaMetadataKey)); + this.loadCustomSceneSelectCharacters() + .then((bodyResourceDescriptions) => { + bodyResourceDescriptions.forEach((bodyResourceDescription) => { + if ( + bodyResourceDescription.level == undefined || + bodyResourceDescription.level < 0 || + bodyResourceDescription.level > 5 + ) { + throw new Error("Texture level is null"); + } + this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription); + }); + this.lazyloadingAttempt = true; + }) + .catch((e) => console.error(e)); - this.layers = loadAllLayers(this.load); - this.lazyloadingAttempt = false; + this.layers = loadAllLayers(this.load); + this.lazyloadingAttempt = false; - //this function must stay at the end of preload function - this.loader.addLoader(); + //this function must stay at the end of preload function + this.loader.addLoader(); + }); } create() { @@ -287,14 +296,14 @@ export class CustomizeScene extends AbstractCharacterScene { analyticsClient.validationWoka("CustomizeWoka"); gameManager.setCharacterLayers(layers); - this.scene.sleep(CustomizeSceneName); + this.scene.stop(CustomizeSceneName); waScaleManager.restoreZoom(); gameManager.tryResumingGame(EnableCameraSceneName); customCharacterSceneVisibleStore.set(false); } public backToPreviousScene() { - this.scene.sleep(CustomizeSceneName); + this.scene.stop(CustomizeSceneName); waScaleManager.restoreZoom(); this.scene.run(SelectCharacterSceneName); customCharacterSceneVisibleStore.set(false); diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index cfa63bfd..17163eb6 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -44,9 +44,11 @@ export class SelectCharacterScene extends AbstractCharacterScene { } preload() { - this.load.json("woka-list", `${PUSHER_URL}/woka-list`); - this.load.on("filecomplete-json-woka-list", () => { - this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get("woka-list")); + const wokaMetadataKey = "woka-list"; + this.cache.json.remove(wokaMetadataKey); + this.load.json(wokaMetadataKey, `${PUSHER_URL}/${wokaMetadataKey}`); + this.load.once(`filecomplete-json-${wokaMetadataKey}`, () => { + this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get(wokaMetadataKey)); this.loadSelectSceneCharacters() .then((bodyResourceDescriptions) => { bodyResourceDescriptions.forEach((bodyResourceDescription) => { @@ -254,9 +256,9 @@ export class SelectCharacterScene extends AbstractCharacterScene { } protected updateSelectedPlayer(): void { - this.selectedPlayer?.anims.pause(this.selectedPlayer?.anims.currentAnim.frames[0]); + this.selectedPlayer?.anims?.pause(this.selectedPlayer?.anims.currentAnim.frames[0]); const player = this.players[this.currentSelectUser]; - player.play(this.playerModels[this.currentSelectUser].name); + player?.play(this.playerModels[this.currentSelectUser].name); this.selectedPlayer = player; localUserStore.setPlayerCharacterIndex(this.currentSelectUser); } From 378a95962a4acbeb6af71496664023b6d84dff92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 23 Feb 2022 21:08:21 +0100 Subject: [PATCH 08/61] Heavy changes: refactoring the pusher to always send the textures (and the front to accept them) --- back/src/Services/SocketManager.ts | 1 + front/src/Connexion/ConnectionManager.ts | 36 ++------ front/src/Connexion/ConnexionModels.ts | 2 + front/src/Connexion/LocalUser.ts | 13 ++- front/src/Connexion/Room.ts | 10 +-- front/src/Connexion/RoomConnection.ts | 37 ++++++-- front/src/Phaser/Entity/Character.ts | 11 ++- front/src/Phaser/Entity/PlayerTextures.ts | 15 +++- .../Entity/PlayerTexturesLoadingManager.ts | 40 +++------ front/src/Phaser/Game/GameScene.ts | 30 ++++--- .../Phaser/Login/AbstractCharacterScene.ts | 30 ++++--- front/src/Phaser/Login/CustomizeScene.ts | 4 +- .../src/Phaser/Login/SelectCharacterScene.ts | 1 - .../Phaser/Game/PlayerTexturesLoadingTest.ts | 28 ------ messages/JsonMessages/AdminApiData.ts | 4 +- messages/JsonMessages/CharacterTexture.ts | 16 ---- messages/JsonMessages/MapDetailsData.ts | 4 +- messages/JsonMessages/RegisterData.ts | 4 +- messages/protos/messages.proto | 6 ++ .../src/Controller/AuthenticateController.ts | 2 - pusher/src/Controller/IoSocketController.ts | 89 ++++++++++++++++--- pusher/src/Controller/WokaListController.ts | 21 ++--- pusher/src/Enum/PlayerTextures.ts | 11 +-- .../src/Model/Websocket/ExSocketInterface.ts | 9 +- pusher/src/Model/Websocket/ProtobufUtils.ts | 10 ++- pusher/src/Services/AdminApi.ts | 40 ++++++--- pusher/src/Services/AdminWokaService.ts | 10 +-- pusher/src/Services/LocalWokaService.ts | 18 +++- pusher/src/Services/SocketManager.ts | 51 ++++------- pusher/src/Services/WokaService.ts | 5 ++ pusher/src/Services/WokaServiceInterface.ts | 2 +- 31 files changed, 290 insertions(+), 270 deletions(-) delete mode 100644 front/tests/Phaser/Game/PlayerTexturesLoadingTest.ts delete mode 100644 messages/JsonMessages/CharacterTexture.ts create mode 100644 pusher/src/Services/WokaService.ts diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 9233811b..1d26f001 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -103,6 +103,7 @@ export class SocketManager { const roomJoinedMessage = new RoomJoinedMessage(); roomJoinedMessage.setTagList(joinRoomMessage.getTagList()); roomJoinedMessage.setUserroomtoken(joinRoomMessage.getUserroomtoken()); + roomJoinedMessage.setCharacterlayerList(joinRoomMessage.getCharacterlayerList()); for (const [itemId, item] of room.getItemsState().entries()) { const itemStateMessage = new ItemStateMessage(); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 391da7bf..998595b7 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -134,7 +134,7 @@ class ConnectionManager { console.error("Invalid data received from /register route. Data: ", data); throw new Error("Invalid data received from /register route."); } - this.localUser = new LocalUser(data.userUuid, data.textures, data.email); + this.localUser = new LocalUser(data.userUuid, data.email); this.authToken = data.authToken; localUserStore.saveUser(this.localUser); localUserStore.setAuthToken(this.authToken); @@ -214,32 +214,6 @@ class ConnectionManager { } } this.localUser = localUserStore.getLocalUser() as LocalUser; //if authToken exist in localStorage then localUser cannot be null - - if (this._currentRoom.textures != undefined && this._currentRoom.textures.length > 0) { - //check if texture was changed - if (this.localUser.textures.length === 0) { - this.localUser.textures = this._currentRoom.textures; - } else { - // TODO: the local store should NOT be used as a buffer for all the texture we were authorized to have. Bad idea. - // Instead, it is the responsibility of the ADMIN to return the EXACT list of textures we can have in a given context - // + this list can change over time or over rooms. - - // 1- a room could forbid a particular dress code. In this case, the user MUST change its skin. - // 2- a room can allow "external skins from other maps" => important: think about fediverse! => switch to URLs? (with a whitelist mechanism?) => but what about NFTs? - - // Note: stocker des URL dans le localstorage pour les utilisateurs actuels: mauvaise idée (empêche de mettre l'URL à jour dans le futur) => en même temps, problème avec le portage de user d'un serveur à l'autre - // Réfléchir à une notion de "character server" ?? - - this._currentRoom.textures.forEach((newTexture) => { - const alreadyExistTexture = this.localUser.textures.find((c) => newTexture.id === c.id); - if (this.localUser.textures.findIndex((c) => newTexture.id === c.id) !== -1) { - return; - } - this.localUser.textures.push(newTexture); - }); - } - localUserStore.saveUser(this.localUser); - } } if (this._currentRoom == undefined) { return Promise.reject(new Error("Invalid URL")); @@ -265,7 +239,7 @@ class ConnectionManager { public async anonymousLogin(isBenchmark: boolean = false): Promise { const data = await axiosWithRetry.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); - this.localUser = new LocalUser(data.userUuid, [], data.email); + this.localUser = new LocalUser(data.userUuid, data.email); this.authToken = data.authToken; if (!isBenchmark) { // In benchmark, we don't have a local storage. @@ -275,7 +249,7 @@ class ConnectionManager { } public initBenchmark(): void { - this.localUser = new LocalUser("", []); + this.localUser = new LocalUser(""); } public connectToRoomSocket( @@ -352,13 +326,13 @@ class ConnectionManager { throw new Error("No Auth code provided"); } } - const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { + const { authToken, userUuid, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token, playUri: this.currentRoom?.key }, }).then((res) => { return res.data; }); localUserStore.setAuthToken(authToken); - this.localUser = new LocalUser(userUuid, textures, email); + this.localUser = new LocalUser(userUuid, email); localUserStore.saveUser(this.localUser); this.authToken = authToken; diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index bf834a02..bd12d866 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -2,6 +2,7 @@ import type { SignalData } from "simple-peer"; import type { RoomConnection } from "./RoomConnection"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; import { PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; +import { CharacterLayer } from "../../../back/src/Model/Websocket/CharacterLayer"; export interface PointInterface { x: number; @@ -83,6 +84,7 @@ export interface RoomJoinedMessageInterface { //groups: GroupCreatedUpdatedMessageInterface[], items: { [itemId: number]: unknown }; variables: Map; + characterLayers: BodyResourceDescriptionInterface[]; } export interface PlayGlobalMessageInterface { diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index d4498883..fce7593d 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -1,10 +1,11 @@ import { MAX_USERNAME_LENGTH } from "../Enum/EnvironmentVariable"; +export type LayerNames = "woka" | "body" | "eyes" | "hair" | "clothes" | "hat" | "accessory"; + export interface CharacterTexture { - id: number; - level: number; + id: string; + layer: LayerNames; url: string; - rights: string; } export const maxUserNameLength: number = MAX_USERNAME_LENGTH; @@ -24,9 +25,5 @@ export function areCharacterLayersValid(value: string[] | null): boolean { } export class LocalUser { - constructor( - public readonly uuid: string, - public textures: CharacterTexture[], - public email: string | null = null - ) {} + constructor(public readonly uuid: string, public email: string | null = null) {} } diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 213c3019..778f7e35 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -9,7 +9,7 @@ import { isMapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; export class MapDetail { - constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {} + constructor(public readonly mapUrl: string) {} } export interface RoomRedirect { @@ -25,7 +25,6 @@ export class Room { private _authenticationMandatory: boolean = DISABLE_ANONYMOUS; private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER; private _mapUrl: string | undefined; - private _textures: CharacterTexture[] | undefined; private instance: string | undefined; private readonly _search: URLSearchParams; private _contactPage: string | undefined; @@ -118,7 +117,6 @@ export class Room { } else if (isMapDetailsData(data)) { console.log("Map ", this.id, " resolves to URL ", data.mapUrl); this._mapUrl = data.mapUrl; - this._textures = data.textures; this._group = data.group; this._authenticationMandatory = data.authenticationMandatory != null ? data.authenticationMandatory : DISABLE_ANONYMOUS; @@ -128,7 +126,7 @@ export class Room { this._expireOn = new Date(data.expireOn); } this._canReport = data.canReport ?? false; - return new MapDetail(data.mapUrl, data.textures); + return new MapDetail(data.mapUrl); } else { throw new Error("Data received by the /map endpoint of the Pusher is not in a valid format."); } @@ -205,10 +203,6 @@ export class Room { return this.roomUrl.toString(); } - get textures(): CharacterTexture[] | undefined { - return this._textures; - } - get mapUrl(): string { if (!this._mapUrl) { throw new Error("Map URL not fetched yet"); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 4e2f8397..3ea8375b 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -20,7 +20,7 @@ import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTe import { adminMessagesService } from "./AdminMessagesService"; import { connectionManager } from "./ConnectionManager"; import { get } from "svelte/store"; -import { warningContainerStore } from "../Stores/MenuStore"; +import { menuIconVisiblilityStore, menuVisiblilityStore, warningContainerStore } from "../Stores/MenuStore"; import { followStateStore, followRoleStore, followUsersStore } from "../Stores/FollowStore"; import { localUserStore } from "./LocalUserStore"; import { @@ -52,10 +52,14 @@ import { PositionMessage_Direction, SetPlayerDetailsMessage as SetPlayerDetailsMessageTsProto, PingMessage as PingMessageTsProto, + CharacterLayerMessage, } from "../Messages/ts-proto-generated/messages"; import { Subject } from "rxjs"; import { OpenPopupEvent } from "../Api/Events/OpenPopupEvent"; import { match } from "assert"; +import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore"; +import { gameManager } from "../Phaser/Game/GameManager"; +import { SelectCharacterScene, SelectCharacterSceneName } from "../Phaser/Login/SelectCharacterScene"; const manualPingDelay = 20000; @@ -336,12 +340,16 @@ export class RoomConnection implements RoomConnection { this.userId = roomJoinedMessage.currentUserId; this.tags = roomJoinedMessage.tag; this._userRoomToken = roomJoinedMessage.userRoomToken; + const characterLayers = roomJoinedMessage.characterLayer.map( + this.mapCharactgerLayerToBodyResourceDescription.bind(this) + ); this._roomJoinedMessageStream.next({ connection: this, room: { items, variables, + characterLayers, } as RoomJoinedMessageInterface, }); break; @@ -351,6 +359,15 @@ export class RoomConnection implements RoomConnection { this.closed = true; break; } + case "invalidTextureMessage": { + menuVisiblilityStore.set(false); + menuIconVisiblilityStore.set(false); + selectCharacterSceneVisibleStore.set(true); + gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); + + this.closed = true; + break; + } case "tokenExpiredMessage": { connectionManager.logout().catch((e) => console.error(e)); this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency @@ -591,6 +608,15 @@ export class RoomConnection implements RoomConnection { }); }*/ + private mapCharactgerLayerToBodyResourceDescription( + characterLayer: CharacterLayerMessage + ): BodyResourceDescriptionInterface { + return { + name: characterLayer.name, + img: characterLayer.url, + }; + } + // TODO: move this to protobuf utils private toMessageUserJoined(message: UserJoinedMessageTsProto): MessageUserJoined { const position = message.position; @@ -598,12 +624,9 @@ export class RoomConnection implements RoomConnection { throw new Error("Invalid JOIN_ROOM message"); } - const characterLayers = message.characterLayers.map((characterLayer): BodyResourceDescriptionInterface => { - return { - name: characterLayer.name, - img: characterLayer.url, - }; - }); + const characterLayers = message.characterLayers.map( + this.mapCharactgerLayerToBodyResourceDescription.bind(this) + ); const companion = message.companion; diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index fa12ae96..79669d55 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -83,7 +83,16 @@ export abstract class Character extends Container implements OutlineableInterfac }); }) .catch(() => { - return lazyLoadPlayerCharacterTextures(scene.load, ["color_22", "eyes_23"]).then((textures) => { + return lazyLoadPlayerCharacterTextures(scene.load, [ + { + name: "color_22", + img: "resources/customisation/character_color/character_color21.png", + }, + { + name: "eyes_23", + img: "resources/customisation/character_eyes/character_eyes23.png", + }, + ]).then((textures) => { this.addTextures(textures, frame); this.invisible = false; this.playAnimation(direction, moving); diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index 34e7f433..657556b8 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -1,7 +1,5 @@ //The list of all the player textures, both the default models and the partial textures used for customization -import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; - export interface BodyResourceDescriptionListInterface { [key: string]: BodyResourceDescriptionInterface; } @@ -12,6 +10,19 @@ export interface BodyResourceDescriptionInterface { level?: number; } +/** + * Temporary object to map layers to the old "level" concept. + */ +export const mapLayerToLevel = { + woka: -1, + body: 0, + eyes: 1, + hair: 2, + clothes: 3, + hat: 4, + accessory: 5, +}; + enum PlayerTexturesKey { Accessory = "accessory", Body = "body", diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 39be830d..0f872e5c 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -1,6 +1,6 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import type { CharacterTexture } from "../../Connexion/LocalUser"; -import { BodyResourceDescriptionInterface, PlayerTextures } from "./PlayerTextures"; +import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures } from "./PlayerTextures"; import CancelablePromise from "cancelable-promise"; export interface FrameConfig { @@ -28,13 +28,11 @@ export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptio return returnArray; }; -export const loadCustomTexture = ( +export const loadWokaTexture = ( loaderPlugin: LoaderPlugin, - texture: CharacterTexture + texture: BodyResourceDescriptionInterface ): CancelablePromise => { - const name = "customCharacterTexture" + texture.id; - const playerResourceDescriptor: BodyResourceDescriptionInterface = { name, img: texture.url, level: texture.level }; - return createLoadingPromise(loaderPlugin, playerResourceDescriptor, { + return createLoadingPromise(loaderPlugin, texture, { frameWidth: 32, frameHeight: 32, }); @@ -42,16 +40,15 @@ export const loadCustomTexture = ( export const lazyLoadPlayerCharacterTextures = ( loadPlugin: LoaderPlugin, - texturekeys: Array + textures: BodyResourceDescriptionInterface[] ): CancelablePromise => { const promisesList: CancelablePromise[] = []; - texturekeys.forEach((textureKey: string | BodyResourceDescriptionInterface) => { + textures.forEach((texture) => { try { //TODO refactor - const playerResourceDescriptor = getRessourceDescriptor(textureKey); - if (playerResourceDescriptor && !loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { + if (!loadPlugin.textureManager.exists(texture.name)) { promisesList.push( - createLoadingPromise(loadPlugin, playerResourceDescriptor, { + createLoadingPromise(loadPlugin, texture, { frameWidth: 32, frameHeight: 32, }) @@ -64,9 +61,9 @@ export const lazyLoadPlayerCharacterTextures = ( let returnPromise: CancelablePromise>; if (promisesList.length > 0) { loadPlugin.start(); - returnPromise = CancelablePromise.all(promisesList).then(() => texturekeys); + returnPromise = CancelablePromise.all(promisesList).then(() => textures); } else { - returnPromise = CancelablePromise.resolve(texturekeys); + returnPromise = CancelablePromise.resolve(textures); } //If the loading fail, we render the default model instead. @@ -77,23 +74,6 @@ export const lazyLoadPlayerCharacterTextures = ( ); }; -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 = PlayerTextures.PLAYER_RESOURCES[textureName]; - if (playerResource !== undefined) return playerResource; - - for (let i = 0; i < PlayerTextures.LAYERS.length; i++) { - const playerResource = PlayerTextures.LAYERS[i][textureName]; - if (playerResource !== undefined) return playerResource; - } - throw new Error("Could not find a data for texture " + textureName); -}; - export const createLoadingPromise = ( loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface, diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 0a44cea3..7248c2ed 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -18,7 +18,7 @@ import { soundManager } from "./SoundManager"; import { SharedVariablesManager } from "./SharedVariablesManager"; import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager"; -import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; +import { lazyLoadPlayerCharacterTextures, loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; import { iframeListener } from "../../Api/IframeListener"; import { DEBUG_MODE, JITSI_URL, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable"; @@ -97,6 +97,8 @@ import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore"; import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; import { SimpleCoWebsite } from "../../WebRtc/CoWebsite/SimpleCoWebsite"; import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; +import { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; +import CancelablePromise from "cancelable-promise"; export interface GameSceneInitInterface { initPosition: PointInterface | null; reconnecting: boolean; @@ -244,13 +246,6 @@ export class GameScene extends DirtyScene { //initialize frame event of scripting API this.listenToIframeEvents(); - const localUser = localUserStore.getLocalUser(); - const textures = localUser?.textures; - if (textures) { - for (const texture of textures) { - loadCustomTexture(this.load, texture).catch((e) => console.error(e)); - } - } this.load.image("iconTalk", "/resources/icons/icon_talking.png"); if (touchScreenManager.supportTouchScreen) { @@ -745,6 +740,14 @@ export class GameScene extends DirtyScene { .then((onConnect: OnConnectInterface) => { this.connection = onConnect.connection; + lazyLoadPlayerCharacterTextures(this.load, onConnect.room.characterLayers) + .then((layers) => { + this.currentPlayerTexturesResolve(layers); + }) + .catch((e) => { + this.currentPlayerTexturesReject(e); + }); + playersStore.connectToRoomConnection(this.connection); userIsAdminStore.set(this.connection.hasTag("admin")); @@ -1689,16 +1692,23 @@ ${escapedMessage} } } + // The promise that will resolve to the current player texture. This will be available only after connection is established. + private currentPlayerTexturesResolve!: (value: string[]) => void; + private currentPlayerTexturesReject!: (reason: unknown) => void; + private currentPlayerTexturesPromise: CancelablePromise = new CancelablePromise((resolve, reject) => { + this.currentPlayerTexturesResolve = resolve; + this.currentPlayerTexturesReject = reject; + }); + private createCurrentPlayer() { //TODO create animation moving between exit and start - const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, this.characterLayers); try { this.CurrentPlayer = new Player( this, this.startPositionCalculator.startPosition.x, this.startPositionCalculator.startPosition.y, this.playerName, - texturesPromise, + this.currentPlayerTexturesPromise, PlayerAnimationDirections.Down, false, this.companion, diff --git a/front/src/Phaser/Login/AbstractCharacterScene.ts b/front/src/Phaser/Login/AbstractCharacterScene.ts index 67e2ba3d..bc260718 100644 --- a/front/src/Phaser/Login/AbstractCharacterScene.ts +++ b/front/src/Phaser/Login/AbstractCharacterScene.ts @@ -1,41 +1,45 @@ import { ResizableScene } from "./ResizableScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; -import { loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; +import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager"; import type { CharacterTexture } from "../../Connexion/LocalUser"; import type CancelablePromise from "cancelable-promise"; +import { PlayerTextures } from "../Entity/PlayerTextures"; +import { Loader } from "../Components/Loader"; +import { CustomizeSceneName } from "./CustomizeScene"; export abstract class AbstractCharacterScene extends ResizableScene { + protected playerTextures: PlayerTextures; + + constructor(params: { key: string }) { + super(params); + this.playerTextures = new PlayerTextures(); + } + loadCustomSceneSelectCharacters(): Promise { - const textures = this.getTextures(); + const textures = PlayerTextures.PLAYER_RESOURCES; const promises: CancelablePromise[] = []; if (textures) { - for (const texture of textures) { + for (const texture of Object.values(textures)) { if (texture.level === -1) { continue; } - promises.push(loadCustomTexture(this.load, texture)); + promises.push(loadWokaTexture(this.load, texture)); } } return Promise.all(promises); } loadSelectSceneCharacters(): Promise { - const textures = this.getTextures(); const promises: CancelablePromise[] = []; - if (textures) { - for (const texture of textures) { + for (const textures of PlayerTextures.LAYERS) { + for (const texture of Object.values(textures)) { if (texture.level !== -1) { continue; } - promises.push(loadCustomTexture(this.load, texture)); + promises.push(loadWokaTexture(this.load, texture)); } } return Promise.all(promises); } - - private getTextures(): CharacterTexture[] | undefined { - const localUser = localUserStore.getLocalUser(); - return localUser?.textures; - } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 8b0e4222..b87e3640 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -5,7 +5,7 @@ import Sprite = Phaser.GameObjects.Sprite; import { gameManager } from "../Game/GameManager"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { Loader } from "../Components/Loader"; -import { BodyResourceDescriptionInterface, PlayerTextures } from "../Entity/PlayerTextures"; +import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; import { AbstractCharacterScene } from "./AbstractCharacterScene"; import { areCharacterLayersValid } from "../../Connexion/LocalUser"; import { SelectCharacterSceneName } from "./SelectCharacterScene"; @@ -32,14 +32,12 @@ export class CustomizeScene extends AbstractCharacterScene { private moveVertically: number = 0; private loader: Loader; - private playerTextures: PlayerTextures; constructor() { super({ key: CustomizeSceneName, }); this.loader = new Loader(this); - this.playerTextures = new PlayerTextures(); } preload() { diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 17163eb6..67cdc952 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -33,7 +33,6 @@ export class SelectCharacterScene extends AbstractCharacterScene { protected lazyloadingAttempt = true; //permit to update texture loaded after renderer private loader: Loader; - private playerTextures: PlayerTextures; constructor() { super({ diff --git a/front/tests/Phaser/Game/PlayerTexturesLoadingTest.ts b/front/tests/Phaser/Game/PlayerTexturesLoadingTest.ts deleted file mode 100644 index 6c984a9e..00000000 --- a/front/tests/Phaser/Game/PlayerTexturesLoadingTest.ts +++ /dev/null @@ -1,28 +0,0 @@ -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 through 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 through 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 through hardcoded values", () => { - const desc = getRessourceDescriptor({ name: "male1", img: "" }); - expect(desc.name).toEqual("male1"); - expect(desc.img).toEqual("resources/characters/pipoya/Male 01-1.png"); - }); -}); diff --git a/messages/JsonMessages/AdminApiData.ts b/messages/JsonMessages/AdminApiData.ts index 314963be..1fd7765e 100644 --- a/messages/JsonMessages/AdminApiData.ts +++ b/messages/JsonMessages/AdminApiData.ts @@ -1,5 +1,5 @@ import * as tg from "generic-type-guard"; -import { isCharacterTexture } from "./CharacterTexture"; +//import { isCharacterTexture } from "./CharacterTexture"; /* * WARNING! The original file is in /messages/JsonMessages. @@ -12,7 +12,7 @@ export const isAdminApiData = new tg.IsInterface() email: tg.isNullable(tg.isString), roomUrl: tg.isString, mapUrlStart: tg.isString, - textures: tg.isArray(isCharacterTexture), +// textures: tg.isArray(isCharacterTexture), }) .withOptionalProperties({ messages: tg.isArray(tg.isUnknown), diff --git a/messages/JsonMessages/CharacterTexture.ts b/messages/JsonMessages/CharacterTexture.ts deleted file mode 100644 index eb2ec15e..00000000 --- a/messages/JsonMessages/CharacterTexture.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as tg from "generic-type-guard"; - -/* - * WARNING! The original file is in /messages/JsonMessages. - * All other files are automatically copied from this file on container startup / build - */ - -export const isCharacterTexture = new tg.IsInterface() - .withProperties({ - id: tg.isNumber, - level: tg.isNumber, - url: tg.isString, - rights: tg.isString, - }) - .get(); -export type CharacterTexture = tg.GuardedType; diff --git a/messages/JsonMessages/MapDetailsData.ts b/messages/JsonMessages/MapDetailsData.ts index 39866eee..87b2b95a 100644 --- a/messages/JsonMessages/MapDetailsData.ts +++ b/messages/JsonMessages/MapDetailsData.ts @@ -1,5 +1,5 @@ import * as tg from "generic-type-guard"; -import { isCharacterTexture } from "./CharacterTexture"; +//import { isCharacterTexture } from "./CharacterTexture"; import { isNumber } from "generic-type-guard"; /* @@ -12,7 +12,7 @@ export const isMapDetailsData = new tg.IsInterface() mapUrl: tg.isString, policy_type: isNumber, //isNumericEnum(GameRoomPolicyTypes), tags: tg.isArray(tg.isString), - textures: tg.isArray(isCharacterTexture), +// textures: tg.isArray(isCharacterTexture), authenticationMandatory: tg.isUnion(tg.isNullable(tg.isBoolean), tg.isUndefined), roomSlug: tg.isNullable(tg.isString), // deprecated contactPage: tg.isNullable(tg.isString), diff --git a/messages/JsonMessages/RegisterData.ts b/messages/JsonMessages/RegisterData.ts index 473ee592..1fe3426f 100644 --- a/messages/JsonMessages/RegisterData.ts +++ b/messages/JsonMessages/RegisterData.ts @@ -1,5 +1,5 @@ import * as tg from "generic-type-guard"; -import { isCharacterTexture } from "./CharacterTexture"; +//import { isCharacterTexture } from "./CharacterTexture"; /* * WARNING! The original file is in /messages/JsonMessages. @@ -13,7 +13,7 @@ export const isRegisterData = new tg.IsInterface() organizationMemberToken: tg.isNullable(tg.isString), mapUrlStart: tg.isString, userUuid: tg.isString, - textures: tg.isArray(isCharacterTexture), +// textures: tg.isArray(isCharacterTexture), authToken: tg.isString, }) .withOptionalProperties({ diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 8ac7bbf0..d0768480 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -34,6 +34,7 @@ message SilentMessage { message CharacterLayerMessage { string url = 1; string name = 2; + string layer = 3; } message CompanionMessage { @@ -223,6 +224,8 @@ message RoomJoinedMessage { repeated string tag = 5; repeated VariableMessage variable = 6; string userRoomToken = 7; + // We send the current skin of the current player. + repeated CharacterLayerMessage characterLayer = 8; } message WebRtcStartMessage { @@ -274,6 +277,8 @@ message WorldFullMessage{ } message TokenExpiredMessage{ } +message InvalidTextureMessage{ +} message WorldConnexionMessage{ string message = 2; @@ -310,6 +315,7 @@ message ServerToClientMessage { FollowRequestMessage followRequestMessage = 21; FollowConfirmationMessage followConfirmationMessage = 22; FollowAbortMessage followAbortMessage = 23; + InvalidTextureMessage invalidTextureMessage = 24; } } diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index f6d8e41f..d2e6c11a 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -273,7 +273,6 @@ export class AuthenticateController extends BaseHttpController { const email = data.email; const roomUrl = data.roomUrl; const mapUrlStart = data.mapUrlStart; - const textures = data.textures; const authToken = jwtTokenManager.createAuthToken(email || userUuid); res.json({ @@ -283,7 +282,6 @@ export class AuthenticateController extends BaseHttpController { roomUrl, mapUrlStart, organizationMemberToken, - textures, } as RegisterData); } catch (e) { console.error("register => ERROR", e); diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index eb8f4e75..0400477a 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -1,4 +1,4 @@ -import { CharacterLayer, ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." +import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." import { GameRoomPolicyTypes } from "../Model/PusherRoom"; import { PointInterface } from "../Model/Websocket/PointInterface"; import { @@ -31,11 +31,46 @@ import { emitInBatch } from "../Services/IoSocketHelpers"; import { ADMIN_API_URL, ADMIN_SOCKETS_TOKEN, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; import { Zone } from "_Model/Zone"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; -import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; import { isAdminMessageInterface } from "../Model/Websocket/Admin/AdminMessages"; import Axios from "axios"; import { InvalidTokenError } from "../Controller/InvalidTokenError"; import HyperExpress from "hyper-express"; +import { localWokaService } from "../Services/LocalWokaService"; +import { WebSocket } from "uWebSockets.js"; +import { WokaDetail } from "../Enum/PlayerTextures"; + +/** + * The object passed between the "open" and the "upgrade" methods when opening a websocket + */ +interface UpgradeData { + // Data passed here is accessible on the "websocket" socket object. + rejected: false; + token: string; + userUuid: string; + IPAddress: string; + roomId: string; + name: string; + companion: CompanionMessage | undefined; + characterLayers: WokaDetail[]; + messages: unknown[]; + tags: string[]; + visitCardUrl: string | null; + userRoomToken: string | undefined; + position: PointInterface; + viewport: { + top: number; + right: number; + bottom: number; + left: number; + }; +} + +interface UpgradeFailedData { + rejected: true; + reason: "tokenInvalid" | "textureInvalid" | null; + message: string; + roomId: string; +} export class IoSocketController { private nextUserId: number = 1; @@ -244,7 +279,7 @@ export class IoSocketController { let memberVisitCardUrl: string | null = null; let memberMessages: unknown; let memberUserRoomToken: string | undefined; - let memberTextures: CharacterTexture[] = []; + let memberTextures: WokaDetail[] = []; const room = await socketManager.getOrCreateRoom(roomId); let userData: FetchMemberDataByUuidResponse = { email: userIdentifier, @@ -256,6 +291,9 @@ export class IoSocketController { anonymous: true, userRoomToken: undefined, }; + + let characterLayerObjs: WokaDetail[]; + if (ADMIN_API_URL) { try { try { @@ -308,6 +346,8 @@ export class IoSocketController { ) { throw new Error("Use the login URL to connect"); } + + characterLayerObjs = memberTextures; } catch (e) { console.log( "access not granted for user " + @@ -318,11 +358,31 @@ export class IoSocketController { console.error(e); throw new Error("User cannot access this world"); } + } else { + const fetchedTextures = await localWokaService.fetchWokaDetails(characterLayers); + if (fetchedTextures === undefined) { + // The textures we want to use do not exist! + // We need to go in error. + res.upgrade( + { + rejected: true, + reason: "textureInvalid", + message: "", + roomId, + } as UpgradeFailedData, + websocketKey, + websocketProtocol, + websocketExtensions, + context + ); + return; + } + characterLayerObjs = fetchedTextures; } // Generate characterLayers objects from characterLayers string[] - const characterLayerObjs: CharacterLayer[] = - SocketManager.mergeCharacterLayersAndCustomTextures(characterLayers, memberTextures); + /*const characterLayerObjs: CharacterLayer[] = + SocketManager.mergeCharacterLayersAndCustomTextures(characterLayers, memberTextures);*/ if (upgradeAborted.aborted) { console.log("Ouch! Client disconnected before we could upgrade it!"); @@ -334,7 +394,7 @@ export class IoSocketController { res.upgrade( { // Data passed here is accessible on the "websocket" socket object. - url, + rejected: false, token, userUuid: userData.userUuid, IPAddress, @@ -346,7 +406,6 @@ export class IoSocketController { tags: memberTags, visitCardUrl: memberVisitCardUrl, userRoomToken: memberUserRoomToken, - textures: memberTextures, position: { x: x, y: y, @@ -359,7 +418,7 @@ export class IoSocketController { bottom, left, }, - }, + } as UpgradeData, /* Spell these correctly */ websocketKey, websocketProtocol, @@ -374,7 +433,7 @@ export class IoSocketController { reason: e instanceof InvalidTokenError ? tokenInvalidException : null, message: e.message, roomId, - }, + } as UpgradeFailedData, websocketKey, websocketProtocol, websocketExtensions, @@ -387,7 +446,7 @@ export class IoSocketController { reason: null, message: "500 Internal Server Error", roomId, - }, + } as UpgradeFailedData, websocketKey, websocketProtocol, websocketExtensions, @@ -398,20 +457,23 @@ export class IoSocketController { })(); }, /* Handlers */ - open: (ws) => { + open: (_ws: WebSocket) => { + const ws = _ws as WebSocket & (UpgradeData | UpgradeFailedData); if (ws.rejected === true) { // If there is a room in the error, let's check if we need to clean it. if (ws.roomId) { - socketManager.deleteRoomIfEmptyFromId(ws.roomId as string); + socketManager.deleteRoomIfEmptyFromId(ws.roomId); } //FIX ME to use status code if (ws.reason === tokenInvalidException) { socketManager.emitTokenExpiredMessage(ws); + } else if (ws.reason === "textureInvalid") { + socketManager.emitInvalidTextureMessage(ws); } else if (ws.message === "World is full") { socketManager.emitWorldFullMessage(ws); } else { - socketManager.emitConnexionErrorMessage(ws, ws.message as string); + socketManager.emitConnexionErrorMessage(ws, ws.message); } setTimeout(() => ws.close(), 0); return; @@ -535,7 +597,6 @@ export class IoSocketController { client.name = ws.name; client.tags = ws.tags; client.visitCardUrl = ws.visitCardUrl; - client.textures = ws.textures; client.characterLayers = ws.characterLayers; client.companion = ws.companion; client.roomId = ws.roomId; diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts index 851aa341..0ce10966 100644 --- a/pusher/src/Controller/WokaListController.ts +++ b/pusher/src/Controller/WokaListController.ts @@ -1,24 +1,13 @@ import { hasToken } from "../Middleware/HasToken"; import { BaseHttpController } from "./BaseHttpController"; -import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; -import { adminWokaService } from "..//Services/AdminWokaService"; -import { localWokaService } from "..//Services/LocalWokaService"; -import { WokaServiceInterface } from "src/Services/WokaServiceInterface"; -import { Server } from "hyper-express"; +import { wokaService } from "../Services/WokaService"; export class WokaListController extends BaseHttpController { - private wokaService: WokaServiceInterface; - - constructor(app: Server) { - super(app); - this.wokaService = ADMIN_API_URL ? adminWokaService : localWokaService; - } - routes() { // eslint-disable-next-line @typescript-eslint/no-misused-promises this.app.get("/woka-list", {}, async (req, res) => { const token = req.header("Authorization"); - const wokaList = await this.wokaService.getWokaList(token); + const wokaList = await wokaService.getWokaList(token); if (!wokaList) { return res.status(500).send("Error on getting woka list"); @@ -28,20 +17,20 @@ export class WokaListController extends BaseHttpController { }); // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.app.post("/woka-details", async (req, res) => { + /*this.app.post("/woka-details", async (req, res) => { const body = await req.json(); if (!body || !body.textureIds) { return res.status(400); } const textureIds = body.textureIds; - const wokaDetails = await this.wokaService.fetchWokaDetails(textureIds); + const wokaDetails = await wokaService.fetchWokaDetails(textureIds); if (!wokaDetails) { return res.json({ details: [] }); } return res.json(wokaDetails); - }); + });*/ } } diff --git a/pusher/src/Enum/PlayerTextures.ts b/pusher/src/Enum/PlayerTextures.ts index 9b597cbc..cc7c43e1 100644 --- a/pusher/src/Enum/PlayerTextures.ts +++ b/pusher/src/Enum/PlayerTextures.ts @@ -49,16 +49,11 @@ export const isWokaDetail = new tg.IsInterface() id: tg.isString, }) .withOptionalProperties({ - texture: tg.isString, + url: tg.isString, + layer: tg.isString, }) .get(); export type WokaDetail = tg.GuardedType; -export const isWokaDetailsResult = new tg.IsInterface() - .withProperties({ - details: tg.isArray(isWokaDetail), - }) - .get(); - -export type WokaDetailsResult = tg.GuardedType; +export type WokaDetailsResult = WokaDetail[]; diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 13045a11..b5815635 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -13,14 +13,10 @@ import { ClientDuplexStream } from "grpc"; import { Zone } from "_Model/Zone"; import { CharacterTexture } from "../../Messages/JsonMessages/CharacterTexture"; import { compressors } from "hyper-express"; +import { WokaDetail } from "_Enum/PlayerTextures"; export type BackConnection = ClientDuplexStream; -export interface CharacterLayer { - name: string; - url: string | undefined; -} - export interface ExSocketInterface extends compressors.WebSocket, Identificable { token: string; roomId: string; @@ -28,7 +24,7 @@ export interface ExSocketInterface extends compressors.WebSocket, Identificable userUuid: string; // A unique identifier for this user IPAddress: string; // IP address name: string; - characterLayers: CharacterLayer[]; + characterLayers: WokaDetail[]; position: PointInterface; viewport: ViewportInterface; companion?: CompanionMessage; @@ -42,7 +38,6 @@ export interface ExSocketInterface extends compressors.WebSocket, Identificable messages: unknown; tags: string[]; visitCardUrl: string | null; - textures: CharacterTexture[]; backConnection: BackConnection; listenedZones: Set; userRoomToken: string | undefined; diff --git a/pusher/src/Model/Websocket/ProtobufUtils.ts b/pusher/src/Model/Websocket/ProtobufUtils.ts index bd9cb9c2..daf2aeb8 100644 --- a/pusher/src/Model/Websocket/ProtobufUtils.ts +++ b/pusher/src/Model/Websocket/ProtobufUtils.ts @@ -5,10 +5,11 @@ import { PointMessage, PositionMessage, } from "../../Messages/generated/messages_pb"; -import { CharacterLayer, ExSocketInterface } from "_Model/Websocket/ExSocketInterface"; +import { ExSocketInterface } from "_Model/Websocket/ExSocketInterface"; import Direction = PositionMessage.Direction; import { ItemEventMessageInterface } from "_Model/Websocket/ItemEventMessage"; import { PositionInterface } from "_Model/PositionInterface"; +import { WokaDetail } from "_Enum/PlayerTextures"; export class ProtobufUtils { public static toPositionMessage(point: PointInterface): PositionMessage { @@ -94,13 +95,16 @@ export class ProtobufUtils { return itemEventMessage; } - public static toCharacterLayerMessages(characterLayers: CharacterLayer[]): CharacterLayerMessage[] { + public static toCharacterLayerMessages(characterLayers: WokaDetail[]): CharacterLayerMessage[] { return characterLayers.map(function (characterLayer): CharacterLayerMessage { const message = new CharacterLayerMessage(); - message.setName(characterLayer.name); + message.setName(characterLayer.id); if (characterLayer.url) { message.setUrl(characterLayer.url); } + if (characterLayer.layer) { + message.setLayer(characterLayer.layer); + } return message; }); } diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index d3b4d414..540939e5 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -1,25 +1,33 @@ import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; -import Axios from "axios"; -import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; +import Axios, { AxiosResponse } from "axios"; import { MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { RoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; import { AdminApiData, isAdminApiData } from "../Messages/JsonMessages/AdminApiData"; +import * as tg from "generic-type-guard"; +import { isNumber } from "generic-type-guard"; +import { isWokaDetail } from "../Enum/PlayerTextures"; export interface AdminBannedData { is_banned: boolean; message: string; } -export interface FetchMemberDataByUuidResponse { - email: string; - userUuid: string; - tags: string[]; - visitCardUrl: string | null; - textures: CharacterTexture[]; - messages: unknown[]; - anonymous?: boolean; - userRoomToken: string | undefined; -} +const isFetchMemberDataByUuidResponse = new tg.IsInterface() + .withProperties({ + email: tg.isString, + userUuid: tg.isString, + tags: tg.isArray(tg.isString), + visitCardUrl: tg.isNullable(tg.isString), + textures: tg.isArray(isWokaDetail), + messages: tg.isArray(tg.isUnknown), + }) + .withOptionalProperties({ + anonymous: tg.isBoolean, + userRoomToken: tg.isString, + }) + .get(); + +export type FetchMemberDataByUuidResponse = tg.GuardedType; class AdminApi { /** @@ -52,10 +60,16 @@ class AdminApi { if (!ADMIN_API_URL) { return Promise.reject(new Error("No admin backoffice set!")); } - const res = await Axios.get(ADMIN_API_URL + "/api/room/access", { + const res = await Axios.get>(ADMIN_API_URL + "/api/room/access", { params: { userIdentifier, roomId, ipAddress }, headers: { Authorization: `${ADMIN_API_TOKEN}` }, }); + if (!isFetchMemberDataByUuidResponse(res.data)) { + throw new Error( + "Invalid answer received from the admin for the /api/map endpoint. Received: " + + JSON.stringify(res.data) + ); + } return res.data; } diff --git a/pusher/src/Services/AdminWokaService.ts b/pusher/src/Services/AdminWokaService.ts index 0598fb2a..a14458bc 100644 --- a/pusher/src/Services/AdminWokaService.ts +++ b/pusher/src/Services/AdminWokaService.ts @@ -1,6 +1,6 @@ import axios from "axios"; import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable"; -import { isWokaDetailsResult, isWokaList, WokaDetailsResult, WokaList } from "../Enum/PlayerTextures"; +import { isWokaList, WokaList } from "../Enum/PlayerTextures"; import { WokaServiceInterface } from "./WokaServiceInterface"; class AdminWokaService implements WokaServiceInterface { @@ -32,7 +32,7 @@ class AdminWokaService implements WokaServiceInterface { * * If one of the textures cannot be found, undefined is returned */ - fetchWokaDetails(textureIds: string[]): Promise { + /*fetchWokaDetails(textureIds: string[]): Promise { return axios .post( `${ADMIN_API_URL}/api/woka-details`, @@ -49,11 +49,11 @@ class AdminWokaService implements WokaServiceInterface { } const result: WokaDetailsResult = res.data; - if (result.details.length !== textureIds.length) { + if (result.length !== textureIds.length) { return undefined; } - for (const detail of result.details) { + for (const detail of result) { if (!detail.texture) { return undefined; } @@ -65,7 +65,7 @@ class AdminWokaService implements WokaServiceInterface { console.error(`Cannot get woka details from admin API with ids: ${textureIds}`, err); return undefined; }); - } + }*/ } export const adminWokaService = new AdminWokaService(); diff --git a/pusher/src/Services/LocalWokaService.ts b/pusher/src/Services/LocalWokaService.ts index 3356c48d..0f96fc3c 100644 --- a/pusher/src/Services/LocalWokaService.ts +++ b/pusher/src/Services/LocalWokaService.ts @@ -23,7 +23,13 @@ class LocalWokaService implements WokaServiceInterface { */ async fetchWokaDetails(textureIds: string[]): Promise { const wokaData: WokaList = await require("../../data/woka.json"); - const textures = new Map(); + const textures = new Map< + string, + { + url: string; + layer: string; + } + >(); const searchIds = new Set(textureIds); for (const part of wokaPartNames) { @@ -37,7 +43,10 @@ class LocalWokaService implements WokaServiceInterface { const texture = collection.textures.find((texture) => texture.id === id); if (texture) { - textures.set(id, texture.url); + textures.set(id, { + url: texture.url, + layer: part, + }); searchIds.delete(id); } } @@ -53,11 +62,12 @@ class LocalWokaService implements WokaServiceInterface { textures.forEach((value, key) => { details.push({ id: key, - texture: value, + url: value.url, + layer: value.layer, }); }); - return { details }; + return details; } } diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index b67c359d..981c580a 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -1,5 +1,5 @@ import { PusherRoom } from "../Model/PusherRoom"; -import { CharacterLayer, ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; +import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface"; import { AdminMessage, AdminPusherToBackMessage, @@ -38,6 +38,7 @@ import { ErrorMessage, WorldFullMessage, PlayerDetailsUpdatedMessage, + InvalidTextureMessage, } from "../Messages/generated/messages_pb"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { ADMIN_API_URL, JITSI_ISS, JITSI_URL, SECRET_JITSI_KEY } from "../Enum/EnvironmentVariable"; @@ -52,7 +53,7 @@ import Debug from "debug"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { WebSocket } from "uWebSockets.js"; import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; -import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; +//import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; import { compressors } from "hyper-express"; const debug = Debug("socket"); @@ -175,10 +176,13 @@ export class SocketManager implements ZoneEventListener { for (const characterLayer of client.characterLayers) { const characterLayerMessage = new CharacterLayerMessage(); - characterLayerMessage.setName(characterLayer.name); + characterLayerMessage.setName(characterLayer.id); if (characterLayer.url !== undefined) { characterLayerMessage.setUrl(characterLayer.url); } + if (characterLayer.layer !== undefined) { + characterLayerMessage.setLayer(characterLayer.layer); + } joinRoomMessage.addCharacterlayer(characterLayerMessage); } @@ -545,36 +549,6 @@ export class SocketManager implements ZoneEventListener { }); } - /** - * Merges the characterLayers received from the front (as an array of string) with the custom textures from the back. - */ - static mergeCharacterLayersAndCustomTextures( - characterLayers: string[], - memberTextures: CharacterTexture[] - ): CharacterLayer[] { - const characterLayerObjs: CharacterLayer[] = []; - for (const characterLayer of characterLayers) { - if (characterLayer.startsWith("customCharacterTexture")) { - const customCharacterLayerId: number = +characterLayer.substr(22); - for (const memberTexture of memberTextures) { - if (memberTexture.id == customCharacterLayerId) { - characterLayerObjs.push({ - name: characterLayer, - url: memberTexture.url, - }); - break; - } - } - } else { - characterLayerObjs.push({ - name: characterLayer, - url: undefined, - }); - } - } - return characterLayerObjs; - } - public onUserEnters(user: UserDescriptor, listener: ExSocketInterface): void { const subMessage = new SubMessage(); subMessage.setUserjoinedmessage(user.toUserJoinedMessage()); @@ -642,6 +616,17 @@ export class SocketManager implements ZoneEventListener { } } + public emitInvalidTextureMessage(client: compressors.WebSocket) { + const errorMessage = new InvalidTextureMessage(); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setInvalidtexturemessage(errorMessage); + + if (!client.disconnecting) { + client.send(serverToClientMessage.serializeBinary().buffer, true); + } + } + public emitConnexionErrorMessage(client: compressors.WebSocket, message: string) { const errorMessage = new WorldConnexionMessage(); errorMessage.setMessage(message); diff --git a/pusher/src/Services/WokaService.ts b/pusher/src/Services/WokaService.ts new file mode 100644 index 00000000..1944ab69 --- /dev/null +++ b/pusher/src/Services/WokaService.ts @@ -0,0 +1,5 @@ +import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; +import { adminWokaService } from "./AdminWokaService"; +import { localWokaService } from "./LocalWokaService"; + +export const wokaService = ADMIN_API_URL ? adminWokaService : localWokaService; diff --git a/pusher/src/Services/WokaServiceInterface.ts b/pusher/src/Services/WokaServiceInterface.ts index 71ee7202..7165a209 100644 --- a/pusher/src/Services/WokaServiceInterface.ts +++ b/pusher/src/Services/WokaServiceInterface.ts @@ -14,5 +14,5 @@ export interface WokaServiceInterface { * * If one of the textures cannot be found, undefined is returned (and the user should be redirected to Woka choice page!) */ - fetchWokaDetails(textureIds: string[]): Promise; + //fetchWokaDetails(textureIds: string[]): Promise; } From 8e11c4445b402aad417b66c2f0386c474c4e79c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 24 Feb 2022 10:25:14 +0100 Subject: [PATCH 09/61] Sending character layer details to admin --- pusher/package.json | 1 + .../src/Controller/AuthenticateController.ts | 2 +- pusher/src/Controller/IoSocketController.ts | 2 +- pusher/src/Services/AdminApi.ts | 10 +++-- pusher/yarn.lock | 43 +++++++++++++++++++ 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/pusher/package.json b/pusher/package.json index fc2d1bf0..e729a262 100644 --- a/pusher/package.json +++ b/pusher/package.json @@ -51,6 +51,7 @@ "mkdirp": "^1.0.4", "openid-client": "^4.7.4", "prom-client": "^12.0.0", + "qs": "^6.10.3", "query-string": "^6.13.3", "uuidv4": "^6.0.7" }, diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index d2e6c11a..c1003609 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -397,7 +397,7 @@ export class AuthenticateController extends BaseHttpController { userRoomToken: undefined, }; try { - data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress); + data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress, characterLayers); } catch (err) { console.error("openIDCallback => fetchMemberDataByUuid", err); } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 0400477a..5f37d708 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -297,7 +297,7 @@ export class IoSocketController { if (ADMIN_API_URL) { try { try { - userData = await adminApi.fetchMemberDataByUuid(userIdentifier, roomId, IPAddress); + userData = await adminApi.fetchMemberDataByUuid(userIdentifier, roomId, IPAddress, characterLayers); } catch (err) { if (Axios.isAxiosError(err)) { if (err?.response?.status == 404) { diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 540939e5..51ad61b6 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -6,6 +6,7 @@ import { AdminApiData, isAdminApiData } from "../Messages/JsonMessages/AdminApiD import * as tg from "generic-type-guard"; import { isNumber } from "generic-type-guard"; import { isWokaDetail } from "../Enum/PlayerTextures"; +import qs from "qs"; export interface AdminBannedData { is_banned: boolean; @@ -55,14 +56,17 @@ class AdminApi { async fetchMemberDataByUuid( userIdentifier: string | null, roomId: string, - ipAddress: string - ): Promise { + ipAddress: string, + characterLayers: string[]): Promise { if (!ADMIN_API_URL) { return Promise.reject(new Error("No admin backoffice set!")); } const res = await Axios.get>(ADMIN_API_URL + "/api/room/access", { - params: { userIdentifier, roomId, ipAddress }, + params: { userIdentifier, roomId, ipAddress, characterLayers }, headers: { Authorization: `${ADMIN_API_TOKEN}` }, + paramsSerializer: p => { + return qs.stringify(p, {arrayFormat: 'brackets'}) + } }); if (!isFetchMemberDataByUuidResponse(res.data)) { throw new Error( diff --git a/pusher/yarn.lock b/pusher/yarn.lock index 9254be50..d06edfd9 100644 --- a/pusher/yarn.lock +++ b/pusher/yarn.lock @@ -565,6 +565,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" @@ -1143,6 +1151,15 @@ generic-type-guard@^3.2.0: resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.5.0.tgz#39de9f8fceee65d79e7540959f0e7b23210c07b6" integrity sha512-OpgXv/sbRobhFboaSyN/Tsh97Sxt5pcfLLxCiYZgYIIWFFp+kn2EzAXiaQZKEVRlq1rOE/zh8cYhJXEwplbJiQ== +get-intrinsic@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -1242,6 +1259,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbols@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -1885,6 +1907,11 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + oidc-token-hash@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" @@ -2057,6 +2084,13 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +qs@^6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" + query-string@^6.13.3: version "6.14.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" @@ -2258,6 +2292,15 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" From 1b286002ff0caa7d6e77075af337a9dc642688c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 24 Feb 2022 12:06:43 +0100 Subject: [PATCH 10/61] Cleaning commented code --- messages/JsonMessages/AdminApiData.ts | 2 -- messages/JsonMessages/MapDetailsData.ts | 2 -- pusher/src/Controller/AuthenticateController.ts | 6 +----- pusher/src/Controller/IoSocketController.ts | 7 ++++++- pusher/src/Controller/MapController.ts | 9 --------- pusher/src/Model/Websocket/ExSocketInterface.ts | 2 -- pusher/src/Services/AdminApi.ts | 9 +++++---- 7 files changed, 12 insertions(+), 25 deletions(-) diff --git a/messages/JsonMessages/AdminApiData.ts b/messages/JsonMessages/AdminApiData.ts index 1fd7765e..236c6355 100644 --- a/messages/JsonMessages/AdminApiData.ts +++ b/messages/JsonMessages/AdminApiData.ts @@ -1,5 +1,4 @@ import * as tg from "generic-type-guard"; -//import { isCharacterTexture } from "./CharacterTexture"; /* * WARNING! The original file is in /messages/JsonMessages. @@ -12,7 +11,6 @@ export const isAdminApiData = new tg.IsInterface() email: tg.isNullable(tg.isString), roomUrl: tg.isString, mapUrlStart: tg.isString, -// textures: tg.isArray(isCharacterTexture), }) .withOptionalProperties({ messages: tg.isArray(tg.isUnknown), diff --git a/messages/JsonMessages/MapDetailsData.ts b/messages/JsonMessages/MapDetailsData.ts index 87b2b95a..09500b80 100644 --- a/messages/JsonMessages/MapDetailsData.ts +++ b/messages/JsonMessages/MapDetailsData.ts @@ -1,5 +1,4 @@ import * as tg from "generic-type-guard"; -//import { isCharacterTexture } from "./CharacterTexture"; import { isNumber } from "generic-type-guard"; /* @@ -12,7 +11,6 @@ export const isMapDetailsData = new tg.IsInterface() mapUrl: tg.isString, policy_type: isNumber, //isNumericEnum(GameRoomPolicyTypes), tags: tg.isArray(tg.isString), -// textures: tg.isArray(isCharacterTexture), authenticationMandatory: tg.isUnion(tg.isNullable(tg.isBoolean), tg.isUndefined), roomSlug: tg.isNullable(tg.isString), // deprecated contactPage: tg.isNullable(tg.isString), diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index c1003609..658367b4 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -249,10 +249,6 @@ export class AuthenticateController extends BaseHttpController { * type: string * description: TODO- unclear. I cannot find any use of this * example: ??? - * textures: - * type: string - * description: TODO - document this is still needed - * example: ??? * messages: * type: array * description: The list of messages to be displayed when the user logs? @@ -397,7 +393,7 @@ export class AuthenticateController extends BaseHttpController { userRoomToken: undefined, }; try { - data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress, characterLayers); + data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress, []); } catch (err) { console.error("openIDCallback => fetchMemberDataByUuid", err); } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 5f37d708..f07ec6b8 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -297,7 +297,12 @@ export class IoSocketController { if (ADMIN_API_URL) { try { try { - userData = await adminApi.fetchMemberDataByUuid(userIdentifier, roomId, IPAddress, characterLayers); + userData = await adminApi.fetchMemberDataByUuid( + userIdentifier, + roomId, + IPAddress, + characterLayers + ); } catch (err) { if (Axios.isAxiosError(err)) { if (err?.response?.status == 404) { diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index 1e709cc0..208efd48 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -59,14 +59,6 @@ export class MapController extends BaseHttpController { * items: * type: string * example: speaker - * textures: - * type: array - * description: The list of public textures for this map (TODO remove this) - * items: - * type: object - * properties: - * todo: - * type: string * authenticationMandatory: * type: boolean|null * description: Whether the authentication is mandatory or not for this map. @@ -126,7 +118,6 @@ export class MapController extends BaseHttpController { roomSlug: null, // Deprecated group: null, tags: [], - textures: [], contactPage: null, authenticationMandatory: DISABLE_ANONYMOUS, } as MapDetailsData); diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index b5815635..2b5d6225 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -8,10 +8,8 @@ import { ServerToClientMessage, SubMessage, } from "../../Messages/generated/messages_pb"; -import { WebSocket } from "uWebSockets.js"; import { ClientDuplexStream } from "grpc"; import { Zone } from "_Model/Zone"; -import { CharacterTexture } from "../../Messages/JsonMessages/CharacterTexture"; import { compressors } from "hyper-express"; import { WokaDetail } from "_Enum/PlayerTextures"; diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 51ad61b6..5db9dc69 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -57,16 +57,17 @@ class AdminApi { userIdentifier: string | null, roomId: string, ipAddress: string, - characterLayers: string[]): Promise { + characterLayers: string[] + ): Promise { if (!ADMIN_API_URL) { return Promise.reject(new Error("No admin backoffice set!")); } const res = await Axios.get>(ADMIN_API_URL + "/api/room/access", { params: { userIdentifier, roomId, ipAddress, characterLayers }, headers: { Authorization: `${ADMIN_API_TOKEN}` }, - paramsSerializer: p => { - return qs.stringify(p, {arrayFormat: 'brackets'}) - } + paramsSerializer: (p) => { + return qs.stringify(p, { arrayFormat: "brackets" }); + }, }); if (!isFetchMemberDataByUuidResponse(res.data)) { throw new Error( From c37046e138999da65f805d045c53380f441b8ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 24 Feb 2022 12:15:23 +0100 Subject: [PATCH 11/61] Fixing regex --- pusher/src/Controller/SwaggerController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Controller/SwaggerController.ts b/pusher/src/Controller/SwaggerController.ts index 616247ed..d9ea358e 100644 --- a/pusher/src/Controller/SwaggerController.ts +++ b/pusher/src/Controller/SwaggerController.ts @@ -38,7 +38,7 @@ export class SwaggerController extends BaseHttpController { if (err) { return response.status(500).send(err.message); } - const result = data.replace(/https:\/\/petstore.swagger.io\/v2\/swagger.json/g, "/openapi"); + const result = data.replace(/https:\/\/petstore\.swagger\.io\/v2\/swagger.json/g, "/openapi"); response.send(result); From 586e829e2e433738b7f8f1b95da276349bdb71d6 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 24 Feb 2022 14:10:23 +0100 Subject: [PATCH 12/61] Fix woka endpoints on pusher --- pusher/src/Controller/WokaListController.ts | 43 ++++++++++--------- pusher/src/Services/AdminWokaService.ts | 47 +-------------------- pusher/src/Services/LocalWokaService.ts | 2 +- pusher/src/Services/WokaServiceInterface.ts | 12 +----- 4 files changed, 27 insertions(+), 77 deletions(-) diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts index 0ce10966..e4e563f5 100644 --- a/pusher/src/Controller/WokaListController.ts +++ b/pusher/src/Controller/WokaListController.ts @@ -1,13 +1,33 @@ -import { hasToken } from "../Middleware/HasToken"; import { BaseHttpController } from "./BaseHttpController"; import { wokaService } from "../Services/WokaService"; +import * as tg from "generic-type-guard"; export class WokaListController extends BaseHttpController { routes() { // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.app.get("/woka-list", {}, async (req, res) => { + this.app.get("/woka/list/:roomId", {}, async (req, res) => { const token = req.header("Authorization"); - const wokaList = await wokaService.getWokaList(token); + + if (!token) { + res.status(401).send("Undefined authorization header"); + return; + } + + const isParameters = new tg.IsInterface() + .withProperties({ + roomId: tg.isString, + }) + .withOptionalProperties({ + messages: tg.isArray(tg.isUnknown), + }) + .get(); + + if (!isParameters(req.path_parameters)) { + return res.status(400).send("Unknown parameters"); + } + + const roomId = req.path_parameters.roomId; + const wokaList = await wokaService.getWokaList(roomId, token); if (!wokaList) { return res.status(500).send("Error on getting woka list"); @@ -15,22 +35,5 @@ export class WokaListController extends BaseHttpController { return res.status(200).json(wokaList); }); - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - /*this.app.post("/woka-details", async (req, res) => { - const body = await req.json(); - if (!body || !body.textureIds) { - return res.status(400); - } - - const textureIds = body.textureIds; - const wokaDetails = await wokaService.fetchWokaDetails(textureIds); - - if (!wokaDetails) { - return res.json({ details: [] }); - } - - return res.json(wokaDetails); - });*/ } } diff --git a/pusher/src/Services/AdminWokaService.ts b/pusher/src/Services/AdminWokaService.ts index a14458bc..acc10151 100644 --- a/pusher/src/Services/AdminWokaService.ts +++ b/pusher/src/Services/AdminWokaService.ts @@ -7,9 +7,9 @@ class AdminWokaService implements WokaServiceInterface { /** * Returns the list of all available Wokas for the current user. */ - getWokaList(token: string): Promise { + getWokaList(roomId: string, token: string): Promise { return axios - .get(`${ADMIN_API_URL}/api/woka-list/${token}`, { + .get(`${ADMIN_API_URL}/api/woka/list/${roomId}/${token}`, { headers: { Authorization: `${ADMIN_API_TOKEN}` }, }) .then((res) => { @@ -23,49 +23,6 @@ class AdminWokaService implements WokaServiceInterface { return undefined; }); } - - /** - * Returns the URL of all the images for the given texture ids. - * - * Key: texture id - * Value: URL - * - * If one of the textures cannot be found, undefined is returned - */ - /*fetchWokaDetails(textureIds: string[]): Promise { - return axios - .post( - `${ADMIN_API_URL}/api/woka-details`, - { - textureIds, - }, - { - headers: { Authorization: `${ADMIN_API_TOKEN}` }, - } - ) - .then((res) => { - if (isWokaDetailsResult(res.data)) { - throw new Error("Bad response format provided by woka detail endpoint"); - } - - const result: WokaDetailsResult = res.data; - if (result.length !== textureIds.length) { - return undefined; - } - - for (const detail of result) { - if (!detail.texture) { - return undefined; - } - } - - return res.data; - }) - .catch((err) => { - console.error(`Cannot get woka details from admin API with ids: ${textureIds}`, err); - return undefined; - }); - }*/ } export const adminWokaService = new AdminWokaService(); diff --git a/pusher/src/Services/LocalWokaService.ts b/pusher/src/Services/LocalWokaService.ts index 0f96fc3c..931ff4b3 100644 --- a/pusher/src/Services/LocalWokaService.ts +++ b/pusher/src/Services/LocalWokaService.ts @@ -5,7 +5,7 @@ class LocalWokaService implements WokaServiceInterface { /** * Returns the list of all available Wokas & Woka Parts for the current user. */ - async getWokaList(token: string): Promise { + async getWokaList(roomId: string, token: string): Promise { const wokaData: WokaList = await require("../../data/woka.json"); if (!wokaData) { return undefined; diff --git a/pusher/src/Services/WokaServiceInterface.ts b/pusher/src/Services/WokaServiceInterface.ts index 7165a209..47d3b8bd 100644 --- a/pusher/src/Services/WokaServiceInterface.ts +++ b/pusher/src/Services/WokaServiceInterface.ts @@ -4,15 +4,5 @@ export interface WokaServiceInterface { /** * Returns the list of all available Wokas for the current user. */ - getWokaList(token: string): Promise; - - /** - * Returns the URL of all the images for the given texture ids. - * - * Key: texture id - * Value: URL - * - * If one of the textures cannot be found, undefined is returned (and the user should be redirected to Woka choice page!) - */ - //fetchWokaDetails(textureIds: string[]): Promise; + getWokaList(roomId: string, token: string): Promise; } From 0543232bc3d5571b1e2a29f964e9adeaebcc1d7a Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 24 Feb 2022 14:54:36 +0100 Subject: [PATCH 13/61] Change error wording on pusher --- pusher/src/Services/AdminApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 5db9dc69..9ea84b2d 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -71,7 +71,7 @@ class AdminApi { }); if (!isFetchMemberDataByUuidResponse(res.data)) { throw new Error( - "Invalid answer received from the admin for the /api/map endpoint. Received: " + + "Invalid answer received from the admin for the /api/room/access endpoint. Received: " + JSON.stringify(res.data) ); } From da469b64d206a3472d7c115ca43fcfbd3ad3e594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 24 Feb 2022 21:09:19 +0100 Subject: [PATCH 14/61] Working on integration of the woka-list with the new admin endpoint. --- back/src/Messages/JsonMessages/.gitignore | 2 ++ back/src/Model/GameRoom.ts | 9 +++-- back/src/Services/AdminApi.ts | 4 +-- .../src/Services/AdminApi/CharacterTexture.ts | 11 ------- back/src/Services/AdminApi/MapDetailsData.ts | 21 ------------ back/src/Services/AdminApi/RoomRedirect.ts | 8 ----- front/src/Connexion/RoomConnection.ts | 33 ++++++++++++++----- front/src/Phaser/Game/GameScene.ts | 2 +- .../src/Phaser/Login/SelectCharacterScene.ts | 17 ++++++++-- messages/package.json | 3 +- .../src/Controller/AuthenticateController.ts | 2 +- pusher/src/Controller/WokaListController.ts | 26 +++++++++++---- pusher/src/Middleware/Cors.ts | 1 + pusher/src/Services/AdminWokaService.ts | 8 +++-- 14 files changed, 79 insertions(+), 68 deletions(-) create mode 100644 back/src/Messages/JsonMessages/.gitignore delete mode 100644 back/src/Services/AdminApi/CharacterTexture.ts delete mode 100644 back/src/Services/AdminApi/MapDetailsData.ts delete mode 100644 back/src/Services/AdminApi/RoomRedirect.ts diff --git a/back/src/Messages/JsonMessages/.gitignore b/back/src/Messages/JsonMessages/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/back/src/Messages/JsonMessages/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 7d7b24a5..0e8203cf 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -27,7 +27,7 @@ import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { RoomSocket, ZoneSocket } from "src/RoomManager"; import { Admin } from "../Model/Admin"; import { adminApi } from "../Services/AdminApi"; -import { isMapDetailsData, MapDetailsData } from "../Services/AdminApi/MapDetailsData"; +import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { ITiledMap } from "@workadventure/tiled-map-type-guard/dist"; import { mapFetcher } from "../Services/MapFetcher"; import { VariablesManager } from "../Services/VariablesManager"; @@ -35,7 +35,7 @@ import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import { LocalUrlError } from "../Services/LocalUrlError"; import { emitErrorOnRoomSocket } from "../Services/MessageHelpers"; import { VariableError } from "../Services/VariableError"; -import { isRoomRedirect } from "../Services/AdminApi/RoomRedirect"; +import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; export type ConnectCallback = (user: User, group: Group) => void; export type DisconnectCallback = (user: User, group: Group) => void; @@ -571,8 +571,11 @@ export class GameRoom { return { mapUrl, policy_type: 1, - textures: [], tags: [], + authenticationMandatory: null, + roomSlug: null, + contactPage: null, + group: null, }; } diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts index f4fa40b6..148877af 100644 --- a/back/src/Services/AdminApi.ts +++ b/back/src/Services/AdminApi.ts @@ -1,7 +1,7 @@ import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import Axios from "axios"; -import { isMapDetailsData, MapDetailsData } from "./AdminApi/MapDetailsData"; -import { isRoomRedirect, RoomRedirect } from "./AdminApi/RoomRedirect"; +import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; +import { isRoomRedirect, RoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; class AdminApi { async fetchMapDetails(playUri: string): Promise { diff --git a/back/src/Services/AdminApi/CharacterTexture.ts b/back/src/Services/AdminApi/CharacterTexture.ts deleted file mode 100644 index 055b3033..00000000 --- a/back/src/Services/AdminApi/CharacterTexture.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as tg from "generic-type-guard"; - -export const isCharacterTexture = new tg.IsInterface() - .withProperties({ - id: tg.isNumber, - level: tg.isNumber, - url: tg.isString, - rights: tg.isString, - }) - .get(); -export type CharacterTexture = tg.GuardedType; diff --git a/back/src/Services/AdminApi/MapDetailsData.ts b/back/src/Services/AdminApi/MapDetailsData.ts deleted file mode 100644 index d3402b92..00000000 --- a/back/src/Services/AdminApi/MapDetailsData.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as tg from "generic-type-guard"; -import { isCharacterTexture } from "./CharacterTexture"; -import { isAny, isNumber } from "generic-type-guard"; - -/*const isNumericEnum = - (vs: T) => - (v: any): v is T => - typeof v === "number" && v in vs;*/ - -export const isMapDetailsData = new tg.IsInterface() - .withProperties({ - mapUrl: tg.isString, - policy_type: isNumber, //isNumericEnum(GameRoomPolicyTypes), - tags: tg.isArray(tg.isString), - textures: tg.isArray(isCharacterTexture), - }) - .withOptionalProperties({ - roomSlug: tg.isUnion(tg.isString, tg.isNull), // deprecated - }) - .get(); -export type MapDetailsData = tg.GuardedType; diff --git a/back/src/Services/AdminApi/RoomRedirect.ts b/back/src/Services/AdminApi/RoomRedirect.ts deleted file mode 100644 index 7257ebd3..00000000 --- a/back/src/Services/AdminApi/RoomRedirect.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as tg from "generic-type-guard"; - -export const isRoomRedirect = new tg.IsInterface() - .withProperties({ - redirectUrl: tg.isString, - }) - .get(); -export type RoomRedirect = tg.GuardedType; diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 3ea8375b..f5896955 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -340,8 +340,21 @@ export class RoomConnection implements RoomConnection { this.userId = roomJoinedMessage.currentUserId; this.tags = roomJoinedMessage.tag; this._userRoomToken = roomJoinedMessage.userRoomToken; + + // If one of the URLs sent to us does not exist, let's go to the Woka selection screen. + for (const characterLayer of roomJoinedMessage.characterLayer) { + if (!characterLayer.url) { + this.goToSelectYourWokaScene(); + this.closed = true; + break; + } + } + if (this.closed) { + break; + } + const characterLayers = roomJoinedMessage.characterLayer.map( - this.mapCharactgerLayerToBodyResourceDescription.bind(this) + this.mapCharacterLayerToBodyResourceDescription.bind(this) ); this._roomJoinedMessageStream.next({ @@ -360,10 +373,7 @@ export class RoomConnection implements RoomConnection { break; } case "invalidTextureMessage": { - menuVisiblilityStore.set(false); - menuIconVisiblilityStore.set(false); - selectCharacterSceneVisibleStore.set(true); - gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); + this.goToSelectYourWokaScene(); this.closed = true; break; @@ -608,7 +618,7 @@ export class RoomConnection implements RoomConnection { }); }*/ - private mapCharactgerLayerToBodyResourceDescription( + private mapCharacterLayerToBodyResourceDescription( characterLayer: CharacterLayerMessage ): BodyResourceDescriptionInterface { return { @@ -624,9 +634,7 @@ export class RoomConnection implements RoomConnection { throw new Error("Invalid JOIN_ROOM message"); } - const characterLayers = message.characterLayers.map( - this.mapCharactgerLayerToBodyResourceDescription.bind(this) - ); + const characterLayers = message.characterLayers.map(this.mapCharacterLayerToBodyResourceDescription.bind(this)); const companion = message.companion; @@ -886,4 +894,11 @@ export class RoomConnection implements RoomConnection { public get userRoomToken(): string | undefined { return this._userRoomToken; } + + private goToSelectYourWokaScene(): void { + menuVisiblilityStore.set(false); + menuIconVisiblilityStore.set(false); + selectCharacterSceneVisibleStore.set(true); + gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); + } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 7248c2ed..b6d8e4fe 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1535,7 +1535,7 @@ ${escapedMessage} this.messageSubscription?.unsubscribe(); this.userInputManager.destroy(); this.pinchManager?.destroy(); - this.emoteManager.destroy(); + this.emoteManager?.destroy(); this.cameraManager.destroy(); this.peerStoreUnsubscribe(); this.emoteUnsubscribe(); diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 67cdc952..1708c634 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -43,9 +43,22 @@ export class SelectCharacterScene extends AbstractCharacterScene { } preload() { - const wokaMetadataKey = "woka-list"; + const wokaMetadataKey = "woka/list"; this.cache.json.remove(wokaMetadataKey); - this.load.json(wokaMetadataKey, `${PUSHER_URL}/${wokaMetadataKey}`); + + // FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!) + this.load.json( + wokaMetadataKey, + `${PUSHER_URL}/${wokaMetadataKey}/` + encodeURIComponent(window.location.href), + undefined, + { + responseType: "text", + headers: { + Authorization: localUserStore.getAuthToken() ?? "", + }, + withCredentials: true, + } + ); this.load.once(`filecomplete-json-${wokaMetadataKey}`, () => { this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get(wokaMetadataKey)); this.loadSelectSceneCharacters() diff --git a/messages/package.json b/messages/package.json index be11d915..524f9996 100644 --- a/messages/package.json +++ b/messages/package.json @@ -9,9 +9,10 @@ "copy-to-front-ts-proto": "sed 's/import { Observable } from \"rxjs\";/import type { Observable } from \"rxjs\";/g' ts-proto-generated/protos/messages.ts > ../front/src/Messages/ts-proto-generated/messages.ts", "copy-to-pusher": "rm -rf ../pusher/src/Messages/generated && cp -rf generated/ ../pusher/src/Messages/generated", "json-copy-to-pusher": "rm -rf ../pusher/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../pusher/src/Messages/JsonMessages/", + "json-copy-to-back": "rm -rf ../back/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../back/src/Messages/JsonMessages/", "json-copy-to-front": "rm -rf ../front/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../front/src/Messages/JsonMessages/", "precommit": "lint-staged", - "proto-all": "yarn run proto && yarn run ts-proto && yarn run copy-to-back && yarn run copy-to-front-ts-proto && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-front", + "proto-all": "yarn run proto && yarn run ts-proto && yarn run copy-to-back && yarn run copy-to-front-ts-proto && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-back && yarn run json-copy-to-front", "proto:watch": "yarn run proto-all; inotifywait -q -m -e close_write protos/messages.proto JsonMessages/ | while read -r filename event; do yarn run proto-all; done", "pretty": "yarn prettier --write 'JsonMessages/**/*.ts'", "pretty-check": "yarn prettier --check 'JsonMessages/**/*.ts'" diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 658367b4..110d2f6c 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -68,7 +68,7 @@ export class AuthenticateController extends BaseHttpController { ); res.status(302); res.setHeader("Location", loginUri); - return res; + return res.send(""); } catch (e) { console.error("openIDLogin => e", e); this.castErrorToResponse(e, res); diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts index e4e563f5..0789fda6 100644 --- a/pusher/src/Controller/WokaListController.ts +++ b/pusher/src/Controller/WokaListController.ts @@ -1,11 +1,16 @@ import { BaseHttpController } from "./BaseHttpController"; import { wokaService } from "../Services/WokaService"; import * as tg from "generic-type-guard"; +import { jwtTokenManager } from "../Services/JWTTokenManager"; export class WokaListController extends BaseHttpController { routes() { + this.app.options("/woka/list/:roomUrl", {}, async (req, res) => { + res.status(200).send(""); + }); + // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.app.get("/woka/list/:roomId", {}, async (req, res) => { + this.app.get("/woka/list/:roomUrl", {}, async (req, res) => { const token = req.header("Authorization"); if (!token) { @@ -13,12 +18,19 @@ export class WokaListController extends BaseHttpController { return; } + try { + const jwtData = jwtTokenManager.verifyJWTToken(token); + // Let's set the "uuid" param + req.params["uuid"] = jwtData.identifier; + } catch (e) { + console.error("Connection refused for token: " + token, e); + res.status(401).send("Invalid token sent"); + return; + } + const isParameters = new tg.IsInterface() .withProperties({ - roomId: tg.isString, - }) - .withOptionalProperties({ - messages: tg.isArray(tg.isUnknown), + roomUrl: tg.isString, }) .get(); @@ -26,8 +38,8 @@ export class WokaListController extends BaseHttpController { return res.status(400).send("Unknown parameters"); } - const roomId = req.path_parameters.roomId; - const wokaList = await wokaService.getWokaList(roomId, token); + const roomUrl = decodeURIComponent(req.path_parameters.roomUrl); + const wokaList = await wokaService.getWokaList(roomUrl, req.params["uuid"]); if (!wokaList) { return res.status(500).send("Error on getting woka list"); diff --git a/pusher/src/Middleware/Cors.ts b/pusher/src/Middleware/Cors.ts index 54fd32ad..9353d7a8 100644 --- a/pusher/src/Middleware/Cors.ts +++ b/pusher/src/Middleware/Cors.ts @@ -10,6 +10,7 @@ export function cors(req: Request, res: Response, next?: MiddlewareNext): Middle ); res.setHeader("access-control-allow-methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); res.setHeader("access-control-allow-origin", FRONT_URL); + res.setHeader("access-control-allow-credentials", "true"); if (next) { next(); diff --git a/pusher/src/Services/AdminWokaService.ts b/pusher/src/Services/AdminWokaService.ts index acc10151..96f276d9 100644 --- a/pusher/src/Services/AdminWokaService.ts +++ b/pusher/src/Services/AdminWokaService.ts @@ -7,10 +7,14 @@ class AdminWokaService implements WokaServiceInterface { /** * Returns the list of all available Wokas for the current user. */ - getWokaList(roomId: string, token: string): Promise { + getWokaList(roomUrl: string, token: string): Promise { return axios - .get(`${ADMIN_API_URL}/api/woka/list/${roomId}/${token}`, { + .get(`${ADMIN_API_URL}/api/woka/list`, { headers: { Authorization: `${ADMIN_API_TOKEN}` }, + params: { + roomUrl, + uuid: token, + }, }) .then((res) => { if (isWokaList(res.data)) { From 08fffab41097df0c5a68c8c7e485c989d3caf0f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 25 Feb 2022 17:05:34 +0100 Subject: [PATCH 15/61] Switching from "name" to "id" in texture object + using zod for woka/list validation --- front/src/Connexion/LocalUser.ts | 5 -- front/src/Connexion/RoomConnection.ts | 2 +- front/src/Phaser/Entity/Character.ts | 4 +- front/src/Phaser/Entity/PlayerTextures.ts | 7 +-- .../Entity/PlayerTexturesLoadingManager.ts | 18 +++---- front/src/Phaser/Login/CustomizeScene.ts | 6 +-- .../src/Phaser/Login/SelectCharacterScene.ts | 12 ++--- pusher/package.json | 3 +- pusher/src/Enum/PlayerTextures.ts | 53 ++++++++----------- pusher/src/Services/AdminWokaService.ts | 11 ++-- pusher/yarn.lock | 5 ++ 11 files changed, 57 insertions(+), 69 deletions(-) diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index fce7593d..cc86ac32 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -16,11 +16,6 @@ export function isUserNameValid(value: unknown): boolean { export function areCharacterLayersValid(value: string[] | null): boolean { if (!value || !value.length) return false; - for (let i = 0; i < value.length; i++) { - if (/^\w+$/.exec(value[i]) === null) { - return false; - } - } return true; } diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index f5896955..20b69d62 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -622,7 +622,7 @@ export class RoomConnection implements RoomConnection { characterLayer: CharacterLayerMessage ): BodyResourceDescriptionInterface { return { - name: characterLayer.name, + id: characterLayer.name, img: characterLayer.url, }; } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 79669d55..37d92b89 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -85,11 +85,11 @@ export abstract class Character extends Container implements OutlineableInterfac .catch(() => { return lazyLoadPlayerCharacterTextures(scene.load, [ { - name: "color_22", + id: "color_22", img: "resources/customisation/character_color/character_color21.png", }, { - name: "eyes_23", + id: "eyes_23", img: "resources/customisation/character_eyes/character_eyes23.png", }, ]).then((textures) => { diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index 657556b8..84157a92 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -5,7 +5,8 @@ export interface BodyResourceDescriptionListInterface { } export interface BodyResourceDescriptionInterface { - name: string; + id: string; + label: string; img: string; level?: number; } @@ -89,7 +90,7 @@ export class PlayerTextures { const resources: BodyResourceDescriptionListInterface = {}; for (const collection of category.collections) { for (const texture of collection.textures) { - resources[texture.id] = { name: texture.name, img: texture.url }; + resources[texture.id] = { id: texture.id, label: texture.name, img: texture.url }; } } return resources; @@ -97,5 +98,5 @@ export class PlayerTextures { } export const OBJECTS: BodyResourceDescriptionInterface[] = [ - { name: "teleportation", img: "resources/objects/teleportation.png" }, + { id: "teleportation", label: "Teleport", img: "resources/objects/teleportation.png" }, ]; diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 0f872e5c..165a6063 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -14,7 +14,7 @@ export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterf const layerArray: BodyResourceDescriptionInterface[] = []; Object.values(layer).forEach((textureDescriptor) => { layerArray.push(textureDescriptor); - load.spritesheet(textureDescriptor.name, textureDescriptor.img, { frameWidth: 32, frameHeight: 32 }); + load.spritesheet(textureDescriptor.id, textureDescriptor.img, { frameWidth: 32, frameHeight: 32 }); }); returnArray.push(layerArray); }); @@ -23,7 +23,7 @@ export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterf export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => { const returnArray = Object.values(PlayerTextures.PLAYER_RESOURCES); returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => { - load.spritesheet(playerResource.name, playerResource.img, { frameWidth: 32, frameHeight: 32 }); + load.spritesheet(playerResource.id, playerResource.img, { frameWidth: 32, frameHeight: 32 }); }); return returnArray; }; @@ -46,7 +46,7 @@ export const lazyLoadPlayerCharacterTextures = ( textures.forEach((texture) => { try { //TODO refactor - if (!loadPlugin.textureManager.exists(texture.name)) { + if (!loadPlugin.textureManager.exists(texture.id)) { promisesList.push( createLoadingPromise(loadPlugin, texture, { frameWidth: 32, @@ -69,7 +69,7 @@ export const lazyLoadPlayerCharacterTextures = ( //If the loading fail, we render the default model instead. return returnPromise.then((keys) => keys.map((key) => { - return typeof key !== "string" ? key.name : key; + return typeof key !== "string" ? key.id : key; }) ); }; @@ -80,22 +80,22 @@ export const createLoadingPromise = ( frameConfig: FrameConfig ) => { return new CancelablePromise((res, rej, cancel) => { - if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) { + if (loadPlugin.textureManager.exists(playerResourceDescriptor.id)) { return res(playerResourceDescriptor); } cancel(() => { loadPlugin.off("loaderror"); - loadPlugin.off("filecomplete-spritesheet-" + playerResourceDescriptor.name); + loadPlugin.off("filecomplete-spritesheet-" + playerResourceDescriptor.id); return; }); - loadPlugin.spritesheet(playerResourceDescriptor.name, playerResourceDescriptor.img, frameConfig); + loadPlugin.spritesheet(playerResourceDescriptor.id, playerResourceDescriptor.img, frameConfig); const errorCallback = (file: { src: string }) => { if (file.src !== playerResourceDescriptor.img) return; console.error("failed loading player resource: ", playerResourceDescriptor); rej(playerResourceDescriptor); - loadPlugin.off("filecomplete-spritesheet-" + playerResourceDescriptor.name, successCallback); + loadPlugin.off("filecomplete-spritesheet-" + playerResourceDescriptor.id, successCallback); loadPlugin.off("loaderror", errorCallback); }; const successCallback = () => { @@ -103,7 +103,7 @@ export const createLoadingPromise = ( res(playerResourceDescriptor); }; - loadPlugin.once("filecomplete-spritesheet-" + playerResourceDescriptor.name, successCallback); + loadPlugin.once("filecomplete-spritesheet-" + playerResourceDescriptor.id, successCallback); loadPlugin.on("loaderror", errorCallback); }); }; diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index b87e3640..e6c6a085 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -199,13 +199,13 @@ export class CustomizeScene extends AbstractCharacterScene { const children: Array = new Array(); for (let j = 0; j <= layerNumber; j++) { if (j === layerNumber) { - children.push(this.layers[j][selectedItem].name); + children.push(this.layers[j][selectedItem].id); } else { const layer = this.selectedLayers[j]; if (layer === undefined) { continue; } - children.push(this.layers[j][layer].name); + children.push(this.layers[j][layer].id); } } return children; @@ -283,7 +283,7 @@ export class CustomizeScene extends AbstractCharacterScene { let i = 0; for (const layerItem of this.selectedLayers) { if (layerItem !== undefined) { - layers.push(this.layers[i][layerItem].name); + layers.push(this.layers[i][layerItem].id); } i++; } diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 1708c634..6fd650b3 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -151,16 +151,16 @@ export class SelectCharacterScene extends AbstractCharacterScene { const playerResource = this.playerModels[i]; //check already exist texture - if (this.players.find((c) => c.texture.key === playerResource.name)) { + if (this.players.find((c) => c.texture.key === playerResource.id)) { continue; } const [middleX, middleY] = this.getCharacterPosition(); - const player = this.physics.add.sprite(middleX, middleY, playerResource.name, 0); + const player = this.physics.add.sprite(middleX, middleY, playerResource.id, 0); this.setUpPlayer(player, i); this.anims.create({ - key: playerResource.name, - frames: this.anims.generateFrameNumbers(playerResource.name, { start: 0, end: 11 }), + key: playerResource.id, + frames: this.anims.generateFrameNumbers(playerResource.id, { start: 0, end: 11 }), frameRate: 8, repeat: -1, }); @@ -185,7 +185,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.currentSelectUser = 0; } this.selectedPlayer = this.players[this.currentSelectUser]; - this.selectedPlayer.play(this.playerModels[this.currentSelectUser].name); + this.selectedPlayer.play(this.playerModels[this.currentSelectUser].id); } protected moveUser() { @@ -270,7 +270,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { protected updateSelectedPlayer(): void { this.selectedPlayer?.anims?.pause(this.selectedPlayer?.anims.currentAnim.frames[0]); const player = this.players[this.currentSelectUser]; - player?.play(this.playerModels[this.currentSelectUser].name); + player?.play(this.playerModels[this.currentSelectUser].id); this.selectedPlayer = player; localUserStore.setPlayerCharacterIndex(this.currentSelectUser); } diff --git a/pusher/package.json b/pusher/package.json index e729a262..6db02396 100644 --- a/pusher/package.json +++ b/pusher/package.json @@ -53,7 +53,8 @@ "prom-client": "^12.0.0", "qs": "^6.10.3", "query-string": "^6.13.3", - "uuidv4": "^6.0.7" + "uuidv4": "^6.0.7", + "zod": "^3.12.0" }, "devDependencies": { "@types/circular-json": "^0.4.0", diff --git a/pusher/src/Enum/PlayerTextures.ts b/pusher/src/Enum/PlayerTextures.ts index cc7c43e1..8c7407f9 100644 --- a/pusher/src/Enum/PlayerTextures.ts +++ b/pusher/src/Enum/PlayerTextures.ts @@ -1,46 +1,35 @@ import * as tg from "generic-type-guard"; +import { z } from "zod"; //The list of all the player textures, both the default models and the partial textures used for customization -export const isWokaTexture = new tg.IsInterface() - .withProperties({ - id: tg.isString, - name: tg.isString, - url: tg.isString, - position: tg.isNumber, - }) - .withOptionalProperties({ - tags: tg.isArray(tg.isString), - tintable: tg.isBoolean, - }) - .get(); +const wokaTexture = z.object({ + id: z.string(), + name: z.string(), + url: z.string(), + tags: z.array(z.string()).optional(), + tintable: z.boolean().optional(), +}); -export type WokaTexture = tg.GuardedType; +export type WokaTexture = z.infer; -export const isWokaTextureCollection = new tg.IsInterface() - .withProperties({ - name: tg.isString, - position: tg.isNumber, - textures: tg.isArray(isWokaTexture), - }) - .get(); +const wokaTextureCollection = z.object({ + name: z.string(), + textures: z.array(wokaTexture), +}); -export type WokaTextureCollection = tg.GuardedType; +export type WokaTextureCollection = z.infer; -export const isWokaPartType = new tg.IsInterface() - .withProperties({ - collections: tg.isArray(isWokaTextureCollection), - }) - .withOptionalProperties({ - required: tg.isBoolean, - }) - .get(); +const wokaPartType = z.object({ + collections: z.array(wokaTextureCollection), + required: z.boolean().optional(), +}); -export type WokaPartType = tg.GuardedType; +export type WokaPartType = z.infer; -export const isWokaList = new tg.IsInterface().withStringIndexSignature(isWokaPartType).get(); +export const wokaList = z.record(wokaPartType); -export type WokaList = tg.GuardedType; +export type WokaList = z.infer; export const wokaPartNames = ["woka", "body", "eyes", "hair", "clothes", "hat", "accessory"]; diff --git a/pusher/src/Services/AdminWokaService.ts b/pusher/src/Services/AdminWokaService.ts index 96f276d9..52261412 100644 --- a/pusher/src/Services/AdminWokaService.ts +++ b/pusher/src/Services/AdminWokaService.ts @@ -1,6 +1,6 @@ -import axios from "axios"; +import axios, { AxiosResponse } from "axios"; import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable"; -import { isWokaList, WokaList } from "../Enum/PlayerTextures"; +import { wokaList, WokaList } from "../Enum/PlayerTextures"; import { WokaServiceInterface } from "./WokaServiceInterface"; class AdminWokaService implements WokaServiceInterface { @@ -9,7 +9,7 @@ class AdminWokaService implements WokaServiceInterface { */ getWokaList(roomUrl: string, token: string): Promise { return axios - .get(`${ADMIN_API_URL}/api/woka/list`, { + .get>(`${ADMIN_API_URL}/api/woka/list`, { headers: { Authorization: `${ADMIN_API_TOKEN}` }, params: { roomUrl, @@ -17,10 +17,7 @@ class AdminWokaService implements WokaServiceInterface { }, }) .then((res) => { - if (isWokaList(res.data)) { - throw new Error("Bad response format provided by woka list endpoint"); - } - return res.data; + return wokaList.parse(res.data); }) .catch((err) => { console.error(`Cannot get woka list from admin API with token: ${token}`, err); diff --git a/pusher/yarn.lock b/pusher/yarn.lock index d06edfd9..7243f352 100644 --- a/pusher/yarn.lock +++ b/pusher/yarn.lock @@ -2834,3 +2834,8 @@ z-schema@^4.2.3: validator "^13.6.0" optionalDependencies: commander "^2.7.1" + +zod@^3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.12.0.tgz#84ba9f6bdb7835e2483982d5f52cfffcb6a00346" + integrity sha512-w+mmntgEL4hDDL5NLFdN6Fq2DSzxfmlSoJqiYE1/CApO8EkOCxvJvRYEVf8Vr/lRs3i6gqoiyFM6KRcWqqdBzQ== From 394f7216389e12a6a1a369161e71346e36e5f84b Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 25 Feb 2022 19:28:07 +0100 Subject: [PATCH 16/61] Add position on default woka data --- pusher/data/woka.json | 888 ++++++++++++++++++++++++++++-------------- 1 file changed, 592 insertions(+), 296 deletions(-) diff --git a/pusher/data/woka.json b/pusher/data/woka.json index ca036647..04cc9cf7 100644 --- a/pusher/data/woka.json +++ b/pusher/data/woka.json @@ -8,122 +8,146 @@ { "id": "male1", "name": "male1", - "url": "resources/characters/pipoya/Male 01-1.png" + "url": "resources/characters/pipoya/Male 01-1.png", + "position": 0 }, { "id": "male2", "name": "male2", - "url": "resources/characters/pipoya/Male 02-2.png" + "url": "resources/characters/pipoya/Male 02-2.png", + "position": 1 }, { "id": "male3", "name": "male3", - "url": "resources/characters/pipoya/Male 03-4.png" + "url": "resources/characters/pipoya/Male 03-4.png", + "position": 2 }, { "id": "male4", "name": "male4", - "url": "resources/characters/pipoya/Male 09-1.png" + "url": "resources/characters/pipoya/Male 09-1.png", + "position": 3 }, { "id": "male5", "name": "male5", - "url": "resources/characters/pipoya/Male 10-3.png" + "url": "resources/characters/pipoya/Male 10-3.png", + "position": 4 }, { "id": "male6", "name": "male6", - "url": "resources/characters/pipoya/Male 17-2.png" + "url": "resources/characters/pipoya/Male 17-2.png", + "position": 5 }, { "id": "male7", "name": "male7", - "url": "resources/characters/pipoya/Male 18-1.png" + "url": "resources/characters/pipoya/Male 18-1.png", + "position": 6 }, { "id": "male8", "name": "male8", - "url": "resources/characters/pipoya/Male 16-4.png" + "url": "resources/characters/pipoya/Male 16-4.png", + "position": 7 }, { "id": "male9", "name": "male9", - "url": "resources/characters/pipoya/Male 07-2.png" + "url": "resources/characters/pipoya/Male 07-2.png", + "position": 8 }, { "id": "male10", "name": "male10", - "url": "resources/characters/pipoya/Male 05-3.png" + "url": "resources/characters/pipoya/Male 05-3.png", + "position": 9 }, { "id": "male11", "name": "male11", - "url": "resources/characters/pipoya/Teacher male 02.png" + "url": "resources/characters/pipoya/Teacher male 02.png", + "position": 10 }, { "id": "male12", "name": "male12", - "url": "resources/characters/pipoya/su4 Student male 12.png" + "url": "resources/characters/pipoya/su4 Student male 12.png", + "position": 11 }, { "id": "female1", "name": "female1", - "url": "resources/characters/pipoya/Female 01-1.png" + "url": "resources/characters/pipoya/Female 01-1.png", + "position": 12 }, { "id": "female2", "name": "female2", - "url": "resources/characters/pipoya/Female 02-2.png" + "url": "resources/characters/pipoya/Female 02-2.png", + "position": 13 }, { "id": "female3", "name": "female3", - "url": "resources/characters/pipoya/Female 03-4.png" + "url": "resources/characters/pipoya/Female 03-4.png", + "position": 14 }, { "id": "female4", "name": "female4", - "url": "resources/characters/pipoya/Female 09-1.png" + "url": "resources/characters/pipoya/Female 09-1.png", + "position": 15 }, { "id": "female5", "name": "female5", - "url": "resources/characters/pipoya/Female 10-3.png" + "url": "resources/characters/pipoya/Female 10-3.png", + "position": 16 }, { "id": "female6", "name": "female6", - "url": "resources/characters/pipoya/Female 17-2.png" + "url": "resources/characters/pipoya/Female 17-2.png", + "position": 17 }, { "id": "female7", "name": "female7", - "url": "resources/characters/pipoya/Female 18-1.png" + "url": "resources/characters/pipoya/Female 18-1.png", + "position": 18 }, { "id": "female8", "name": "female8", - "url": "resources/characters/pipoya/Female 16-4.png" + "url": "resources/characters/pipoya/Female 16-4.png", + "position": 19 }, { "id": "female9", "name": "female9", - "url": "resources/characters/pipoya/Female 07-2.png" + "url": "resources/characters/pipoya/Female 07-2.png", + "position": 20 }, { "id": "female10", "name": "female10", - "url": "resources/characters/pipoya/Female 05-3.png" + "url": "resources/characters/pipoya/Female 05-3.png", + "position": 21 }, { "id": "female11", "name": "female11", - "url": "resources/characters/pipoya/Teacher fmale 02.png" + "url": "resources/characters/pipoya/Teacher fmale 02.png", + "position": 22 }, { "id": "female12", "name": "female12", - "url": "resources/characters/pipoya/su4 Student fmale 12.png" + "url": "resources/characters/pipoya/su4 Student fmale 12.png", + "position": 23 } ] } @@ -139,167 +163,200 @@ { "id": "body1", "name": "body1", - "url": "resources/customisation/character_color/character_color0.png" + "url": "resources/customisation/character_color/character_color0.png", + "position": 0 }, { "id": "body2", "name": "body2", - "url": "resources/customisation/character_color/character_color1.png" + "url": "resources/customisation/character_color/character_color1.png", + "position": 1 }, { "id": "body3", "name": "body3", - "url": "resources/customisation/character_color/character_color2.png" + "url": "resources/customisation/character_color/character_color2.png", + "position": 2 }, { "id": "body4", "name": "body4", - "url": "resources/customisation/character_color/character_color3.png" + "url": "resources/customisation/character_color/character_color3.png", + "position": 3 }, { "id": "body5", "name": "body5", - "url": "resources/customisation/character_color/character_color4.png" + "url": "resources/customisation/character_color/character_color4.png", + "position": 4 }, { "id": "body6", "name": "body6", - "url": "resources/customisation/character_color/character_color5.png" + "url": "resources/customisation/character_color/character_color5.png", + "position": 5 }, { "id": "body7", "name": "body7", - "url": "resources/customisation/character_color/character_color6.png" + "url": "resources/customisation/character_color/character_color6.png", + "position": 6 }, { "id": "body8", "name": "body8", - "url": "resources/customisation/character_color/character_color7.png" + "url": "resources/customisation/character_color/character_color7.png", + "position": 7 }, { "id": "body9", "name": "body9", - "url": "resources/customisation/character_color/character_color8.png" + "url": "resources/customisation/character_color/character_color8.png", + "position": 8 }, { "id": "body10", "name": "body10", - "url": "resources/customisation/character_color/character_color9.png" + "url": "resources/customisation/character_color/character_color9.png", + "position": 9 }, { "id": "body11", "name": "body11", - "url": "resources/customisation/character_color/character_color10.png" + "url": "resources/customisation/character_color/character_color10.png", + "position": 10 }, { "id": "body12", "name": "body12", - "url": "resources/customisation/character_color/character_color11.png" + "url": "resources/customisation/character_color/character_color11.png", + "position": 11 }, { "id": "body13", "name": "body13", - "url": "resources/customisation/character_color/character_color12.png" + "url": "resources/customisation/character_color/character_color12.png", + "position": 12 }, { "id": "body14", "name": "body14", - "url": "resources/customisation/character_color/character_color13.png" + "url": "resources/customisation/character_color/character_color13.png", + "position": 13 }, { "id": "body15", "name": "body15", - "url": "resources/customisation/character_color/character_color14.png" + "url": "resources/customisation/character_color/character_color14.png", + "position": 14 }, { "id": "body16", "name": "body16", - "url": "resources/customisation/character_color/character_color15.png" + "url": "resources/customisation/character_color/character_color15.png", + "position": 15 }, { "id": "body17", "name": "body17", - "url": "resources/customisation/character_color/character_color16.png" + "url": "resources/customisation/character_color/character_color16.png", + "position": 16 }, { "id": "body18", "name": "body18", - "url": "resources/customisation/character_color/character_color17.png" + "url": "resources/customisation/character_color/character_color17.png", + "position": 17 }, { "id": "body19", "name": "body19", - "url": "resources/customisation/character_color/character_color18.png" + "url": "resources/customisation/character_color/character_color18.png", + "position": 18 }, { "id": "body20", "name": "body20", - "url": "resources/customisation/character_color/character_color19.png" + "url": "resources/customisation/character_color/character_color19.png", + "position": 19 }, { "id": "body21", "name": "body21", - "url": "resources/customisation/character_color/character_color20.png" + "url": "resources/customisation/character_color/character_color20.png", + "position": 20 }, { "id": "body22", "name": "body22", - "url": "resources/customisation/character_color/character_color21.png" + "url": "resources/customisation/character_color/character_color21.png", + "position": 21 }, { "id": "body23", "name": "body23", - "url": "resources/customisation/character_color/character_color22.png" + "url": "resources/customisation/character_color/character_color22.png", + "position": 22 }, { "id": "body24", "name": "body24", - "url": "resources/customisation/character_color/character_color23.png" + "url": "resources/customisation/character_color/character_color23.png", + "position": 23 }, { "id": "body25", "name": "body25", - "url": "resources/customisation/character_color/character_color24.png" + "url": "resources/customisation/character_color/character_color24.png", + "position": 24 }, { "id": "body26", "name": "body26", - "url": "resources/customisation/character_color/character_color25.png" + "url": "resources/customisation/character_color/character_color25.png", + "position": 25 }, { "id": "body27", "name": "body27", - "url": "resources/customisation/character_color/character_color26.png" + "url": "resources/customisation/character_color/character_color26.png", + "position": 26 }, { "id": "body28", "name": "body28", - "url": "resources/customisation/character_color/character_color27.png" + "url": "resources/customisation/character_color/character_color27.png", + "position": 27 }, { "id": "body29", "name": "body29", - "url": "resources/customisation/character_color/character_color28.png" + "url": "resources/customisation/character_color/character_color28.png", + "position": 28 }, { "id": "body30", "name": "body30", - "url": "resources/customisation/character_color/character_color29.png" + "url": "resources/customisation/character_color/character_color29.png", + "position": 29 }, { "id": "body31", "name": "body31", - "url": "resources/customisation/character_color/character_color30.png" + "url": "resources/customisation/character_color/character_color30.png", + "position": 30 }, { "id": "body32", "name": "body32", - "url": "resources/customisation/character_color/character_color31.png" + "url": "resources/customisation/character_color/character_color31.png", + "position": 31 }, { "id": "body33", "name": "body33", - "url": "resources/customisation/character_color/character_color32.png" + "url": "resources/customisation/character_color/character_color32.png", + "position": 32 } ] } @@ -315,152 +372,182 @@ { "id": "eyes1", "name": "eyes1", - "url": "resources/customisation/character_eyes/character_eyes1.png" + "url": "resources/customisation/character_eyes/character_eyes1.png", + "position": 0 }, { "id": "eyes2", "name": "eyes2", - "url": "resources/customisation/character_eyes/character_eyes2.png" + "url": "resources/customisation/character_eyes/character_eyes2.png", + "position": 1 }, { "id": "eyes3", "name": "eyes3", - "url": "resources/customisation/character_eyes/character_eyes3.png" + "url": "resources/customisation/character_eyes/character_eyes3.png", + "position": 2 }, { "id": "eyes4", "name": "eyes4", - "url": "resources/customisation/character_eyes/character_eyes4.png" + "url": "resources/customisation/character_eyes/character_eyes4.png", + "position": 3 }, { "id": "eyes5", "name": "eyes5", - "url": "resources/customisation/character_eyes/character_eyes5.png" + "url": "resources/customisation/character_eyes/character_eyes5.png", + "position": 4 }, { "id": "eyes6", "name": "eyes6", - "url": "resources/customisation/character_eyes/character_eyes6.png" + "url": "resources/customisation/character_eyes/character_eyes6.png", + "position": 5 }, { "id": "eyes7", "name": "eyes7", - "url": "resources/customisation/character_eyes/character_eyes7.png" + "url": "resources/customisation/character_eyes/character_eyes7.png", + "position": 6 }, { "id": "eyes8", "name": "eyes8", - "url": "resources/customisation/character_eyes/character_eyes8.png" + "url": "resources/customisation/character_eyes/character_eyes8.png", + "position": 7 }, { "id": "eyes9", "name": "eyes9", - "url": "resources/customisation/character_eyes/character_eyes9.png" + "url": "resources/customisation/character_eyes/character_eyes9.png", + "position": 8 }, { "id": "eyes10", "name": "eyes10", - "url": "resources/customisation/character_eyes/character_eyes10.png" + "url": "resources/customisation/character_eyes/character_eyes10.png", + "position": 9 }, { "id": "eyes11", "name": "eyes11", - "url": "resources/customisation/character_eyes/character_eyes11.png" + "url": "resources/customisation/character_eyes/character_eyes11.png", + "position": 10 }, { "id": "eyes12", "name": "eyes12", - "url": "resources/customisation/character_eyes/character_eyes12.png" + "url": "resources/customisation/character_eyes/character_eyes12.png", + "position": 11 }, { "id": "eyes13", "name": "eyes13", - "url": "resources/customisation/character_eyes/character_eyes13.png" + "url": "resources/customisation/character_eyes/character_eyes13.png", + "position": 12 }, { "id": "eyes14", "name": "eyes14", - "url": "resources/customisation/character_eyes/character_eyes14.png" + "url": "resources/customisation/character_eyes/character_eyes14.png", + "position": 13 }, { "id": "eyes15", "name": "eyes15", - "url": "resources/customisation/character_eyes/character_eyes15.png" + "url": "resources/customisation/character_eyes/character_eyes15.png", + "position": 14 }, { "id": "eyes16", "name": "eyes16", - "url": "resources/customisation/character_eyes/character_eyes16.png" + "url": "resources/customisation/character_eyes/character_eyes16.png", + "position": 15 }, { "id": "eyes17", "name": "eyes17", - "url": "resources/customisation/character_eyes/character_eyes17.png" + "url": "resources/customisation/character_eyes/character_eyes17.png", + "position": 16 }, { "id": "eyes18", "name": "eyes18", - "url": "resources/customisation/character_eyes/character_eyes18.png" + "url": "resources/customisation/character_eyes/character_eyes18.png", + "position": 17 }, { "id": "eyes19", "name": "eyes19", - "url": "resources/customisation/character_eyes/character_eyes19.png" + "url": "resources/customisation/character_eyes/character_eyes19.png", + "position": 18 }, { "id": "eyes20", "name": "eyes20", - "url": "resources/customisation/character_eyes/character_eyes20.png" + "url": "resources/customisation/character_eyes/character_eyes20.png", + "position": 19 }, { "id": "eyes21", "name": "eyes21", - "url": "resources/customisation/character_eyes/character_eyes21.png" + "url": "resources/customisation/character_eyes/character_eyes21.png", + "position": 20 }, { "id": "eyes22", "name": "eyes22", - "url": "resources/customisation/character_eyes/character_eyes22.png" + "url": "resources/customisation/character_eyes/character_eyes22.png", + "position": 21 }, { "id": "eyes23", "name": "eyes23", - "url": "resources/customisation/character_eyes/character_eyes23.png" + "url": "resources/customisation/character_eyes/character_eyes23.png", + "position": 22 }, { "id": "eyes24", "name": "eyes24", - "url": "resources/customisation/character_eyes/character_eyes24.png" + "url": "resources/customisation/character_eyes/character_eyes24.png", + "position": 23 }, { "id": "eyes25", "name": "eyes25", - "url": "resources/customisation/character_eyes/character_eyes25.png" + "url": "resources/customisation/character_eyes/character_eyes25.png", + "position": 24 }, { "id": "eyes26", "name": "eyes26", - "url": "resources/customisation/character_eyes/character_eyes26.png" + "url": "resources/customisation/character_eyes/character_eyes26.png", + "position": 25 }, { "id": "eyes27", "name": "eyes27", - "url": "resources/customisation/character_eyes/character_eyes27.png" + "url": "resources/customisation/character_eyes/character_eyes27.png", + "position": 26 }, { "id": "eyes28", "name": "eyes28", - "url": "resources/customisation/character_eyes/character_eyes28.png" + "url": "resources/customisation/character_eyes/character_eyes28.png", + "position": 27 }, { "id": "eyes29", "name": "eyes29", - "url": "resources/customisation/character_eyes/character_eyes29.png" + "url": "resources/customisation/character_eyes/character_eyes29.png", + "position": 28 }, { "id": "eyes30", "name": "eyes30", - "url": "resources/customisation/character_eyes/character_eyes30.png" + "url": "resources/customisation/character_eyes/character_eyes30.png", + "position": 29 } ] } @@ -475,372 +562,446 @@ { "id": "hair1", "name": "hair1", - "url": "resources/customisation/character_hairs/character_hairs0.png" + "url": "resources/customisation/character_hairs/character_hairs0.png", + "position": 0 }, { "id": "hair2", "name": "hair2", - "url": "resources/customisation/character_hairs/character_hairs1.png" + "url": "resources/customisation/character_hairs/character_hairs1.png", + "position": 1 }, { "id": "hair3", "name": "hair3", - "url": "resources/customisation/character_hairs/character_hairs2.png" + "url": "resources/customisation/character_hairs/character_hairs2.png", + "position": 2 }, { "id": "hair4", "name": "hair4", - "url": "resources/customisation/character_hairs/character_hairs3.png" + "url": "resources/customisation/character_hairs/character_hairs3.png", + "position": 3 }, { "id": "hair5", "name": "hair5", - "url": "resources/customisation/character_hairs/character_hairs4.png" + "url": "resources/customisation/character_hairs/character_hairs4.png", + "position": 4 }, { "id": "hair6", "name": "hair6", - "url": "resources/customisation/character_hairs/character_hairs5.png" + "url": "resources/customisation/character_hairs/character_hairs5.png", + "position": 5 }, { "id": "hair7", "name": "hair7", - "url": "resources/customisation/character_hairs/character_hairs6.png" + "url": "resources/customisation/character_hairs/character_hairs6.png", + "position": 6 }, { "id": "hair8", "name": "hair8", - "url": "resources/customisation/character_hairs/character_hairs7.png" + "url": "resources/customisation/character_hairs/character_hairs7.png", + "position": 7 }, { "id": "hair9", "name": "hair9", - "url": "resources/customisation/character_hairs/character_hairs8.png" + "url": "resources/customisation/character_hairs/character_hairs8.png", + "position": 8 }, { "id": "hair10", "name": "hair10", - "url": "resources/customisation/character_hairs/character_hairs9.png" + "url": "resources/customisation/character_hairs/character_hairs9.png", + "position": 9 }, { "id": "hair11", "name": "hair11", - "url": "resources/customisation/character_hairs/character_hairs10.png" + "url": "resources/customisation/character_hairs/character_hairs10.png", + "position": 10 }, { "id": "hair12", "name": "hair12", - "url": "resources/customisation/character_hairs/character_hairs11.png" + "url": "resources/customisation/character_hairs/character_hairs11.png", + "position": 11 }, { "id": "hair13", "name": "hair13", - "url": "resources/customisation/character_hairs/character_hairs12.png" + "url": "resources/customisation/character_hairs/character_hairs12.png", + "position": 12 }, { "id": "hair14", "name": "hair14", - "url": "resources/customisation/character_hairs/character_hairs13.png" + "url": "resources/customisation/character_hairs/character_hairs13.png", + "position": 13 }, { "id": "hair15", "name": "hair15", - "url": "resources/customisation/character_hairs/character_hairs14.png" + "url": "resources/customisation/character_hairs/character_hairs14.png", + "position": 14 }, { "id": "hair16", "name": "hair16", - "url": "resources/customisation/character_hairs/character_hairs15.png" + "url": "resources/customisation/character_hairs/character_hairs15.png", + "position": 15 }, { "id": "hair17", "name": "hair17", - "url": "resources/customisation/character_hairs/character_hairs16.png" + "url": "resources/customisation/character_hairs/character_hairs16.png", + "position": 16 }, { "id": "hair18", "name": "hair18", - "url": "resources/customisation/character_hairs/character_hairs17.png" + "url": "resources/customisation/character_hairs/character_hairs17.png", + "position": 17 }, { "id": "hair19", "name": "hair19", - "url": "resources/customisation/character_hairs/character_hairs18.png" + "url": "resources/customisation/character_hairs/character_hairs18.png", + "position": 18 }, { "id": "hair20", "name": "hair20", - "url": "resources/customisation/character_hairs/character_hairs19.png" + "url": "resources/customisation/character_hairs/character_hairs19.png", + "position": 19 }, { "id": "hair21", "name": "hair21", - "url": "resources/customisation/character_hairs/character_hairs20.png" + "url": "resources/customisation/character_hairs/character_hairs20.png", + "position": 20 }, { "id": "hair22", "name": "hair22", - "url": "resources/customisation/character_hairs/character_hairs21.png" + "url": "resources/customisation/character_hairs/character_hairs21.png", + "position": 21 }, { "id": "hair23", "name": "hair23", - "url": "resources/customisation/character_hairs/character_hairs22.png" + "url": "resources/customisation/character_hairs/character_hairs22.png", + "position": 22 }, { "id": "hair24", "name": "hair24", - "url": "resources/customisation/character_hairs/character_hairs23.png" + "url": "resources/customisation/character_hairs/character_hairs23.png", + "position": 23 }, { "id": "hair25", "name": "hair25", - "url": "resources/customisation/character_hairs/character_hairs24.png" + "url": "resources/customisation/character_hairs/character_hairs24.png", + "position": 24 }, { "id": "hair26", "name": "hair26", - "url": "resources/customisation/character_hairs/character_hairs25.png" + "url": "resources/customisation/character_hairs/character_hairs25.png", + "position": 25 }, { "id": "hair27", "name": "hair27", - "url": "resources/customisation/character_hairs/character_hairs26.png" + "url": "resources/customisation/character_hairs/character_hairs26.png", + "position": 26 }, { "id": "hair28", "name": "hair28", - "url": "resources/customisation/character_hairs/character_hairs27.png" + "url": "resources/customisation/character_hairs/character_hairs27.png", + "position": 27 }, { "id": "hair29", "name": "hair29", - "url": "resources/customisation/character_hairs/character_hairs28.png" + "url": "resources/customisation/character_hairs/character_hairs28.png", + "position": 28 }, { "id": "hair30", "name": "hair30", - "url": "resources/customisation/character_hairs/character_hairs29.png" + "url": "resources/customisation/character_hairs/character_hairs29.png", + "position": 29 }, { "id": "hair31", "name": "hair31", - "url": "resources/customisation/character_hairs/character_hairs30.png" + "url": "resources/customisation/character_hairs/character_hairs30.png", + "position": 30 }, { "id": "hair32", "name": "hair32", - "url": "resources/customisation/character_hairs/character_hairs31.png" + "url": "resources/customisation/character_hairs/character_hairs31.png", + "position": 31 }, { "id": "hair33", "name": "hair33", - "url": "resources/customisation/character_hairs/character_hairs32.png" + "url": "resources/customisation/character_hairs/character_hairs32.png", + "position": 32 }, { "id": "hair34", "name": "hair34", - "url": "resources/customisation/character_hairs/character_hairs33.png" + "url": "resources/customisation/character_hairs/character_hairs33.png", + "position": 33 }, { "id": "hair35", "name": "hair35", - "url": "resources/customisation/character_hairs/character_hairs34.png" + "url": "resources/customisation/character_hairs/character_hairs34.png", + "position": 34 }, { "id": "hair36", "name": "hair36", - "url": "resources/customisation/character_hairs/character_hairs35.png" + "url": "resources/customisation/character_hairs/character_hairs35.png", + "position": 35 }, { "id": "hair37", "name": "hair37", - "url": "resources/customisation/character_hairs/character_hairs36.png" + "url": "resources/customisation/character_hairs/character_hairs36.png", + "position": 36 }, { "id": "hair38", "name": "hair38", - "url": "resources/customisation/character_hairs/character_hairs37.png" + "url": "resources/customisation/character_hairs/character_hairs37.png", + "position": 37 }, { "id": "hair39", "name": "hair39", - "url": "resources/customisation/character_hairs/character_hairs38.png" + "url": "resources/customisation/character_hairs/character_hairs38.png", + "position": 38 }, { "id": "hair40", "name": "hair40", - "url": "resources/customisation/character_hairs/character_hairs39.png" + "url": "resources/customisation/character_hairs/character_hairs39.png", + "position": 39 }, { "id": "hair41", "name": "hair41", - "url": "resources/customisation/character_hairs/character_hairs40.png" + "url": "resources/customisation/character_hairs/character_hairs40.png", + "position": 40 }, { "id": "hair42", "name": "hair42", - "url": "resources/customisation/character_hairs/character_hairs41.png" + "url": "resources/customisation/character_hairs/character_hairs41.png", + "position": 41 }, { "id": "hair43", "name": "hair43", - "url": "resources/customisation/character_hairs/character_hairs42.png" + "url": "resources/customisation/character_hairs/character_hairs42.png", + "position": 42 }, { "id": "hair44", "name": "hair44", - "url": "resources/customisation/character_hairs/character_hairs43.png" + "url": "resources/customisation/character_hairs/character_hairs43.png", + "position": 43 }, { "id": "hair45", "name": "hair45", - "url": "resources/customisation/character_hairs/character_hairs44.png" + "url": "resources/customisation/character_hairs/character_hairs44.png", + "position": 44 }, { "id": "hair46", "name": "hair46", - "url": "resources/customisation/character_hairs/character_hairs45.png" + "url": "resources/customisation/character_hairs/character_hairs45.png", + "position": 45 }, { "id": "hair47", "name": "hair47", - "url": "resources/customisation/character_hairs/character_hairs46.png" + "url": "resources/customisation/character_hairs/character_hairs46.png", + "position": 46 }, { "id": "hair48", "name": "hair48", - "url": "resources/customisation/character_hairs/character_hairs47.png" + "url": "resources/customisation/character_hairs/character_hairs47.png", + "position": 47 }, { "id": "hair49", "name": "hair49", - "url": "resources/customisation/character_hairs/character_hairs48.png" + "url": "resources/customisation/character_hairs/character_hairs48.png", + "position": 48 }, { "id": "hair50", "name": "hair50", - "url": "resources/customisation/character_hairs/character_hairs49.png" + "url": "resources/customisation/character_hairs/character_hairs49.png", + "position": 49 }, { "id": "hair51", "name": "hair51", - "url": "resources/customisation/character_hairs/character_hairs50.png" + "url": "resources/customisation/character_hairs/character_hairs50.png", + "position": 50 }, { "id": "hair52", "name": "hair52", - "url": "resources/customisation/character_hairs/character_hairs51.png" + "url": "resources/customisation/character_hairs/character_hairs51.png", + "position": 51 }, { "id": "hair53", "name": "hair53", - "url": "resources/customisation/character_hairs/character_hairs52.png" + "url": "resources/customisation/character_hairs/character_hairs52.png", + "position": 52 }, { "id": "hair54", "name": "hair54", - "url": "resources/customisation/character_hairs/character_hairs53.png" + "url": "resources/customisation/character_hairs/character_hairs53.png", + "position": 53 }, { "id": "hair55", "name": "hair55", - "url": "resources/customisation/character_hairs/character_hairs54.png" + "url": "resources/customisation/character_hairs/character_hairs54.png", + "position": 54 }, { "id": "hair56", "name": "hair56", - "url": "resources/customisation/character_hairs/character_hairs55.png" + "url": "resources/customisation/character_hairs/character_hairs55.png", + "position": 55 }, { "id": "hair57", "name": "hair57", - "url": "resources/customisation/character_hairs/character_hairs56.png" + "url": "resources/customisation/character_hairs/character_hairs56.png", + "position": 56 }, { "id": "hair58", "name": "hair58", - "url": "resources/customisation/character_hairs/character_hairs57.png" + "url": "resources/customisation/character_hairs/character_hairs57.png", + "position": 57 }, { "id": "hair59", "name": "hair59", - "url": "resources/customisation/character_hairs/character_hairs58.png" + "url": "resources/customisation/character_hairs/character_hairs58.png", + "position": 58 }, { "id": "hair60", "name": "hair60", - "url": "resources/customisation/character_hairs/character_hairs59.png" + "url": "resources/customisation/character_hairs/character_hairs59.png", + "position": 59 }, { "id": "hair61", "name": "hair61", - "url": "resources/customisation/character_hairs/character_hairs60.png" + "url": "resources/customisation/character_hairs/character_hairs60.png", + "position": 60 }, { "id": "hair62", "name": "hair62", - "url": "resources/customisation/character_hairs/character_hairs61.png" + "url": "resources/customisation/character_hairs/character_hairs61.png", + "position": 61 }, { "id": "hair63", "name": "hair63", - "url": "resources/customisation/character_hairs/character_hairs62.png" + "url": "resources/customisation/character_hairs/character_hairs62.png", + "position": 62 }, { "id": "hair64", "name": "hair64", - "url": "resources/customisation/character_hairs/character_hairs63.png" + "url": "resources/customisation/character_hairs/character_hairs63.png", + "position": 63 }, { "id": "hair65", "name": "hair65", - "url": "resources/customisation/character_hairs/character_hairs64.png" + "url": "resources/customisation/character_hairs/character_hairs64.png", + "position": 64 }, { "id": "hair66", "name": "hair66", - "url": "resources/customisation/character_hairs/character_hairs65.png" + "url": "resources/customisation/character_hairs/character_hairs65.png", + "position": 65 }, { "id": "hair67", "name": "hair67", - "url": "resources/customisation/character_hairs/character_hairs66.png" + "url": "resources/customisation/character_hairs/character_hairs66.png", + "position": 66 }, { "id": "hair68", "name": "hair68", - "url": "resources/customisation/character_hairs/character_hairs67.png" + "url": "resources/customisation/character_hairs/character_hairs67.png", + "position": 67 }, { "id": "hair69", "name": "hair69", - "url": "resources/customisation/character_hairs/character_hairs68.png" + "url": "resources/customisation/character_hairs/character_hairs68.png", + "position": 68 }, { "id": "hair70", "name": "hair70", - "url": "resources/customisation/character_hairs/character_hairs69.png" + "url": "resources/customisation/character_hairs/character_hairs69.png", + "position": 69 }, { "id": "hair71", "name": "hair71", - "url": "resources/customisation/character_hairs/character_hairs70.png" + "url": "resources/customisation/character_hairs/character_hairs70.png", + "position": 70 }, { "id": "hair72", "name": "hair72", - "url": "resources/customisation/character_hairs/character_hairs71.png" + "url": "resources/customisation/character_hairs/character_hairs71.png", + "position": 71 }, { "id": "hair73", "name": "hair73", - "url": "resources/customisation/character_hairs/character_hairs72.png" + "url": "resources/customisation/character_hairs/character_hairs72.png", + "position": 72 }, { "id": "hair74", "name": "hair74", - "url": "resources/customisation/character_hairs/character_hairs73.png" + "url": "resources/customisation/character_hairs/character_hairs73.png", + "position": 73 } ] } @@ -855,372 +1016,446 @@ { "id": "clothes1", "name": "clothes1", - "url": "resources/customisation/character_clothes/character_clothes0.png" + "url": "resources/customisation/character_clothes/character_clothes0.png", + "position": 0 }, { "id": "clothes2", "name": "clothes2", - "url": "resources/customisation/character_clothes/character_clothes1.png" + "url": "resources/customisation/character_clothes/character_clothes1.png", + "position": 1 }, { "id": "clothes3", "name": "clothes3", - "url": "resources/customisation/character_clothes/character_clothes2.png" + "url": "resources/customisation/character_clothes/character_clothes2.png", + "position": 2 }, { "id": "clothes4", "name": "clothes4", - "url": "resources/customisation/character_clothes/character_clothes3.png" + "url": "resources/customisation/character_clothes/character_clothes3.png", + "position": 3 }, { "id": "clothes5", "name": "clothes5", - "url": "resources/customisation/character_clothes/character_clothes4.png" + "url": "resources/customisation/character_clothes/character_clothes4.png", + "position": 4 }, { "id": "clothes6", "name": "clothes6", - "url": "resources/customisation/character_clothes/character_clothes5.png" + "url": "resources/customisation/character_clothes/character_clothes5.png", + "position": 5 }, { "id": "clothes7", "name": "clothes7", - "url": "resources/customisation/character_clothes/character_clothes6.png" + "url": "resources/customisation/character_clothes/character_clothes6.png", + "position": 6 }, { "id": "clothes8", "name": "clothes8", - "url": "resources/customisation/character_clothes/character_clothes7.png" + "url": "resources/customisation/character_clothes/character_clothes7.png", + "position": 7 }, { "id": "clothes9", "name": "clothes9", - "url": "resources/customisation/character_clothes/character_clothes8.png" + "url": "resources/customisation/character_clothes/character_clothes8.png", + "position": 8 }, { "id": "clothes10", "name": "clothes10", - "url": "resources/customisation/character_clothes/character_clothes9.png" + "url": "resources/customisation/character_clothes/character_clothes9.png", + "position": 9 }, { "id": "clothes11", "name": "clothes11", - "url": "resources/customisation/character_clothes/character_clothes10.png" + "url": "resources/customisation/character_clothes/character_clothes10.png", + "position": 10 }, { "id": "clothes12", "name": "clothes12", - "url": "resources/customisation/character_clothes/character_clothes11.png" + "url": "resources/customisation/character_clothes/character_clothes11.png", + "position": 11 }, { "id": "clothes13", "name": "clothes13", - "url": "resources/customisation/character_clothes/character_clothes12.png" + "url": "resources/customisation/character_clothes/character_clothes12.png", + "position": 12 }, { "id": "clothes14", "name": "clothes14", - "url": "resources/customisation/character_clothes/character_clothes13.png" + "url": "resources/customisation/character_clothes/character_clothes13.png", + "position": 13 }, { "id": "clothes15", "name": "clothes15", - "url": "resources/customisation/character_clothes/character_clothes14.png" + "url": "resources/customisation/character_clothes/character_clothes14.png", + "position": 14 }, { "id": "clothes16", "name": "clothes16", - "url": "resources/customisation/character_clothes/character_clothes15.png" + "url": "resources/customisation/character_clothes/character_clothes15.png", + "position": 15 }, { "id": "clothes17", "name": "clothes17", - "url": "resources/customisation/character_clothes/character_clothes16.png" + "url": "resources/customisation/character_clothes/character_clothes16.png", + "position": 16 }, { "id": "clothes18", "name": "clothes18", - "url": "resources/customisation/character_clothes/character_clothes17.png" + "url": "resources/customisation/character_clothes/character_clothes17.png", + "position": 17 }, { "id": "clothes19", "name": "clothes19", - "url": "resources/customisation/character_clothes/character_clothes18.png" + "url": "resources/customisation/character_clothes/character_clothes18.png", + "position": 18 }, { "id": "clothes20", "name": "clothes20", - "url": "resources/customisation/character_clothes/character_clothes19.png" + "url": "resources/customisation/character_clothes/character_clothes19.png", + "position": 19 }, { "id": "clothes21", "name": "clothes21", - "url": "resources/customisation/character_clothes/character_clothes20.png" + "url": "resources/customisation/character_clothes/character_clothes20.png", + "position": 20 }, { "id": "clothes22", "name": "clothes22", - "url": "resources/customisation/character_clothes/character_clothes21.png" + "url": "resources/customisation/character_clothes/character_clothes21.png", + "position": 21 }, { "id": "clothes23", "name": "clothes23", - "url": "resources/customisation/character_clothes/character_clothes22.png" + "url": "resources/customisation/character_clothes/character_clothes22.png", + "position": 22 }, { "id": "clothes24", "name": "clothes24", - "url": "resources/customisation/character_clothes/character_clothes23.png" + "url": "resources/customisation/character_clothes/character_clothes23.png", + "position": 23 }, { "id": "clothes25", "name": "clothes25", - "url": "resources/customisation/character_clothes/character_clothes24.png" + "url": "resources/customisation/character_clothes/character_clothes24.png", + "position": 24 }, { "id": "clothes26", "name": "clothes26", - "url": "resources/customisation/character_clothes/character_clothes25.png" + "url": "resources/customisation/character_clothes/character_clothes25.png", + "position": 25 }, { "id": "clothes27", "name": "clothes27", - "url": "resources/customisation/character_clothes/character_clothes26.png" + "url": "resources/customisation/character_clothes/character_clothes26.png", + "position": 26 }, { "id": "clothes28", "name": "clothes28", - "url": "resources/customisation/character_clothes/character_clothes27.png" + "url": "resources/customisation/character_clothes/character_clothes27.png", + "position": 27 }, { "id": "clothes29", "name": "clothes29", - "url": "resources/customisation/character_clothes/character_clothes28.png" + "url": "resources/customisation/character_clothes/character_clothes28.png", + "position": 28 }, { "id": "clothes30", "name": "clothes30", - "url": "resources/customisation/character_clothes/character_clothes29.png" + "url": "resources/customisation/character_clothes/character_clothes29.png", + "position": 29 }, { "id": "clothes31", "name": "clothes31", - "url": "resources/customisation/character_clothes/character_clothes30.png" + "url": "resources/customisation/character_clothes/character_clothes30.png", + "position": 30 }, { "id": "clothes32", "name": "clothes32", - "url": "resources/customisation/character_clothes/character_clothes31.png" + "url": "resources/customisation/character_clothes/character_clothes31.png", + "position": 31 }, { "id": "clothes33", "name": "clothes33", - "url": "resources/customisation/character_clothes/character_clothes32.png" + "url": "resources/customisation/character_clothes/character_clothes32.png", + "position": 32 }, { "id": "clothes34", "name": "clothes34", - "url": "resources/customisation/character_clothes/character_clothes33.png" + "url": "resources/customisation/character_clothes/character_clothes33.png", + "position": 33 }, { "id": "clothes35", "name": "clothes35", - "url": "resources/customisation/character_clothes/character_clothes34.png" + "url": "resources/customisation/character_clothes/character_clothes34.png", + "position": 34 }, { "id": "clothes36", "name": "clothes36", - "url": "resources/customisation/character_clothes/character_clothes35.png" + "url": "resources/customisation/character_clothes/character_clothes35.png", + "position": 35 }, { "id": "clothes37", "name": "clothes37", - "url": "resources/customisation/character_clothes/character_clothes36.png" + "url": "resources/customisation/character_clothes/character_clothes36.png", + "position": 36 }, { "id": "clothes38", "name": "clothes38", - "url": "resources/customisation/character_clothes/character_clothes37.png" + "url": "resources/customisation/character_clothes/character_clothes37.png", + "position": 37 }, { "id": "clothes39", "name": "clothes39", - "url": "resources/customisation/character_clothes/character_clothes38.png" + "url": "resources/customisation/character_clothes/character_clothes38.png", + "position": 38 }, { "id": "clothes40", "name": "clothes40", - "url": "resources/customisation/character_clothes/character_clothes39.png" + "url": "resources/customisation/character_clothes/character_clothes39.png", + "position": 39 }, { "id": "clothes41", "name": "clothes41", - "url": "resources/customisation/character_clothes/character_clothes40.png" + "url": "resources/customisation/character_clothes/character_clothes40.png", + "position": 40 }, { "id": "clothes42", "name": "clothes42", - "url": "resources/customisation/character_clothes/character_clothes41.png" + "url": "resources/customisation/character_clothes/character_clothes41.png", + "position": 41 }, { "id": "clothes43", "name": "clothes43", - "url": "resources/customisation/character_clothes/character_clothes42.png" + "url": "resources/customisation/character_clothes/character_clothes42.png", + "position": 42 }, { "id": "clothes44", "name": "clothes44", - "url": "resources/customisation/character_clothes/character_clothes43.png" + "url": "resources/customisation/character_clothes/character_clothes43.png", + "position": 43 }, { "id": "clothes45", "name": "clothes45", - "url": "resources/customisation/character_clothes/character_clothes44.png" + "url": "resources/customisation/character_clothes/character_clothes44.png", + "position": 44 }, { "id": "clothes46", "name": "clothes46", - "url": "resources/customisation/character_clothes/character_clothes45.png" + "url": "resources/customisation/character_clothes/character_clothes45.png", + "position": 45 }, { "id": "clothes47", "name": "clothes47", - "url": "resources/customisation/character_clothes/character_clothes46.png" + "url": "resources/customisation/character_clothes/character_clothes46.png", + "position": 46 }, { "id": "clothes48", "name": "clothes48", - "url": "resources/customisation/character_clothes/character_clothes47.png" + "url": "resources/customisation/character_clothes/character_clothes47.png", + "position": 47 }, { "id": "clothes49", "name": "clothes49", - "url": "resources/customisation/character_clothes/character_clothes48.png" + "url": "resources/customisation/character_clothes/character_clothes48.png", + "position": 48 }, { "id": "clothes50", "name": "clothes50", - "url": "resources/customisation/character_clothes/character_clothes49.png" + "url": "resources/customisation/character_clothes/character_clothes49.png", + "position": 49 }, { "id": "clothes51", "name": "clothes51", - "url": "resources/customisation/character_clothes/character_clothes50.png" + "url": "resources/customisation/character_clothes/character_clothes50.png", + "position": 50 }, { "id": "clothes52", "name": "clothes52", - "url": "resources/customisation/character_clothes/character_clothes51.png" + "url": "resources/customisation/character_clothes/character_clothes51.png", + "position": 51 }, { "id": "clothes53", "name": "clothes53", - "url": "resources/customisation/character_clothes/character_clothes52.png" + "url": "resources/customisation/character_clothes/character_clothes52.png", + "position": 52 }, { "id": "clothes54", "name": "clothes54", - "url": "resources/customisation/character_clothes/character_clothes53.png" + "url": "resources/customisation/character_clothes/character_clothes53.png", + "position": 53 }, { "id": "clothes55", "name": "clothes55", - "url": "resources/customisation/character_clothes/character_clothes54.png" + "url": "resources/customisation/character_clothes/character_clothes54.png", + "position": 54 }, { "id": "clothes56", "name": "clothes56", - "url": "resources/customisation/character_clothes/character_clothes55.png" + "url": "resources/customisation/character_clothes/character_clothes55.png", + "position": 55 }, { "id": "clothes57", "name": "clothes57", - "url": "resources/customisation/character_clothes/character_clothes56.png" + "url": "resources/customisation/character_clothes/character_clothes56.png", + "position": 56 }, { "id": "clothes58", "name": "clothes58", - "url": "resources/customisation/character_clothes/character_clothes57.png" + "url": "resources/customisation/character_clothes/character_clothes57.png", + "position": 57 }, { "id": "clothes59", "name": "clothes59", - "url": "resources/customisation/character_clothes/character_clothes58.png" + "url": "resources/customisation/character_clothes/character_clothes58.png", + "position": 58 }, { "id": "clothes60", "name": "clothes60", - "url": "resources/customisation/character_clothes/character_clothes59.png" + "url": "resources/customisation/character_clothes/character_clothes59.png", + "position": 59 }, { "id": "clothes61", "name": "clothes61", - "url": "resources/customisation/character_clothes/character_clothes60.png" + "url": "resources/customisation/character_clothes/character_clothes60.png", + "position": 60 }, { "id": "clothes62", "name": "clothes62", - "url": "resources/customisation/character_clothes/character_clothes61.png" + "url": "resources/customisation/character_clothes/character_clothes61.png", + "position": 61 }, { "id": "clothes63", "name": "clothes63", - "url": "resources/customisation/character_clothes/character_clothes62.png" + "url": "resources/customisation/character_clothes/character_clothes62.png", + "position": 62 }, { "id": "clothes64", "name": "clothes64", - "url": "resources/customisation/character_clothes/character_clothes63.png" + "url": "resources/customisation/character_clothes/character_clothes63.png", + "position": 63 }, { "id": "clothes65", "name": "clothes65", - "url": "resources/customisation/character_clothes/character_clothes64.png" + "url": "resources/customisation/character_clothes/character_clothes64.png", + "position": 64 }, { "id": "clothes66", "name": "clothes66", - "url": "resources/customisation/character_clothes/character_clothes65.png" + "url": "resources/customisation/character_clothes/character_clothes65.png", + "position": 65 }, { "id": "clothes67", "name": "clothes67", - "url": "resources/customisation/character_clothes/character_clothes66.png" + "url": "resources/customisation/character_clothes/character_clothes66.png", + "position": 66 }, { "id": "clothes68", "name": "clothes68", - "url": "resources/customisation/character_clothes/character_clothes67.png" + "url": "resources/customisation/character_clothes/character_clothes67.png", + "position": 67 }, { "id": "clothes69", "name": "clothes69", - "url": "resources/customisation/character_clothes/character_clothes68.png" + "url": "resources/customisation/character_clothes/character_clothes68.png", + "position": 68 }, { "id": "clothes70", "name": "clothes70", - "url": "resources/customisation/character_clothes/character_clothes69.png" + "url": "resources/customisation/character_clothes/character_clothes69.png", + "position": 69 }, { "id": "clothes_pride_shirt", "name": "clothes_pride_shirt", - "url": "resources/customisation/character_clothes/pride_shirt.png" + "url": "resources/customisation/character_clothes/pride_shirt.png", + "position": 70 }, { "id": "clothes_black_hoodie", "name": "clothes_black_hoodie", - "url": "resources/customisation/character_clothes/black_hoodie.png" + "url": "resources/customisation/character_clothes/black_hoodie.png", + "position": 71 }, { "id": "clothes_white_hoodie", "name": "clothes_white_hoodie", - "url": "resources/customisation/character_clothes/white_hoodie.png" + "url": "resources/customisation/character_clothes/white_hoodie.png", + "position": 72 }, { "id": "clothes_engelbert", "name": "clothes_engelbert", - "url": "resources/customisation/character_clothes/engelbert.png" + "url": "resources/customisation/character_clothes/engelbert.png", + "position": 73 } ] } @@ -1235,137 +1470,164 @@ { "id": "hat1", "name": "hat1", - "url": "resources/customisation/character_hats/character_hats1.png" + "url": "resources/customisation/character_hats/character_hats1.png", + "position": 0 }, { "id": "hat2", "name": "hat2", - "url": "resources/customisation/character_hats/character_hats2.png" + "url": "resources/customisation/character_hats/character_hats2.png", + "position": 1 }, { "id": "hat3", "name": "hat3", - "url": "resources/customisation/character_hats/character_hats3.png" + "url": "resources/customisation/character_hats/character_hats3.png", + "position": 2 }, { "id": "hat4", "name": "hat4", - "url": "resources/customisation/character_hats/character_hats4.png" + "url": "resources/customisation/character_hats/character_hats4.png", + "position": 3 }, { "id": "hat5", "name": "hat5", - "url": "resources/customisation/character_hats/character_hats5.png" + "url": "resources/customisation/character_hats/character_hats5.png", + "position": 4 }, { "id": "hat6", "name": "hat6", - "url": "resources/customisation/character_hats/character_hats6.png" + "url": "resources/customisation/character_hats/character_hats6.png", + "position": 5 }, { "id": "hat7", "name": "hat7", - "url": "resources/customisation/character_hats/character_hats7.png" + "url": "resources/customisation/character_hats/character_hats7.png", + "position": 6 }, { "id": "hat8", "name": "hat8", - "url": "resources/customisation/character_hats/character_hats8.png" + "url": "resources/customisation/character_hats/character_hats8.png", + "position": 7 }, { "id": "hat9", "name": "hat9", - "url": "resources/customisation/character_hats/character_hats9.png" + "url": "resources/customisation/character_hats/character_hats9.png", + "position": 8 }, { "id": "hat10", "name": "hat10", - "url": "resources/customisation/character_hats/character_hats10.png" + "url": "resources/customisation/character_hats/character_hats10.png", + "position": 9 }, { "id": "hat11", "name": "hat11", - "url": "resources/customisation/character_hats/character_hats11.png" + "url": "resources/customisation/character_hats/character_hats11.png", + "position": 10 }, { "id": "hat12", "name": "hat12", - "url": "resources/customisation/character_hats/character_hats12.png" + "url": "resources/customisation/character_hats/character_hats12.png", + "position": 11 }, { "id": "hat13", "name": "hat13", - "url": "resources/customisation/character_hats/character_hats13.png" + "url": "resources/customisation/character_hats/character_hats13.png", + "position": 12 }, { "id": "hat14", "name": "hat14", - "url": "resources/customisation/character_hats/character_hats14.png" + "url": "resources/customisation/character_hats/character_hats14.png", + "position": 13 }, { "id": "hat15", "name": "hat15", - "url": "resources/customisation/character_hats/character_hats15.png" + "url": "resources/customisation/character_hats/character_hats15.png", + "position": 14 }, { "id": "hat16", "name": "hat16", - "url": "resources/customisation/character_hats/character_hats16.png" + "url": "resources/customisation/character_hats/character_hats16.png", + "position": 15 }, { "id": "hat17", "name": "hat17", - "url": "resources/customisation/character_hats/character_hats17.png" + "url": "resources/customisation/character_hats/character_hats17.png", + "position": 16 }, { "id": "hat18", "name": "hat18", - "url": "resources/customisation/character_hats/character_hats18.png" + "url": "resources/customisation/character_hats/character_hats18.png", + "position": 17 }, { "id": "hat19", "name": "hat19", - "url": "resources/customisation/character_hats/character_hats19.png" + "url": "resources/customisation/character_hats/character_hats19.png", + "position": 18 }, { "id": "hat20", "name": "hat20", - "url": "resources/customisation/character_hats/character_hats20.png" + "url": "resources/customisation/character_hats/character_hats20.png", + "position": 19 }, { "id": "hat21", "name": "hat21", - "url": "resources/customisation/character_hats/character_hats21.png" + "url": "resources/customisation/character_hats/character_hats21.png", + "position": 20 }, { "id": "hat22", "name": "hat22", - "url": "resources/customisation/character_hats/character_hats22.png" + "url": "resources/customisation/character_hats/character_hats22.png", + "position": 21 }, { "id": "hat23", "name": "hat23", - "url": "resources/customisation/character_hats/character_hats23.png" + "url": "resources/customisation/character_hats/character_hats23.png", + "position": 22 }, { "id": "hat24", "name": "hat24", - "url": "resources/customisation/character_hats/character_hats24.png" + "url": "resources/customisation/character_hats/character_hats24.png", + "position": 23 }, { "id": "hat25", "name": "hat25", - "url": "resources/customisation/character_hats/character_hats25.png" + "url": "resources/customisation/character_hats/character_hats25.png", + "position": 24 }, { "id": "hat26", "name": "hat26", - "url": "resources/customisation/character_hats/character_hats26.png" + "url": "resources/customisation/character_hats/character_hats26.png", + "position": 25 }, { "id": "tinfoil_hat1", "name": "tinfoil_hat1", - "url": "resources/customisation/character_hats/tinfoil_hat1.png" + "url": "resources/customisation/character_hats/tinfoil_hat1.png", + "position": 26 } ] } @@ -1381,172 +1643,206 @@ { "id": "accessory1", "name": "accessory1", - "url": "resources/customisation/character_accessories/character_accessories1.png" + "url": "resources/customisation/character_accessories/character_accessories1.png", + "position": 0 }, { "id": "accessory2", "name": "accessory2", - "url": "resources/customisation/character_accessories/character_accessories2.png" + "url": "resources/customisation/character_accessories/character_accessories2.png", + "position": 1 }, { "id": "accessory3", "name": "accessory3", - "url": "resources/customisation/character_accessories/character_accessories3.png" + "url": "resources/customisation/character_accessories/character_accessories3.png", + "position": 2 }, { "id": "accessory4", "name": "accessory4", - "url": "resources/customisation/character_accessories/character_accessories4.png" + "url": "resources/customisation/character_accessories/character_accessories4.png", + "position": 3 }, { "id": "accessory5", "name": "accessory5", - "url": "resources/customisation/character_accessories/character_accessories5.png" + "url": "resources/customisation/character_accessories/character_accessories5.png", + "position": 4 }, { "id": "accessory6", "name": "accessory6", - "url": "resources/customisation/character_accessories/character_accessories6.png" + "url": "resources/customisation/character_accessories/character_accessories6.png", + "position": 5 }, { "id": "accessory7", "name": "accessory7", - "url": "resources/customisation/character_accessories/character_accessories7.png" + "url": "resources/customisation/character_accessories/character_accessories7.png", + "position": 6 }, { "id": "accessory8", "name": "accessory8", - "url": "resources/customisation/character_accessories/character_accessories8.png" + "url": "resources/customisation/character_accessories/character_accessories8.png", + "position": 7 }, { "id": "accessory9", "name": "accessory9", - "url": "resources/customisation/character_accessories/character_accessories9.png" + "url": "resources/customisation/character_accessories/character_accessories9.png", + "position": 8 }, { "id": "accessory10", "name": "accessory10", - "url": "resources/customisation/character_accessories/character_accessories10.png" + "url": "resources/customisation/character_accessories/character_accessories10.png", + "position": 9 }, { "id": "accessory11", "name": "accessory11", - "url": "resources/customisation/character_accessories/character_accessories11.png" + "url": "resources/customisation/character_accessories/character_accessories11.png", + "position": 10 }, { "id": "accessory12", "name": "accessory12", - "url": "resources/customisation/character_accessories/character_accessories12.png" + "url": "resources/customisation/character_accessories/character_accessories12.png", + "position": 11 }, { "id": "accessory13", "name": "accessory13", - "url": "resources/customisation/character_accessories/character_accessories13.png" + "url": "resources/customisation/character_accessories/character_accessories13.png", + "position": 12 }, { "id": "accessory14", "name": "accessory14", - "url": "resources/customisation/character_accessories/character_accessories14.png" + "url": "resources/customisation/character_accessories/character_accessories14.png", + "position": 13 }, { "id": "accessory15", "name": "accessory15", - "url": "resources/customisation/character_accessories/character_accessories15.png" + "url": "resources/customisation/character_accessories/character_accessories15.png", + "position": 14 }, { "id": "accessory16", "name": "accessory16", - "url": "resources/customisation/character_accessories/character_accessories16.png" + "url": "resources/customisation/character_accessories/character_accessories16.png", + "position": 15 }, { "id": "accessory17", "name": "accessory17", - "url": "resources/customisation/character_accessories/character_accessories17.png" + "url": "resources/customisation/character_accessories/character_accessories17.png", + "position": 16 }, { "id": "accessory18", "name": "accessory18", - "url": "resources/customisation/character_accessories/character_accessories18.png" + "url": "resources/customisation/character_accessories/character_accessories18.png", + "position": 17 }, { "id": "accessory19", "name": "accessory19", - "url": "resources/customisation/character_accessories/character_accessories19.png" + "url": "resources/customisation/character_accessories/character_accessories19.png", + "position": 18 }, { "id": "accessory20", "name": "accessory20", - "url": "resources/customisation/character_accessories/character_accessories20.png" + "url": "resources/customisation/character_accessories/character_accessories20.png", + "position": 19 }, { "id": "accessory21", "name": "accessory21", - "url": "resources/customisation/character_accessories/character_accessories21.png" + "url": "resources/customisation/character_accessories/character_accessories21.png", + "position": 20 }, { "id": "accessory22", "name": "accessory22", - "url": "resources/customisation/character_accessories/character_accessories22.png" + "url": "resources/customisation/character_accessories/character_accessories22.png", + "position": 21 }, { "id": "accessory23", "name": "accessory23", - "url": "resources/customisation/character_accessories/character_accessories23.png" + "url": "resources/customisation/character_accessories/character_accessories23.png", + "position": 22 }, { "id": "accessory24", "name": "accessory24", - "url": "resources/customisation/character_accessories/character_accessories24.png" + "url": "resources/customisation/character_accessories/character_accessories24.png", + "position": 23 }, { "id": "accessory25", "name": "accessory25", - "url": "resources/customisation/character_accessories/character_accessories25.png" + "url": "resources/customisation/character_accessories/character_accessories25.png", + "position": 24 }, { "id": "accessory26", "name": "accessory26", - "url": "resources/customisation/character_accessories/character_accessories26.png" + "url": "resources/customisation/character_accessories/character_accessories26.png", + "position": 25 }, { "id": "accessory27", "name": "accessory27", - "url": "resources/customisation/character_accessories/character_accessories27.png" + "url": "resources/customisation/character_accessories/character_accessories27.png", + "position": 26 }, { "id": "accessory28", "name": "accessory28", - "url": "resources/customisation/character_accessories/character_accessories28.png" + "url": "resources/customisation/character_accessories/character_accessories28.png", + "position": 27 }, { "id": "accessory29", "name": "accessory29", - "url": "resources/customisation/character_accessories/character_accessories29.png" + "url": "resources/customisation/character_accessories/character_accessories29.png", + "position": 28 }, { "id": "accessory30", "name": "accessory30", - "url": "resources/customisation/character_accessories/character_accessories30.png" + "url": "resources/customisation/character_accessories/character_accessories30.png", + "position": 29 }, { "id": "accessory31", "name": "accessory31", - "url": "resources/customisation/character_accessories/character_accessories31.png" + "url": "resources/customisation/character_accessories/character_accessories31.png", + "position": 30 }, { "id": "accessory32", "name": "accessory32", - "url": "resources/customisation/character_accessories/character_accessories32.png" + "url": "resources/customisation/character_accessories/character_accessories32.png", + "position": 31 }, { "id": "accessory_mate_bottle", "name": "accessory_mate_bottle", - "url": "resources/customisation/character_accessories/mate_bottle1.png" + "url": "resources/customisation/character_accessories/mate_bottle1.png", + "position": 32 }, { "id": "accessory_mask", "name": "accessory_mask", - "url": "resources/customisation/character_accessories/mask.png" + "url": "resources/customisation/character_accessories/mask.png", + "position": 33 } ] } From 4091606f75620537035926be1710a0d40f8a26d6 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 28 Feb 2022 12:09:15 +0100 Subject: [PATCH 17/61] Remove async on pusher option method --- pusher/src/Controller/WokaListController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts index 0789fda6..04be10f4 100644 --- a/pusher/src/Controller/WokaListController.ts +++ b/pusher/src/Controller/WokaListController.ts @@ -5,7 +5,7 @@ import { jwtTokenManager } from "../Services/JWTTokenManager"; export class WokaListController extends BaseHttpController { routes() { - this.app.options("/woka/list/:roomUrl", {}, async (req, res) => { + this.app.options("/woka/list/:roomUrl", {}, (req, res) => { res.status(200).send(""); }); From 9300b053357c3adbb8621f54644cd9bf3a412686 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 4 Mar 2022 10:50:44 +0100 Subject: [PATCH 18/61] Fix woka list url --- front/src/Phaser/Login/CustomizeScene.ts | 2 +- front/src/Phaser/Login/SelectCharacterScene.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index e6c6a085..3bd0c71d 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -43,7 +43,7 @@ export class CustomizeScene extends AbstractCharacterScene { preload() { const wokaMetadataKey = "woka-list"; this.cache.json.remove(wokaMetadataKey); - this.load.json(wokaMetadataKey, `${PUSHER_URL}/${wokaMetadataKey}`); + this.load.json(wokaMetadataKey, `${PUSHER_URL}/woka/list`); this.load.once(`filecomplete-json-${wokaMetadataKey}`, () => { this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get(wokaMetadataKey)); this.loadCustomSceneSelectCharacters() diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 6fd650b3..f2af9cb9 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -43,13 +43,13 @@ export class SelectCharacterScene extends AbstractCharacterScene { } preload() { - const wokaMetadataKey = "woka/list"; + const wokaMetadataKey = "woka-list"; this.cache.json.remove(wokaMetadataKey); // FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!) this.load.json( wokaMetadataKey, - `${PUSHER_URL}/${wokaMetadataKey}/` + encodeURIComponent(window.location.href), + `${PUSHER_URL}/woka/list/` + encodeURIComponent(window.location.href), undefined, { responseType: "text", From 3760b9c4ad8c5afe241af99815b967ad15448471 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 8 Mar 2022 15:52:48 +0100 Subject: [PATCH 19/61] add options for /register --- pusher/src/Controller/AuthenticateController.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 110d2f6c..964b4027 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -255,6 +255,9 @@ export class AuthenticateController extends BaseHttpController { * example: ??? */ private register() { + this.app.options("/register", {}, (req, res) => { + res.status(200).send(""); + }); this.app.post("/register", (req, res) => { (async () => { const param = await req.json(); From 674c5bdeb8ceca2b17a6157040da0752f5659c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 9 Mar 2022 11:22:33 +0100 Subject: [PATCH 20/61] Fxiing loading the Woka list --- front/src/Phaser/Login/CustomizeScene.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 3bd0c71d..9227d3e3 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -43,7 +43,19 @@ export class CustomizeScene extends AbstractCharacterScene { preload() { const wokaMetadataKey = "woka-list"; this.cache.json.remove(wokaMetadataKey); - this.load.json(wokaMetadataKey, `${PUSHER_URL}/woka/list`); + // FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!) + this.load.json( + wokaMetadataKey, + `${PUSHER_URL}/woka/list/` + encodeURIComponent(window.location.href), + undefined, + { + responseType: "text", + headers: { + Authorization: localUserStore.getAuthToken() ?? "", + }, + withCredentials: true, + } + ); this.load.once(`filecomplete-json-${wokaMetadataKey}`, () => { this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get(wokaMetadataKey)); this.loadCustomSceneSelectCharacters() From e5095db04852d44f9debfeafc4014a7c1a07f73a Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 9 Mar 2022 12:45:04 +0100 Subject: [PATCH 21/61] remove customize button if no body parts are available --- .../selectCharacter/SelectCharacterScene.svelte | 13 ++++++++----- front/src/Phaser/Entity/PlayerTextures.ts | 3 +++ front/src/Phaser/Login/SelectCharacterScene.ts | 11 +++++++++++ front/src/Stores/SelectCharacterSceneStore.ts | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 front/src/Stores/SelectCharacterSceneStore.ts diff --git a/front/src/Components/selectCharacter/SelectCharacterScene.svelte b/front/src/Components/selectCharacter/SelectCharacterScene.svelte index 9f6e5f9c..807cf88f 100644 --- a/front/src/Components/selectCharacter/SelectCharacterScene.svelte +++ b/front/src/Components/selectCharacter/SelectCharacterScene.svelte @@ -2,6 +2,7 @@ import type { Game } from "../../Phaser/Game/Game"; import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene"; import LL from "../../i18n/i18n-svelte"; + import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore"; export let game: Game; @@ -40,11 +41,13 @@ class="selectCharacterSceneFormSubmit nes-btn is-primary" on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()} - + {#if $customizeAvailableStore} + + {/if} diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index 84157a92..d41fc9f6 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -88,6 +88,9 @@ export class PlayerTextures { private getMappedResources(category: PlayerTexturesCategory): BodyResourceDescriptionListInterface { const resources: BodyResourceDescriptionListInterface = {}; + if (!category) { + return {}; + } for (const collection of category.collections) { for (const texture of collection.textures) { resources[texture.id] = { id: texture.id, label: texture.name, img: texture.url }; diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index f2af9cb9..5fe0ecc9 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -15,6 +15,7 @@ import { waScaleManager } from "../Services/WaScaleManager"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; +import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore"; //todo: put this constants in a dedicated file export const SelectCharacterSceneName = "SelectCharacterScene"; @@ -78,6 +79,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { } create() { + customizeAvailableStore.set(this.isCustomizationAvailable()); selectCharacterSceneVisibleStore.set(true); this.events.addListener("wake", () => { waScaleManager.saveZoom(); @@ -295,4 +297,13 @@ export class SelectCharacterScene extends AbstractCharacterScene { //move position of user this.moveUser(); } + + private isCustomizationAvailable(): boolean { + for (const layer of PlayerTextures.LAYERS) { + if (Object.keys(layer).length > 0) { + return true; + } + } + return false; + } } diff --git a/front/src/Stores/SelectCharacterSceneStore.ts b/front/src/Stores/SelectCharacterSceneStore.ts new file mode 100644 index 00000000..654da03c --- /dev/null +++ b/front/src/Stores/SelectCharacterSceneStore.ts @@ -0,0 +1,3 @@ +import { writable } from "svelte/store"; + +export const customizeAvailableStore = writable(false); From fb05037594b4a84d0c7d712cb1489ec866666885 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 9 Mar 2022 14:11:15 +0100 Subject: [PATCH 22/61] remove unused position field from PlayerTexturesCollection interface --- front/src/Phaser/Entity/PlayerTextures.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index d41fc9f6..3d8e2b89 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -43,7 +43,6 @@ interface PlayerTexturesCategory { interface PlayerTexturesCollection { name: string; - position: number; textures: PlayerTexturesRecord[]; } From b3e896d9fc3d5ecadc6e451abc6ff04e117ae643 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 10 Mar 2022 09:24:31 +0100 Subject: [PATCH 23/61] removed unused label field --- front/src/Phaser/Entity/PlayerTextures.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index 3d8e2b89..035ddf8d 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -6,7 +6,6 @@ export interface BodyResourceDescriptionListInterface { export interface BodyResourceDescriptionInterface { id: string; - label: string; img: string; level?: number; } @@ -92,7 +91,7 @@ export class PlayerTextures { } for (const collection of category.collections) { for (const texture of collection.textures) { - resources[texture.id] = { id: texture.id, label: texture.name, img: texture.url }; + resources[texture.id] = { id: texture.id, img: texture.url }; } } return resources; @@ -100,5 +99,5 @@ export class PlayerTextures { } export const OBJECTS: BodyResourceDescriptionInterface[] = [ - { id: "teleportation", label: "Teleport", img: "resources/objects/teleportation.png" }, + { id: "teleportation", img: "resources/objects/teleportation.png" }, ]; From 060cdf3310198a3dbe33d6f5d47d7a54e9abf8d9 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 10 Mar 2022 09:50:41 +0100 Subject: [PATCH 24/61] fix LocalUser test --- front/src/Connexion/LocalUser.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index cc86ac32..4db0d6d7 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -15,7 +15,14 @@ export function isUserNameValid(value: unknown): boolean { } export function areCharacterLayersValid(value: string[] | null): boolean { - if (!value || !value.length) return false; + if (!value || !value.length) { + return false; + } + for (const layerName of value) { + if (layerName.length === 0 || layerName === " ") { + return false; + } + } return true; } From 9f823506b980319b9eded1607db6dc4258150680 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 10 Mar 2022 16:18:47 +0100 Subject: [PATCH 25/61] little PlayerTextures class refactor --- front/src/Connexion/ConnexionModels.ts | 2 - front/src/Phaser/Entity/PlayerTextures.ts | 71 ++++++++++++------- .../Entity/PlayerTexturesLoadingManager.ts | 16 +++-- .../Phaser/Login/AbstractCharacterScene.ts | 10 +-- front/src/Phaser/Login/CustomizeScene.ts | 2 +- .../src/Phaser/Login/SelectCharacterScene.ts | 4 +- 6 files changed, 64 insertions(+), 41 deletions(-) diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index bd12d866..c681fd37 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -1,8 +1,6 @@ import type { SignalData } from "simple-peer"; import type { RoomConnection } from "./RoomConnection"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; -import { PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; -import { CharacterLayer } from "../../../back/src/Model/Websocket/CharacterLayer"; export interface PointInterface { x: number; diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index 035ddf8d..02d9dd3a 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -23,7 +23,7 @@ export const mapLayerToLevel = { accessory: 5, }; -enum PlayerTexturesKey { +export enum PlayerTexturesKey { Accessory = "accessory", Body = "body", Clothes = "clothes", @@ -52,35 +52,58 @@ interface PlayerTexturesRecord { } export class PlayerTextures { - public static PLAYER_RESOURCES: BodyResourceDescriptionListInterface; - public static COLOR_RESOURCES: BodyResourceDescriptionListInterface; - public static EYES_RESOURCES: BodyResourceDescriptionListInterface; - public static HAIR_RESOURCES: BodyResourceDescriptionListInterface; - public static CLOTHES_RESOURCES: BodyResourceDescriptionListInterface; - public static HATS_RESOURCES: BodyResourceDescriptionListInterface; - public static ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface; - public static LAYERS: BodyResourceDescriptionListInterface[]; + private PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {}; + private COLOR_RESOURCES: BodyResourceDescriptionListInterface = {}; + private EYES_RESOURCES: BodyResourceDescriptionListInterface = {}; + private HAIR_RESOURCES: BodyResourceDescriptionListInterface = {}; + private CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {}; + private HATS_RESOURCES: BodyResourceDescriptionListInterface = {}; + private ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {}; + private LAYERS: BodyResourceDescriptionListInterface[] = []; public loadPlayerTexturesMetadata(metadata: PlayerTexturesMetadata): void { this.mapTexturesMetadataIntoResources(metadata); } - private mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { - PlayerTextures.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); - PlayerTextures.COLOR_RESOURCES = this.getMappedResources(metadata.body); - PlayerTextures.EYES_RESOURCES = this.getMappedResources(metadata.eyes); - PlayerTextures.HAIR_RESOURCES = this.getMappedResources(metadata.hair); - PlayerTextures.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); - PlayerTextures.HATS_RESOURCES = this.getMappedResources(metadata.hat); - PlayerTextures.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); + public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface { + switch (key) { + case PlayerTexturesKey.Accessory: + return this.ACCESSORIES_RESOURCES; + case PlayerTexturesKey.Body: + return this.COLOR_RESOURCES; + case PlayerTexturesKey.Clothes: + return this.CLOTHES_RESOURCES; + case PlayerTexturesKey.Eyes: + return this.EYES_RESOURCES; + case PlayerTexturesKey.Hair: + return this.HAIR_RESOURCES; + case PlayerTexturesKey.Hat: + return this.HATS_RESOURCES; + case PlayerTexturesKey.Woka: + return this.PLAYER_RESOURCES; + } + } - PlayerTextures.LAYERS = [ - PlayerTextures.COLOR_RESOURCES, - PlayerTextures.EYES_RESOURCES, - PlayerTextures.HAIR_RESOURCES, - PlayerTextures.CLOTHES_RESOURCES, - PlayerTextures.HATS_RESOURCES, - PlayerTextures.ACCESSORIES_RESOURCES, + public getLayers(): BodyResourceDescriptionListInterface[] { + return this.LAYERS; + } + + private mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { + this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); + this.COLOR_RESOURCES = this.getMappedResources(metadata.body); + this.EYES_RESOURCES = this.getMappedResources(metadata.eyes); + this.HAIR_RESOURCES = this.getMappedResources(metadata.hair); + this.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); + this.HATS_RESOURCES = this.getMappedResources(metadata.hat); + this.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); + + this.LAYERS = [ + this.COLOR_RESOURCES, + this.EYES_RESOURCES, + this.HAIR_RESOURCES, + this.CLOTHES_RESOURCES, + this.HATS_RESOURCES, + this.ACCESSORIES_RESOURCES, ]; } diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 165a6063..f3c9d273 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -1,6 +1,6 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import type { CharacterTexture } from "../../Connexion/LocalUser"; -import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures } from "./PlayerTextures"; +import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures"; import CancelablePromise from "cancelable-promise"; export interface FrameConfig { @@ -8,9 +8,12 @@ export interface FrameConfig { frameHeight: number; } -export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => { +export const loadAllLayers = ( + load: LoaderPlugin, + playerTextures: PlayerTextures +): BodyResourceDescriptionInterface[][] => { const returnArray: BodyResourceDescriptionInterface[][] = []; - PlayerTextures.LAYERS.forEach((layer) => { + playerTextures.getLayers().forEach((layer) => { const layerArray: BodyResourceDescriptionInterface[] = []; Object.values(layer).forEach((textureDescriptor) => { layerArray.push(textureDescriptor); @@ -20,8 +23,11 @@ export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterf }); return returnArray; }; -export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => { - const returnArray = Object.values(PlayerTextures.PLAYER_RESOURCES); +export const loadAllDefaultModels = ( + load: LoaderPlugin, + playerTextures: PlayerTextures +): BodyResourceDescriptionInterface[] => { + const returnArray = Object.values(playerTextures.getTexturesResources(PlayerTexturesKey.Woka)); returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => { load.spritesheet(playerResource.id, playerResource.img, { frameWidth: 32, frameHeight: 32 }); }); diff --git a/front/src/Phaser/Login/AbstractCharacterScene.ts b/front/src/Phaser/Login/AbstractCharacterScene.ts index bc260718..b31db769 100644 --- a/front/src/Phaser/Login/AbstractCharacterScene.ts +++ b/front/src/Phaser/Login/AbstractCharacterScene.ts @@ -1,12 +1,8 @@ import { ResizableScene } from "./ResizableScene"; -import { localUserStore } from "../../Connexion/LocalUserStore"; -import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; +import { BodyResourceDescriptionInterface, PlayerTexturesKey } from "../Entity/PlayerTextures"; import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager"; -import type { CharacterTexture } from "../../Connexion/LocalUser"; import type CancelablePromise from "cancelable-promise"; import { PlayerTextures } from "../Entity/PlayerTextures"; -import { Loader } from "../Components/Loader"; -import { CustomizeSceneName } from "./CustomizeScene"; export abstract class AbstractCharacterScene extends ResizableScene { protected playerTextures: PlayerTextures; @@ -17,7 +13,7 @@ export abstract class AbstractCharacterScene extends ResizableScene { } loadCustomSceneSelectCharacters(): Promise { - const textures = PlayerTextures.PLAYER_RESOURCES; + const textures = this.playerTextures.getTexturesResources(PlayerTexturesKey.Woka); const promises: CancelablePromise[] = []; if (textures) { for (const texture of Object.values(textures)) { @@ -32,7 +28,7 @@ export abstract class AbstractCharacterScene extends ResizableScene { loadSelectSceneCharacters(): Promise { const promises: CancelablePromise[] = []; - for (const textures of PlayerTextures.LAYERS) { + for (const textures of this.playerTextures.getLayers()) { for (const texture of Object.values(textures)) { if (texture.level !== -1) { continue; diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 9227d3e3..918cf9cf 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -74,7 +74,7 @@ export class CustomizeScene extends AbstractCharacterScene { }) .catch((e) => console.error(e)); - this.layers = loadAllLayers(this.load); + this.layers = loadAllLayers(this.load, this.playerTextures); this.lazyloadingAttempt = false; //this function must stay at the end of preload function diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 5fe0ecc9..f7fd3c8a 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -70,7 +70,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.lazyloadingAttempt = true; }) .catch((e) => console.error(e)); - this.playerModels = loadAllDefaultModels(this.load); + this.playerModels = loadAllDefaultModels(this.load, this.playerTextures); this.lazyloadingAttempt = false; //this function must stay at the end of preload function @@ -299,7 +299,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { } private isCustomizationAvailable(): boolean { - for (const layer of PlayerTextures.LAYERS) { + for (const layer of this.playerTextures.getLayers()) { if (Object.keys(layer).length > 0) { return true; } From f97681cef598a980c9ae82429874062bb918f1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 11 Mar 2022 10:44:40 +0100 Subject: [PATCH 26/61] Actually returning something in logout-callback --- pusher/src/Controller/AuthenticateController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 964b4027..dff49349 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -204,7 +204,7 @@ export class AuthenticateController extends BaseHttpController { console.error("openIDCallback => logout-callback", error); } - return res; + return res.status(200).send(''); }); } From 46f16f1422d7c134ceebed5ca23daf8445b4f02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 11 Mar 2022 12:02:45 +0100 Subject: [PATCH 27/61] Copying messages to back too --- .github/workflows/continuous_integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index bf433a0a..8554d079 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -155,7 +155,7 @@ jobs: working-directory: "messages" - name: "Build proto messages" - run: yarn run proto && yarn run copy-to-back + run: yarn run proto && yarn run copy-to-back && yarn run json-copy-to-back working-directory: "messages" - name: "Build" From 138e8aece4b6fee5e423f32028f078c69d48a49d Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Fri, 11 Mar 2022 13:36:55 +0100 Subject: [PATCH 28/61] better woka preview, wip --- .../CustomizeWoka/CustomWokaPreviewer.ts | 28 +++ front/src/Phaser/Entity/Character.ts | 82 +------- .../src/Phaser/Entity/CustomizedCharacter.ts | 38 +++- front/src/Phaser/Login/CustomizeScene.ts | 184 +++++++++--------- front/src/Phaser/Login/EntryScene.ts | 8 +- .../src/Phaser/Login/SelectCharacterScene.ts | 2 +- front/src/Phaser/Player/Animation.ts | 69 +++++++ 7 files changed, 240 insertions(+), 171 deletions(-) create mode 100644 front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts new file mode 100644 index 00000000..cb01f0dc --- /dev/null +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -0,0 +1,28 @@ +import { CustomizedCharacter } from "../../Entity/CustomizedCharacter"; +import { PlayerAnimationDirections } from "../../Player/Animation"; + +export class CustomWokaPreviewer extends Phaser.GameObjects.Container { + private background: Phaser.GameObjects.Rectangle; + private character: CustomizedCharacter; + + constructor(scene: Phaser.Scene, x: number, y: number) { + super(scene, x, y); + + this.background = this.createBackground(); + this.character = new CustomizedCharacter(scene, 0, 0, ["body19", "clothes4"]); + this.character.setScale(4); + this.setSize(this.background.displayWidth, this.background.displayHeight); + + this.add([this.background, this.character]); + + this.scene.add.existing(this); + } + + public update(): void { + this.character.playAnimation(PlayerAnimationDirections.Down, true); + } + + private createBackground(): Phaser.GameObjects.Rectangle { + return this.scene.add.rectangle(0, 0, 150, 300, 0xbfbfbf, 0.5); + } +} diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 37d92b89..1a276b1d 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -1,4 +1,9 @@ -import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; +import { + AnimationData, + getPlayerAnimations, + PlayerAnimationDirections, + PlayerAnimationTypes, +} from "../Player/Animation"; import { SpeechBubble } from "./SpeechBubble"; import Text = Phaser.GameObjects.Text; import Container = Phaser.GameObjects.Container; @@ -20,15 +25,6 @@ import type CancelablePromise from "cancelable-promise"; import { TalkIcon } from "../Components/TalkIcon"; const playerNameY = -25; - -interface AnimationData { - key: string; - frameRate: number; - repeat: number; - frameModel: string; //todo use an enum - frames: number[]; -} - const interactiveRadius = 35; export abstract class Character extends Container implements OutlineableInterface { @@ -232,7 +228,7 @@ export abstract class Character extends Container implements OutlineableInterfac } } - public addTextures(textures: string[], frame?: string | number): void { + private addTextures(textures: string[], frame?: string | number): void { if (textures.length < 1) { throw new TextureError("no texture given"); } @@ -243,7 +239,8 @@ export abstract class Character extends Container implements OutlineableInterfac } const sprite = new Sprite(this.scene, 0, 0, texture, frame); this.add(sprite); - this.getPlayerAnimations(texture).forEach((d) => { + console.log(texture); + getPlayerAnimations(texture).forEach((d) => { this.scene.anims.create({ key: d.key, frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }), @@ -263,67 +260,6 @@ export abstract class Character extends Container implements OutlineableInterfac return this.scene.plugins.get("rexOutlinePipeline") as unknown as OutlinePipelinePlugin | undefined; } - private getPlayerAnimations(name: string): AnimationData[] { - return [ - { - key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`, - frameModel: name, - frames: [0, 1, 2, 1], - frameRate: 10, - repeat: -1, - }, - { - key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`, - frameModel: name, - frames: [3, 4, 5, 4], - frameRate: 10, - repeat: -1, - }, - { - key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`, - frameModel: name, - frames: [6, 7, 8, 7], - frameRate: 10, - repeat: -1, - }, - { - key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`, - frameModel: name, - frames: [9, 10, 11, 10], - frameRate: 10, - repeat: -1, - }, - { - key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`, - frameModel: name, - frames: [1], - frameRate: 10, - repeat: 1, - }, - { - key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`, - frameModel: name, - frames: [4], - frameRate: 10, - repeat: 1, - }, - { - key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`, - frameModel: name, - frames: [7], - frameRate: 10, - repeat: 1, - }, - { - key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`, - frameModel: name, - frames: [10], - frameRate: 10, - repeat: 1, - }, - ]; - } - protected playAnimation(direction: PlayerAnimationDirections, moving: boolean): void { if (this.invisible) return; for (const [texture, sprite] of this.sprites.entries()) { diff --git a/front/src/Phaser/Entity/CustomizedCharacter.ts b/front/src/Phaser/Entity/CustomizedCharacter.ts index 79ac8ebc..5d91964f 100644 --- a/front/src/Phaser/Entity/CustomizedCharacter.ts +++ b/front/src/Phaser/Entity/CustomizedCharacter.ts @@ -1,20 +1,54 @@ import Container = Phaser.GameObjects.Container; import type { Scene } from "phaser"; import Sprite = Phaser.GameObjects.Sprite; +import { getPlayerAnimations, PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; /** * A sprite of a customized character (used in the Customize Scene only) */ export class CustomizedCharacter extends Container { + private sprites: Phaser.GameObjects.Sprite[]; + public constructor(scene: Scene, x: number, y: number, layers: string[]) { super(scene, x, y); + this.sprites = []; this.updateSprites(layers); } public updateSprites(layers: string[]): void { + this.sprites = []; this.removeAll(true); - for (const layer of layers) { - this.add(new Sprite(this.scene, 0, 0, layer)); + for (const texture of layers) { + const newSprite = new Sprite(this.scene, 0, 0, texture); + this.sprites.push(newSprite); + getPlayerAnimations(texture).forEach((d) => { + this.scene.anims.create({ + key: d.key, + frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }), + frameRate: d.frameRate, + repeat: d.repeat, + }); + }); + // Needed, otherwise, animations are not handled correctly. + if (this.scene) { + this.scene.sys.updateList.add(newSprite); + } + } + this.add(this.sprites); + } + + public playAnimation(direction: PlayerAnimationDirections, moving: boolean): void { + for (const sprite of this.sprites) { + if (!sprite.anims) { + console.error("ANIMS IS NOT DEFINED!!!"); + return; + } + const textureKey = sprite.texture.key; + if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) { + sprite.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Walk, true); + } else if (!moving) { + sprite.anims.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Idle, true); + } } } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 918cf9cf..8f94ab69 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -9,19 +9,22 @@ import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures" import { AbstractCharacterScene } from "./AbstractCharacterScene"; import { areCharacterLayersValid } from "../../Connexion/LocalUser"; import { SelectCharacterSceneName } from "./SelectCharacterScene"; -import { activeRowStore, customCharacterSceneVisibleStore } from "../../Stores/CustomCharacterStore"; +import { activeRowStore } from "../../Stores/CustomCharacterStore"; import { waScaleManager } from "../Services/WaScaleManager"; import { CustomizedCharacter } from "../Entity/CustomizedCharacter"; import { get } from "svelte/store"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; +import { CustomWokaPreviewer } from "../Components/CustomizeWoka/CustomWokaPreviewer"; export const CustomizeSceneName = "CustomizeScene"; export class CustomizeScene extends AbstractCharacterScene { private Rectangle!: Rectangle; + private customWokaPreviewer: CustomWokaPreviewer; + private selectedLayers: number[] = [0]; private containersRow: CustomizedCharacter[][] = []; private layers: BodyResourceDescriptionInterface[][] = []; @@ -40,7 +43,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.loader = new Loader(this); } - preload() { + public preload(): void { const wokaMetadataKey = "woka-list"; this.cache.json.remove(wokaMetadataKey); // FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!) @@ -82,13 +85,8 @@ export class CustomizeScene extends AbstractCharacterScene { }); } - create() { - customCharacterSceneVisibleStore.set(true); - this.events.addListener("wake", () => { - waScaleManager.saveZoom(); - waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1; - customCharacterSceneVisibleStore.set(true); - }); + public create(): void { + console.log(this.layers); waScaleManager.saveZoom(); waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1; @@ -100,7 +98,6 @@ export class CustomizeScene extends AbstractCharacterScene { 33 ); this.Rectangle.setStrokeStyle(2, 0xffffff); - this.add.existing(this.Rectangle); this.createCustomizeLayer(0, 0, 0); this.createCustomizeLayer(0, 0, 1); @@ -110,6 +107,91 @@ export class CustomizeScene extends AbstractCharacterScene { this.createCustomizeLayer(0, 0, 5); this.moveLayers(); + + const customCursorPosition = localUserStore.getCustomCursorPosition(); + if (customCursorPosition) { + activeRowStore.set(customCursorPosition.activeRow); + this.selectedLayers = customCursorPosition.selectedLayers; + this.moveLayers(); + this.updateSelectedLayer(); + } + + this.customWokaPreviewer = new CustomWokaPreviewer(this, 300, 300); + + this.onResize(); + + this.bindEventHandlers(); + } + + public update(time: number, dt: number): void { + this.customWokaPreviewer.update(); + if (this.lazyloadingAttempt) { + this.moveLayers(); + this.doMoveCursorHorizontally(this.moveHorizontally); + this.lazyloadingAttempt = false; + } + + if (this.moveHorizontally !== 0) { + this.doMoveCursorHorizontally(this.moveHorizontally); + this.moveHorizontally = 0; + } + if (this.moveVertically !== 0) { + this.doMoveCursorVertically(this.moveVertically); + this.moveVertically = 0; + } + } + + public moveCursorHorizontally(index: number): void { + this.moveHorizontally = index; + } + + public moveCursorVertically(index: number): void { + this.moveVertically = index; + } + + public onResize(): void { + this.moveLayers(); + + this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; + this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3; + + this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; + this.customWokaPreviewer.y = this.cameras.main.worldView.y + this.cameras.main.height / 2; + } + + public nextSceneToCamera() { + const layers: string[] = []; + let i = 0; + for (const layerItem of this.selectedLayers) { + if (layerItem !== undefined) { + layers.push(this.layers[i][layerItem].id); + } + i++; + } + if (!areCharacterLayersValid(layers)) { + return; + } + + analyticsClient.validationWoka("CustomizeWoka"); + + gameManager.setCharacterLayers(layers); + this.scene.stop(CustomizeSceneName); + waScaleManager.restoreZoom(); + gameManager.tryResumingGame(EnableCameraSceneName); + } + + public backToPreviousScene() { + this.scene.stop(CustomizeSceneName); + waScaleManager.restoreZoom(); + this.scene.run(SelectCharacterSceneName); + } + + private bindEventHandlers(): void { + this.events.addListener("wake", () => { + waScaleManager.saveZoom(); + waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1; + }); + this.input.keyboard.on("keyup-ENTER", () => { this.nextSceneToCamera(); }); @@ -124,24 +206,6 @@ export class CustomizeScene extends AbstractCharacterScene { this.input.keyboard.on("keyup-LEFT", () => (this.moveHorizontally = -1)); this.input.keyboard.on("keyup-DOWN", () => (this.moveVertically = 1)); this.input.keyboard.on("keyup-UP", () => (this.moveVertically = -1)); - - const customCursorPosition = localUserStore.getCustomCursorPosition(); - if (customCursorPosition) { - activeRowStore.set(customCursorPosition.activeRow); - this.selectedLayers = customCursorPosition.selectedLayers; - this.moveLayers(); - this.updateSelectedLayer(); - } - - this.onResize(); - } - - public moveCursorHorizontally(index: number): void { - this.moveHorizontally = index; - } - - public moveCursorVertically(index: number): void { - this.moveVertically = index; } private doMoveCursorHorizontally(index: number): void { @@ -246,17 +310,6 @@ export class CustomizeScene extends AbstractCharacterScene { } } - /** - * @param x, the sprite's vertical position - * @param y, the sprites's horizontal position - * @param name, the sprite's name - * @return a new sprite - */ - private generateLayers(x: number, y: number, name: string): Sprite { - //return new Sprite(this, x, y, name); - return this.add.sprite(0, 0, name); - } - private updateSelectedLayer() { for (let i = 0; i < this.containersRow.length; i++) { for (let j = 0; j < this.containersRow[i].length; j++) { @@ -265,57 +318,4 @@ export class CustomizeScene extends AbstractCharacterScene { } } } - - update(time: number, delta: number): void { - if (this.lazyloadingAttempt) { - this.moveLayers(); - this.doMoveCursorHorizontally(this.moveHorizontally); - this.lazyloadingAttempt = false; - } - - if (this.moveHorizontally !== 0) { - this.doMoveCursorHorizontally(this.moveHorizontally); - this.moveHorizontally = 0; - } - if (this.moveVertically !== 0) { - this.doMoveCursorVertically(this.moveVertically); - this.moveVertically = 0; - } - } - - public onResize(): void { - this.moveLayers(); - - this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3; - } - - public nextSceneToCamera() { - const layers: string[] = []; - let i = 0; - for (const layerItem of this.selectedLayers) { - if (layerItem !== undefined) { - layers.push(this.layers[i][layerItem].id); - } - i++; - } - if (!areCharacterLayersValid(layers)) { - return; - } - - analyticsClient.validationWoka("CustomizeWoka"); - - gameManager.setCharacterLayers(layers); - this.scene.stop(CustomizeSceneName); - waScaleManager.restoreZoom(); - gameManager.tryResumingGame(EnableCameraSceneName); - customCharacterSceneVisibleStore.set(false); - } - - public backToPreviousScene() { - this.scene.stop(CustomizeSceneName); - waScaleManager.restoreZoom(); - this.scene.run(SelectCharacterSceneName); - customCharacterSceneVisibleStore.set(false); - } } diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index d86e3a2e..5a898ae0 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -7,8 +7,8 @@ import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene"; import LL from "../../i18n/i18n-svelte"; import { get } from "svelte/store"; import { localeDetector } from "../../i18n/locales"; -import { PlayerTextures } from "../Entity/PlayerTextures"; -import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; +import { CustomizeSceneName } from "./CustomizeScene"; +import { SelectCharacterSceneName } from "./SelectCharacterScene"; export const EntrySceneName = "EntryScene"; @@ -46,7 +46,9 @@ export class EntryScene extends Scene { // Let's rescale before starting the game // We can do it at this stage. waScaleManager.applyNewSize(); - this.scene.start(nextSceneName); + // this.scene.start(nextSceneName); + // this.scene.start(CustomizeSceneName); + this.scene.start(SelectCharacterSceneName); }) .catch((err) => { const $LL = get(LL); diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index f7fd3c8a..d2f1e8f4 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -149,9 +149,9 @@ export class SelectCharacterScene extends AbstractCharacterScene { } createCurrentPlayer(): void { + console.log("CREATE CURRENT PLAYER"); for (let i = 0; i < this.playerModels.length; i++) { const playerResource = this.playerModels[i]; - //check already exist texture if (this.players.find((c) => c.texture.key === playerResource.id)) { continue; diff --git a/front/src/Phaser/Player/Animation.ts b/front/src/Phaser/Player/Animation.ts index cf13e087..868f9243 100644 --- a/front/src/Phaser/Player/Animation.ts +++ b/front/src/Phaser/Player/Animation.ts @@ -8,3 +8,72 @@ export enum PlayerAnimationTypes { Walk = "walk", Idle = "idle", } + +export interface AnimationData { + key: string; + frameRate: number; + repeat: number; + frameModel: string; //todo use an enum + frames: number[]; +} + +export function getPlayerAnimations(name: string): AnimationData[] { + return [ + { + key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`, + frameModel: name, + frames: [0, 1, 2, 1], + frameRate: 10, + repeat: -1, + }, + { + key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`, + frameModel: name, + frames: [3, 4, 5, 4], + frameRate: 10, + repeat: -1, + }, + { + key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`, + frameModel: name, + frames: [6, 7, 8, 7], + frameRate: 10, + repeat: -1, + }, + { + key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`, + frameModel: name, + frames: [9, 10, 11, 10], + frameRate: 10, + repeat: -1, + }, + { + key: `${name}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [1], + frameRate: 10, + repeat: 1, + }, + { + key: `${name}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [4], + frameRate: 10, + repeat: 1, + }, + { + key: `${name}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [7], + frameRate: 10, + repeat: 1, + }, + { + key: `${name}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`, + frameModel: name, + frames: [10], + frameRate: 10, + repeat: 1, + }, + ]; +} From 2a73400f7cd1e6a13b03580e84b97aac1d967cc3 Mon Sep 17 00:00:00 2001 From: Piotr Hanusiak Date: Fri, 11 Mar 2022 14:30:00 +0100 Subject: [PATCH 29/61] remove customize button if no body parts are available (#1952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove customize button if no body parts are available * remove unused position field from PlayerTexturesCollection interface * removed unused label field * fix LocalUser test * little PlayerTextures class refactor * Fixing linting * Fixing missing Openapi packages in prod * Fixing back build Co-authored-by: Hanusiak Piotr Co-authored-by: David Négrier --- .github/workflows/build-and-deploy.yml | 2 +- docker-compose.e2e.yml | 2 + .../SelectCharacterScene.svelte | 13 +-- front/src/Connexion/ConnexionModels.ts | 2 - front/src/Connexion/LocalUser.ts | 9 ++- front/src/Phaser/Entity/PlayerTextures.ts | 80 ++++++++++++------- .../Entity/PlayerTexturesLoadingManager.ts | 16 ++-- .../Phaser/Login/AbstractCharacterScene.ts | 10 +-- front/src/Phaser/Login/CustomizeScene.ts | 2 +- .../src/Phaser/Login/SelectCharacterScene.ts | 13 ++- front/src/Stores/SelectCharacterSceneStore.ts | 3 + messages/JsonMessages/RegisterData.ts | 1 - pusher/package.json | 4 +- pusher/src/Controller/SwaggerController.ts | 7 +- 14 files changed, 107 insertions(+), 57 deletions(-) create mode 100644 front/src/Stores/SelectCharacterSceneStore.ts diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index e924cc11..9b17136d 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -81,7 +81,7 @@ jobs: working-directory: messages - name: Build proto messages - run: yarn run proto && yarn run copy-to-back + run: yarn run proto && yarn run copy-to-back && yarn run json-copy-to-back working-directory: messages # docker diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml index 52e312b5..25dafee4 100644 --- a/docker-compose.e2e.yml +++ b/docker-compose.e2e.yml @@ -28,6 +28,8 @@ services: dockerfile: pusher/Dockerfile command: yarn run runprod volumes: [] + environment: + ENABLE_OPENAPI_ENDPOINT: "false" back: image: 'wa-back-e2e' diff --git a/front/src/Components/selectCharacter/SelectCharacterScene.svelte b/front/src/Components/selectCharacter/SelectCharacterScene.svelte index 9f6e5f9c..807cf88f 100644 --- a/front/src/Components/selectCharacter/SelectCharacterScene.svelte +++ b/front/src/Components/selectCharacter/SelectCharacterScene.svelte @@ -2,6 +2,7 @@ import type { Game } from "../../Phaser/Game/Game"; import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene"; import LL from "../../i18n/i18n-svelte"; + import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore"; export let game: Game; @@ -40,11 +41,13 @@ class="selectCharacterSceneFormSubmit nes-btn is-primary" on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()} - + {#if $customizeAvailableStore} + + {/if} diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index bd12d866..c681fd37 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -1,8 +1,6 @@ import type { SignalData } from "simple-peer"; import type { RoomConnection } from "./RoomConnection"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; -import { PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; -import { CharacterLayer } from "../../../back/src/Model/Websocket/CharacterLayer"; export interface PointInterface { x: number; diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index cc86ac32..4db0d6d7 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -15,7 +15,14 @@ export function isUserNameValid(value: unknown): boolean { } export function areCharacterLayersValid(value: string[] | null): boolean { - if (!value || !value.length) return false; + if (!value || !value.length) { + return false; + } + for (const layerName of value) { + if (layerName.length === 0 || layerName === " ") { + return false; + } + } return true; } diff --git a/front/src/Phaser/Entity/PlayerTextures.ts b/front/src/Phaser/Entity/PlayerTextures.ts index 84157a92..02d9dd3a 100644 --- a/front/src/Phaser/Entity/PlayerTextures.ts +++ b/front/src/Phaser/Entity/PlayerTextures.ts @@ -6,7 +6,6 @@ export interface BodyResourceDescriptionListInterface { export interface BodyResourceDescriptionInterface { id: string; - label: string; img: string; level?: number; } @@ -24,7 +23,7 @@ export const mapLayerToLevel = { accessory: 5, }; -enum PlayerTexturesKey { +export enum PlayerTexturesKey { Accessory = "accessory", Body = "body", Clothes = "clothes", @@ -43,7 +42,6 @@ interface PlayerTexturesCategory { interface PlayerTexturesCollection { name: string; - position: number; textures: PlayerTexturesRecord[]; } @@ -54,43 +52,69 @@ interface PlayerTexturesRecord { } export class PlayerTextures { - public static PLAYER_RESOURCES: BodyResourceDescriptionListInterface; - public static COLOR_RESOURCES: BodyResourceDescriptionListInterface; - public static EYES_RESOURCES: BodyResourceDescriptionListInterface; - public static HAIR_RESOURCES: BodyResourceDescriptionListInterface; - public static CLOTHES_RESOURCES: BodyResourceDescriptionListInterface; - public static HATS_RESOURCES: BodyResourceDescriptionListInterface; - public static ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface; - public static LAYERS: BodyResourceDescriptionListInterface[]; + private PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {}; + private COLOR_RESOURCES: BodyResourceDescriptionListInterface = {}; + private EYES_RESOURCES: BodyResourceDescriptionListInterface = {}; + private HAIR_RESOURCES: BodyResourceDescriptionListInterface = {}; + private CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {}; + private HATS_RESOURCES: BodyResourceDescriptionListInterface = {}; + private ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {}; + private LAYERS: BodyResourceDescriptionListInterface[] = []; public loadPlayerTexturesMetadata(metadata: PlayerTexturesMetadata): void { this.mapTexturesMetadataIntoResources(metadata); } - private mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { - PlayerTextures.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); - PlayerTextures.COLOR_RESOURCES = this.getMappedResources(metadata.body); - PlayerTextures.EYES_RESOURCES = this.getMappedResources(metadata.eyes); - PlayerTextures.HAIR_RESOURCES = this.getMappedResources(metadata.hair); - PlayerTextures.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); - PlayerTextures.HATS_RESOURCES = this.getMappedResources(metadata.hat); - PlayerTextures.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); + public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface { + switch (key) { + case PlayerTexturesKey.Accessory: + return this.ACCESSORIES_RESOURCES; + case PlayerTexturesKey.Body: + return this.COLOR_RESOURCES; + case PlayerTexturesKey.Clothes: + return this.CLOTHES_RESOURCES; + case PlayerTexturesKey.Eyes: + return this.EYES_RESOURCES; + case PlayerTexturesKey.Hair: + return this.HAIR_RESOURCES; + case PlayerTexturesKey.Hat: + return this.HATS_RESOURCES; + case PlayerTexturesKey.Woka: + return this.PLAYER_RESOURCES; + } + } - PlayerTextures.LAYERS = [ - PlayerTextures.COLOR_RESOURCES, - PlayerTextures.EYES_RESOURCES, - PlayerTextures.HAIR_RESOURCES, - PlayerTextures.CLOTHES_RESOURCES, - PlayerTextures.HATS_RESOURCES, - PlayerTextures.ACCESSORIES_RESOURCES, + public getLayers(): BodyResourceDescriptionListInterface[] { + return this.LAYERS; + } + + private mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { + this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); + this.COLOR_RESOURCES = this.getMappedResources(metadata.body); + this.EYES_RESOURCES = this.getMappedResources(metadata.eyes); + this.HAIR_RESOURCES = this.getMappedResources(metadata.hair); + this.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); + this.HATS_RESOURCES = this.getMappedResources(metadata.hat); + this.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); + + this.LAYERS = [ + this.COLOR_RESOURCES, + this.EYES_RESOURCES, + this.HAIR_RESOURCES, + this.CLOTHES_RESOURCES, + this.HATS_RESOURCES, + this.ACCESSORIES_RESOURCES, ]; } private getMappedResources(category: PlayerTexturesCategory): BodyResourceDescriptionListInterface { const resources: BodyResourceDescriptionListInterface = {}; + if (!category) { + return {}; + } for (const collection of category.collections) { for (const texture of collection.textures) { - resources[texture.id] = { id: texture.id, label: texture.name, img: texture.url }; + resources[texture.id] = { id: texture.id, img: texture.url }; } } return resources; @@ -98,5 +122,5 @@ export class PlayerTextures { } export const OBJECTS: BodyResourceDescriptionInterface[] = [ - { id: "teleportation", label: "Teleport", img: "resources/objects/teleportation.png" }, + { id: "teleportation", img: "resources/objects/teleportation.png" }, ]; diff --git a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts index 165a6063..f3c9d273 100644 --- a/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts +++ b/front/src/Phaser/Entity/PlayerTexturesLoadingManager.ts @@ -1,6 +1,6 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import type { CharacterTexture } from "../../Connexion/LocalUser"; -import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures } from "./PlayerTextures"; +import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures"; import CancelablePromise from "cancelable-promise"; export interface FrameConfig { @@ -8,9 +8,12 @@ export interface FrameConfig { frameHeight: number; } -export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => { +export const loadAllLayers = ( + load: LoaderPlugin, + playerTextures: PlayerTextures +): BodyResourceDescriptionInterface[][] => { const returnArray: BodyResourceDescriptionInterface[][] = []; - PlayerTextures.LAYERS.forEach((layer) => { + playerTextures.getLayers().forEach((layer) => { const layerArray: BodyResourceDescriptionInterface[] = []; Object.values(layer).forEach((textureDescriptor) => { layerArray.push(textureDescriptor); @@ -20,8 +23,11 @@ export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterf }); return returnArray; }; -export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => { - const returnArray = Object.values(PlayerTextures.PLAYER_RESOURCES); +export const loadAllDefaultModels = ( + load: LoaderPlugin, + playerTextures: PlayerTextures +): BodyResourceDescriptionInterface[] => { + const returnArray = Object.values(playerTextures.getTexturesResources(PlayerTexturesKey.Woka)); returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => { load.spritesheet(playerResource.id, playerResource.img, { frameWidth: 32, frameHeight: 32 }); }); diff --git a/front/src/Phaser/Login/AbstractCharacterScene.ts b/front/src/Phaser/Login/AbstractCharacterScene.ts index bc260718..b31db769 100644 --- a/front/src/Phaser/Login/AbstractCharacterScene.ts +++ b/front/src/Phaser/Login/AbstractCharacterScene.ts @@ -1,12 +1,8 @@ import { ResizableScene } from "./ResizableScene"; -import { localUserStore } from "../../Connexion/LocalUserStore"; -import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures"; +import { BodyResourceDescriptionInterface, PlayerTexturesKey } from "../Entity/PlayerTextures"; import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager"; -import type { CharacterTexture } from "../../Connexion/LocalUser"; import type CancelablePromise from "cancelable-promise"; import { PlayerTextures } from "../Entity/PlayerTextures"; -import { Loader } from "../Components/Loader"; -import { CustomizeSceneName } from "./CustomizeScene"; export abstract class AbstractCharacterScene extends ResizableScene { protected playerTextures: PlayerTextures; @@ -17,7 +13,7 @@ export abstract class AbstractCharacterScene extends ResizableScene { } loadCustomSceneSelectCharacters(): Promise { - const textures = PlayerTextures.PLAYER_RESOURCES; + const textures = this.playerTextures.getTexturesResources(PlayerTexturesKey.Woka); const promises: CancelablePromise[] = []; if (textures) { for (const texture of Object.values(textures)) { @@ -32,7 +28,7 @@ export abstract class AbstractCharacterScene extends ResizableScene { loadSelectSceneCharacters(): Promise { const promises: CancelablePromise[] = []; - for (const textures of PlayerTextures.LAYERS) { + for (const textures of this.playerTextures.getLayers()) { for (const texture of Object.values(textures)) { if (texture.level !== -1) { continue; diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 9227d3e3..918cf9cf 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -74,7 +74,7 @@ export class CustomizeScene extends AbstractCharacterScene { }) .catch((e) => console.error(e)); - this.layers = loadAllLayers(this.load); + this.layers = loadAllLayers(this.load, this.playerTextures); this.lazyloadingAttempt = false; //this function must stay at the end of preload function diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index f2af9cb9..f7fd3c8a 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -15,6 +15,7 @@ import { waScaleManager } from "../Services/WaScaleManager"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; +import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore"; //todo: put this constants in a dedicated file export const SelectCharacterSceneName = "SelectCharacterScene"; @@ -69,7 +70,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.lazyloadingAttempt = true; }) .catch((e) => console.error(e)); - this.playerModels = loadAllDefaultModels(this.load); + this.playerModels = loadAllDefaultModels(this.load, this.playerTextures); this.lazyloadingAttempt = false; //this function must stay at the end of preload function @@ -78,6 +79,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { } create() { + customizeAvailableStore.set(this.isCustomizationAvailable()); selectCharacterSceneVisibleStore.set(true); this.events.addListener("wake", () => { waScaleManager.saveZoom(); @@ -295,4 +297,13 @@ export class SelectCharacterScene extends AbstractCharacterScene { //move position of user this.moveUser(); } + + private isCustomizationAvailable(): boolean { + for (const layer of this.playerTextures.getLayers()) { + if (Object.keys(layer).length > 0) { + return true; + } + } + return false; + } } diff --git a/front/src/Stores/SelectCharacterSceneStore.ts b/front/src/Stores/SelectCharacterSceneStore.ts new file mode 100644 index 00000000..654da03c --- /dev/null +++ b/front/src/Stores/SelectCharacterSceneStore.ts @@ -0,0 +1,3 @@ +import { writable } from "svelte/store"; + +export const customizeAvailableStore = writable(false); diff --git a/messages/JsonMessages/RegisterData.ts b/messages/JsonMessages/RegisterData.ts index 1fe3426f..de1b2ca7 100644 --- a/messages/JsonMessages/RegisterData.ts +++ b/messages/JsonMessages/RegisterData.ts @@ -13,7 +13,6 @@ export const isRegisterData = new tg.IsInterface() organizationMemberToken: tg.isNullable(tg.isString), mapUrlStart: tg.isString, userUuid: tg.isString, -// textures: tg.isArray(isCharacterTexture), authToken: tg.isString, }) .withOptionalProperties({ diff --git a/pusher/package.json b/pusher/package.json index 6db02396..4de55915 100644 --- a/pusher/package.json +++ b/pusher/package.json @@ -4,9 +4,9 @@ "description": "", "main": "index.js", "scripts": { - "tsc": "tsc", + "tsc": "tsc && cp -rf ./data ./dist/", "dev": "ts-node-dev --respawn ./server.ts", - "prod": "tsc && node --max-old-space-size=4096 ./dist/server.js", + "prod": "tsc && cp -rf ./data ./dist/ && 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", "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", diff --git a/pusher/src/Controller/SwaggerController.ts b/pusher/src/Controller/SwaggerController.ts index d9ea358e..618b5266 100644 --- a/pusher/src/Controller/SwaggerController.ts +++ b/pusher/src/Controller/SwaggerController.ts @@ -1,12 +1,11 @@ -import swaggerJsdoc from "swagger-jsdoc"; import { BaseHttpController } from "./BaseHttpController"; -// @ts-ignore -import LiveDirectory from "live-directory"; import * as fs from "fs"; export class SwaggerController extends BaseHttpController { routes() { this.app.get("/openapi", (req, res) => { + // Let's load the module dynamically (it may not exist in prod because part of the -dev packages) + const swaggerJsdoc = require("swagger-jsdoc"); const options = { swaggerDefinition: { openapi: "3.0.0", @@ -22,6 +21,8 @@ export class SwaggerController extends BaseHttpController { }); // Create a LiveDirectory instance to virtualize directory with our assets + // @ts-ignore + const LiveDirectory = require("live-directory"); const LiveAssets = new LiveDirectory({ path: __dirname + "/../../node_modules/swagger-ui-dist", // We want to provide the system path to the folder. Avoid using relative paths. keep: { From 6d50d15630cca1c27f5e3a94747f2f2860995974 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Fri, 11 Mar 2022 15:21:51 +0100 Subject: [PATCH 30/61] different approach for displaying preview --- .../CustomizeWoka/CustomWokaPreviewer.ts | 77 +++++++++++++++++-- .../src/Phaser/Entity/CustomizedCharacter.ts | 27 ------- front/src/Phaser/Login/CustomizeScene.ts | 1 + 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index cb01f0dc..63be98dc 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -1,28 +1,91 @@ -import { CustomizedCharacter } from "../../Entity/CustomizedCharacter"; -import { PlayerAnimationDirections } from "../../Player/Animation"; +import { getPlayerAnimations, PlayerAnimationDirections, PlayerAnimationTypes } from "../../Player/Animation"; + +export enum CustomWokaBodyPart { + Body = "Body", + Eyes = "Eyes", + Hair = "Hair", + Clothes = "Clothes", + Hat = "Hat", + Accessory = "Accessory", +} export class CustomWokaPreviewer extends Phaser.GameObjects.Container { private background: Phaser.GameObjects.Rectangle; - private character: CustomizedCharacter; + + private sprites: Record; constructor(scene: Phaser.Scene, x: number, y: number) { super(scene, x, y); + this.sprites = { + [CustomWokaBodyPart.Accessory]: this.scene.add.sprite(0, 0, "").setScale(4), + [CustomWokaBodyPart.Body]: this.scene.add.sprite(0, 0, "").setScale(4), + [CustomWokaBodyPart.Clothes]: this.scene.add.sprite(0, 0, "").setScale(4), + [CustomWokaBodyPart.Eyes]: this.scene.add.sprite(0, 0, "").setScale(4), + [CustomWokaBodyPart.Hair]: this.scene.add.sprite(0, 0, "").setScale(4), + [CustomWokaBodyPart.Hat]: this.scene.add.sprite(0, 0, "").setScale(4), + }; + + this.updateSprite("accessory1", CustomWokaBodyPart.Accessory); + this.updateSprite("body1", CustomWokaBodyPart.Body); + this.updateSprite("clothes4", CustomWokaBodyPart.Clothes); + this.updateSprite("eyes5", CustomWokaBodyPart.Eyes); + this.updateSprite("hair3", CustomWokaBodyPart.Hair); + this.updateSprite("hat2", CustomWokaBodyPart.Hat); + this.background = this.createBackground(); - this.character = new CustomizedCharacter(scene, 0, 0, ["body19", "clothes4"]); - this.character.setScale(4); this.setSize(this.background.displayWidth, this.background.displayHeight); - this.add([this.background, this.character]); + this.add([ + this.background, + this.sprites.Body, + this.sprites.Eyes, + this.sprites.Hair, + this.sprites.Clothes, + this.sprites.Hat, + this.sprites.Accessory, + ]); this.scene.add.existing(this); } public update(): void { - this.character.playAnimation(PlayerAnimationDirections.Down, true); + this.playAnimation(PlayerAnimationDirections.Down, true); } private createBackground(): Phaser.GameObjects.Rectangle { return this.scene.add.rectangle(0, 0, 150, 300, 0xbfbfbf, 0.5); } + + public playAnimation(direction: PlayerAnimationDirections, moving: boolean): void { + for (const bodyPartKey in this.sprites) { + const sprite = this.sprites[bodyPartKey as CustomWokaBodyPart]; + if (!sprite.anims) { + console.error("ANIMS IS NOT DEFINED!!!"); + return; + } + const textureKey = sprite.texture.key; + if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) { + sprite.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Walk, true); + } else if (!moving) { + sprite.anims.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Idle, true); + } + } + } + + public updateSprite(textureKey: string, bodyPart: CustomWokaBodyPart): void { + this.sprites[bodyPart].setTexture(textureKey); + getPlayerAnimations(textureKey).forEach((d) => { + this.scene.anims.create({ + key: d.key, + frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }), + frameRate: d.frameRate, + repeat: d.repeat, + }); + }); + // Needed, otherwise, animations are not handled correctly. + if (this.scene) { + this.scene.sys.updateList.add(this.sprites[bodyPart]); + } + } } diff --git a/front/src/Phaser/Entity/CustomizedCharacter.ts b/front/src/Phaser/Entity/CustomizedCharacter.ts index 5d91964f..8c25450f 100644 --- a/front/src/Phaser/Entity/CustomizedCharacter.ts +++ b/front/src/Phaser/Entity/CustomizedCharacter.ts @@ -21,34 +21,7 @@ export class CustomizedCharacter extends Container { for (const texture of layers) { const newSprite = new Sprite(this.scene, 0, 0, texture); this.sprites.push(newSprite); - getPlayerAnimations(texture).forEach((d) => { - this.scene.anims.create({ - key: d.key, - frames: this.scene.anims.generateFrameNumbers(d.frameModel, { frames: d.frames }), - frameRate: d.frameRate, - repeat: d.repeat, - }); - }); - // Needed, otherwise, animations are not handled correctly. - if (this.scene) { - this.scene.sys.updateList.add(newSprite); - } } this.add(this.sprites); } - - public playAnimation(direction: PlayerAnimationDirections, moving: boolean): void { - for (const sprite of this.sprites) { - if (!sprite.anims) { - console.error("ANIMS IS NOT DEFINED!!!"); - return; - } - const textureKey = sprite.texture.key; - if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) { - sprite.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Walk, true); - } else if (!moving) { - sprite.anims.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Idle, true); - } - } - } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 8f94ab69..f6ba2c42 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -125,6 +125,7 @@ export class CustomizeScene extends AbstractCharacterScene { public update(time: number, dt: number): void { this.customWokaPreviewer.update(); + if (this.lazyloadingAttempt) { this.moveLayers(); this.doMoveCursorHorizontally(this.moveHorizontally); From 9644512d684d463a2f26f5951b658d595c246f70 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Fri, 11 Mar 2022 16:05:39 +0100 Subject: [PATCH 31/61] different animations for previewer --- .../CustomizeWoka/CustomWokaPreviewer.ts | 61 +++++++++++++------ .../src/Phaser/Components/Ui/StatesButton.ts | 0 front/src/Phaser/Login/CustomizeScene.ts | 33 +++++++++- front/src/Phaser/Login/EntryScene.ts | 4 +- 4 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 front/src/Phaser/Components/Ui/StatesButton.ts diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index 63be98dc..f822c97e 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -10,20 +10,24 @@ export enum CustomWokaBodyPart { } export class CustomWokaPreviewer extends Phaser.GameObjects.Container { - private background: Phaser.GameObjects.Rectangle; - + private background: Phaser.GameObjects.Graphics; private sprites: Record; + private currentAnimationDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; + private currentlyMoving: boolean = true; + constructor(scene: Phaser.Scene, x: number, y: number) { super(scene, x, y); + const spritesOffset = -2; + this.sprites = { - [CustomWokaBodyPart.Accessory]: this.scene.add.sprite(0, 0, "").setScale(4), - [CustomWokaBodyPart.Body]: this.scene.add.sprite(0, 0, "").setScale(4), - [CustomWokaBodyPart.Clothes]: this.scene.add.sprite(0, 0, "").setScale(4), - [CustomWokaBodyPart.Eyes]: this.scene.add.sprite(0, 0, "").setScale(4), - [CustomWokaBodyPart.Hair]: this.scene.add.sprite(0, 0, "").setScale(4), - [CustomWokaBodyPart.Hat]: this.scene.add.sprite(0, 0, "").setScale(4), + [CustomWokaBodyPart.Accessory]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), + [CustomWokaBodyPart.Body]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), + [CustomWokaBodyPart.Clothes]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), + [CustomWokaBodyPart.Eyes]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), + [CustomWokaBodyPart.Hair]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), + [CustomWokaBodyPart.Hat]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), }; this.updateSprite("accessory1", CustomWokaBodyPart.Accessory); @@ -33,8 +37,11 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.updateSprite("hair3", CustomWokaBodyPart.Hair); this.updateSprite("hat2", CustomWokaBodyPart.Hat); - this.background = this.createBackground(); - this.setSize(this.background.displayWidth, this.background.displayHeight); + const width = 150; + const height = 200; + + this.background = this.createBackground(width, height); + this.setSize(width, height); this.add([ this.background, @@ -50,14 +57,26 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { } public update(): void { - this.playAnimation(PlayerAnimationDirections.Down, true); + this.animate(); } - private createBackground(): Phaser.GameObjects.Rectangle { - return this.scene.add.rectangle(0, 0, 150, 300, 0xbfbfbf, 0.5); + public changeAnimation(direction: PlayerAnimationDirections, moving: boolean): void { + this.currentAnimationDirection = direction; + this.currentlyMoving = moving; } - public playAnimation(direction: PlayerAnimationDirections, moving: boolean): void { + private createBackground(width: number, height: number): Phaser.GameObjects.Graphics { + const background = this.scene.add.graphics(); + background.fillStyle(0xffffff); + background.lineStyle(5, 0xadafbc); + + background.fillRect(-width / 2, -height / 2, width, height); + background.strokeRect(-width / 2, -height / 2, width, height); + + return background; + } + + private animate(): void { for (const bodyPartKey in this.sprites) { const sprite = this.sprites[bodyPartKey as CustomWokaBodyPart]; if (!sprite.anims) { @@ -65,10 +84,16 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { return; } const textureKey = sprite.texture.key; - if (moving && (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== direction)) { - sprite.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Walk, true); - } else if (!moving) { - sprite.anims.play(textureKey + "-" + direction + "-" + PlayerAnimationTypes.Idle, true); + if ( + this.currentlyMoving && + (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== this.currentAnimationDirection) + ) { + sprite.play(textureKey + "-" + this.currentAnimationDirection + "-" + PlayerAnimationTypes.Walk, true); + } else if (!this.currentlyMoving) { + sprite.anims.play( + textureKey + "-" + this.currentAnimationDirection + "-" + PlayerAnimationTypes.Idle, + true + ); } } } diff --git a/front/src/Phaser/Components/Ui/StatesButton.ts b/front/src/Phaser/Components/Ui/StatesButton.ts new file mode 100644 index 00000000..e69de29b diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index f6ba2c42..8f1e598d 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -16,7 +16,7 @@ import { get } from "svelte/store"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; -import { CustomWokaPreviewer } from "../Components/CustomizeWoka/CustomWokaPreviewer"; +import { CustomWokaBodyPart, CustomWokaPreviewer } from "../Components/CustomizeWoka/CustomWokaPreviewer"; export const CustomizeSceneName = "CustomizeScene"; @@ -207,6 +207,37 @@ export class CustomizeScene extends AbstractCharacterScene { this.input.keyboard.on("keyup-LEFT", () => (this.moveHorizontally = -1)); this.input.keyboard.on("keyup-DOWN", () => (this.moveVertically = 1)); this.input.keyboard.on("keyup-UP", () => (this.moveVertically = -1)); + + this.input.keyboard.on("keydown-R", () => { + this.randomizeOutfit(); + }); + } + + private randomizeOutfit(): void { + this.customWokaPreviewer.updateSprite( + this.layers[0][Math.floor(Math.random() * this.layers[0].length)].id, + CustomWokaBodyPart.Body + ); + this.customWokaPreviewer.updateSprite( + this.layers[1][Math.floor(Math.random() * this.layers[1].length)].id, + CustomWokaBodyPart.Eyes + ); + this.customWokaPreviewer.updateSprite( + this.layers[2][Math.floor(Math.random() * this.layers[2].length)].id, + CustomWokaBodyPart.Hair + ); + this.customWokaPreviewer.updateSprite( + this.layers[3][Math.floor(Math.random() * this.layers[3].length)].id, + CustomWokaBodyPart.Clothes + ); + this.customWokaPreviewer.updateSprite( + this.layers[4][Math.floor(Math.random() * this.layers[4].length)].id, + CustomWokaBodyPart.Hat + ); + this.customWokaPreviewer.updateSprite( + this.layers[5][Math.floor(Math.random() * this.layers[5].length)].id, + CustomWokaBodyPart.Accessory + ); } private doMoveCursorHorizontally(index: number): void { diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index 5a898ae0..d54272a9 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -47,8 +47,8 @@ export class EntryScene extends Scene { // We can do it at this stage. waScaleManager.applyNewSize(); // this.scene.start(nextSceneName); - // this.scene.start(CustomizeSceneName); - this.scene.start(SelectCharacterSceneName); + this.scene.start(CustomizeSceneName); + // this.scene.start(SelectCharacterSceneName); }) .catch((err) => { const $LL = get(LL); From 36646cbdbe72e661209255c3ab121f3124a5dabc Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 11 Mar 2022 16:25:30 +0100 Subject: [PATCH 32/61] Add returns on pusher endpoints --- pusher/src/Controller/AdminController.ts | 1 + pusher/src/Controller/AuthenticateController.ts | 1 + pusher/src/Controller/BaseHttpController.ts | 2 ++ pusher/src/Controller/MapController.ts | 1 + pusher/src/Controller/OpenIdProfileController.ts | 1 + pusher/src/Controller/WokaListController.ts | 1 + 6 files changed, 7 insertions(+) diff --git a/pusher/src/Controller/AdminController.ts b/pusher/src/Controller/AdminController.ts index 7b288ab2..c579f95b 100644 --- a/pusher/src/Controller/AdminController.ts +++ b/pusher/src/Controller/AdminController.ts @@ -61,6 +61,7 @@ export class AdminController extends BaseHttpController { } res.send("ok"); + return; }); } diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 01a48f22..82760e62 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -371,6 +371,7 @@ export class AuthenticateController extends BaseHttpController { //get login profile res.status(302); res.setHeader("Location", adminApi.getProfileUrl(authTokenData.accessToken)); + res.send(""); return; } catch (error) { this.castErrorToResponse(error, res); diff --git a/pusher/src/Controller/BaseHttpController.ts b/pusher/src/Controller/BaseHttpController.ts index 454c92db..a15f7529 100644 --- a/pusher/src/Controller/BaseHttpController.ts +++ b/pusher/src/Controller/BaseHttpController.ts @@ -37,9 +37,11 @@ export class BaseHttpController { " " + (e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText) ); + return; } else { res.status(500); res.send("An error occurred"); + return; } } } diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index 208efd48..c6243713 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -159,6 +159,7 @@ export class MapController extends BaseHttpController { } res.json(mapDetails); + return; } catch (e) { this.castErrorToResponse(e, res); } diff --git a/pusher/src/Controller/OpenIdProfileController.ts b/pusher/src/Controller/OpenIdProfileController.ts index 1abfcf79..3ff4c948 100644 --- a/pusher/src/Controller/OpenIdProfileController.ts +++ b/pusher/src/Controller/OpenIdProfileController.ts @@ -23,6 +23,7 @@ export class OpenIdProfileController extends BaseHttpController { resCheckTokenAuth.picture as string | undefined ) ); + return; } catch (error) { console.error("profileCallback => ERROR", error); this.castErrorToResponse(error, res); diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts index 04be10f4..56300e90 100644 --- a/pusher/src/Controller/WokaListController.ts +++ b/pusher/src/Controller/WokaListController.ts @@ -7,6 +7,7 @@ export class WokaListController extends BaseHttpController { routes() { this.app.options("/woka/list/:roomUrl", {}, (req, res) => { res.status(200).send(""); + return; }); // eslint-disable-next-line @typescript-eslint/no-misused-promises From f4fd686699380763a63493dfe7b7daa609e967a5 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 14 Mar 2022 12:58:33 +0100 Subject: [PATCH 33/61] Change names of woka textures --- pusher/data/woka.json | 592 +++++++++++++++++++++--------------------- 1 file changed, 296 insertions(+), 296 deletions(-) diff --git a/pusher/data/woka.json b/pusher/data/woka.json index 04cc9cf7..a67bed1b 100644 --- a/pusher/data/woka.json +++ b/pusher/data/woka.json @@ -7,145 +7,145 @@ "textures": [ { "id": "male1", - "name": "male1", + "name": "Male 1", "url": "resources/characters/pipoya/Male 01-1.png", "position": 0 }, { "id": "male2", - "name": "male2", + "name": "Male 2", "url": "resources/characters/pipoya/Male 02-2.png", "position": 1 }, { "id": "male3", - "name": "male3", + "name": "Male 3", "url": "resources/characters/pipoya/Male 03-4.png", "position": 2 }, { "id": "male4", - "name": "male4", + "name": "Male 4", "url": "resources/characters/pipoya/Male 09-1.png", "position": 3 }, { "id": "male5", - "name": "male5", + "name": "Male 5", "url": "resources/characters/pipoya/Male 10-3.png", "position": 4 }, { "id": "male6", - "name": "male6", + "name": "Male 6", "url": "resources/characters/pipoya/Male 17-2.png", "position": 5 }, { "id": "male7", - "name": "male7", + "name": "Male 7", "url": "resources/characters/pipoya/Male 18-1.png", "position": 6 }, { "id": "male8", - "name": "male8", + "name": "Male 8", "url": "resources/characters/pipoya/Male 16-4.png", "position": 7 }, { "id": "male9", - "name": "male9", + "name": "Male 9", "url": "resources/characters/pipoya/Male 07-2.png", "position": 8 }, { "id": "male10", - "name": "male10", + "name": "Male 10", "url": "resources/characters/pipoya/Male 05-3.png", "position": 9 }, { "id": "male11", - "name": "male11", + "name": "Male 11", "url": "resources/characters/pipoya/Teacher male 02.png", "position": 10 }, { "id": "male12", - "name": "male12", + "name": "Male 12", "url": "resources/characters/pipoya/su4 Student male 12.png", "position": 11 }, { "id": "female1", - "name": "female1", + "name": "Female 1", "url": "resources/characters/pipoya/Female 01-1.png", "position": 12 }, { "id": "female2", - "name": "female2", + "name": "Female 2", "url": "resources/characters/pipoya/Female 02-2.png", "position": 13 }, { "id": "female3", - "name": "female3", + "name": "Female 3", "url": "resources/characters/pipoya/Female 03-4.png", "position": 14 }, { "id": "female4", - "name": "female4", + "name": "Female 4", "url": "resources/characters/pipoya/Female 09-1.png", "position": 15 }, { "id": "female5", - "name": "female5", + "name": "Female 5", "url": "resources/characters/pipoya/Female 10-3.png", "position": 16 }, { "id": "female6", - "name": "female6", + "name": "Female 6", "url": "resources/characters/pipoya/Female 17-2.png", "position": 17 }, { "id": "female7", - "name": "female7", + "name": "Female 7", "url": "resources/characters/pipoya/Female 18-1.png", "position": 18 }, { "id": "female8", - "name": "female8", + "name": "Female 8", "url": "resources/characters/pipoya/Female 16-4.png", "position": 19 }, { "id": "female9", - "name": "female9", + "name": "Female 9", "url": "resources/characters/pipoya/Female 07-2.png", "position": 20 }, { "id": "female10", - "name": "female10", + "name": "Female 10", "url": "resources/characters/pipoya/Female 05-3.png", "position": 21 }, { "id": "female11", - "name": "female11", + "name": "Female 11", "url": "resources/characters/pipoya/Teacher fmale 02.png", "position": 22 }, { "id": "female12", - "name": "female12", + "name": "Female 12", "url": "resources/characters/pipoya/su4 Student fmale 12.png", "position": 23 } @@ -162,199 +162,199 @@ "textures": [ { "id": "body1", - "name": "body1", + "name": "Body 1", "url": "resources/customisation/character_color/character_color0.png", "position": 0 }, { "id": "body2", - "name": "body2", + "name": "Body 2", "url": "resources/customisation/character_color/character_color1.png", "position": 1 }, { "id": "body3", - "name": "body3", + "name": "Body 3", "url": "resources/customisation/character_color/character_color2.png", "position": 2 }, { "id": "body4", - "name": "body4", + "name": "Body 4", "url": "resources/customisation/character_color/character_color3.png", "position": 3 }, { "id": "body5", - "name": "body5", + "name": "Body 5", "url": "resources/customisation/character_color/character_color4.png", "position": 4 }, { "id": "body6", - "name": "body6", + "name": "Body 6", "url": "resources/customisation/character_color/character_color5.png", "position": 5 }, { "id": "body7", - "name": "body7", + "name": "Body 7", "url": "resources/customisation/character_color/character_color6.png", "position": 6 }, { "id": "body8", - "name": "body8", + "name": "Body 8", "url": "resources/customisation/character_color/character_color7.png", "position": 7 }, { "id": "body9", - "name": "body9", + "name": "Body 9", "url": "resources/customisation/character_color/character_color8.png", "position": 8 }, { "id": "body10", - "name": "body10", + "name": "Body 10", "url": "resources/customisation/character_color/character_color9.png", "position": 9 }, { "id": "body11", - "name": "body11", + "name": "Body 11", "url": "resources/customisation/character_color/character_color10.png", "position": 10 }, { "id": "body12", - "name": "body12", + "name": "Body 12", "url": "resources/customisation/character_color/character_color11.png", "position": 11 }, { "id": "body13", - "name": "body13", + "name": "Body 13", "url": "resources/customisation/character_color/character_color12.png", "position": 12 }, { "id": "body14", - "name": "body14", + "name": "Body 14", "url": "resources/customisation/character_color/character_color13.png", "position": 13 }, { "id": "body15", - "name": "body15", + "name": "Body 15", "url": "resources/customisation/character_color/character_color14.png", "position": 14 }, { "id": "body16", - "name": "body16", + "name": "Body 16", "url": "resources/customisation/character_color/character_color15.png", "position": 15 }, { "id": "body17", - "name": "body17", + "name": "Body 17", "url": "resources/customisation/character_color/character_color16.png", "position": 16 }, { "id": "body18", - "name": "body18", + "name": "Body 18", "url": "resources/customisation/character_color/character_color17.png", "position": 17 }, { "id": "body19", - "name": "body19", + "name": "Body 19", "url": "resources/customisation/character_color/character_color18.png", "position": 18 }, { "id": "body20", - "name": "body20", + "name": "Body 20", "url": "resources/customisation/character_color/character_color19.png", "position": 19 }, { "id": "body21", - "name": "body21", + "name": "Body 21", "url": "resources/customisation/character_color/character_color20.png", "position": 20 }, { "id": "body22", - "name": "body22", + "name": "Body 22", "url": "resources/customisation/character_color/character_color21.png", "position": 21 }, { "id": "body23", - "name": "body23", + "name": "Body 23", "url": "resources/customisation/character_color/character_color22.png", "position": 22 }, { "id": "body24", - "name": "body24", + "name": "Body 24", "url": "resources/customisation/character_color/character_color23.png", "position": 23 }, { "id": "body25", - "name": "body25", + "name": "Body 25", "url": "resources/customisation/character_color/character_color24.png", "position": 24 }, { "id": "body26", - "name": "body26", + "name": "Body 26", "url": "resources/customisation/character_color/character_color25.png", "position": 25 }, { "id": "body27", - "name": "body27", + "name": "Body 27", "url": "resources/customisation/character_color/character_color26.png", "position": 26 }, { "id": "body28", - "name": "body28", + "name": "Body 28", "url": "resources/customisation/character_color/character_color27.png", "position": 27 }, { "id": "body29", - "name": "body29", + "name": "Body 29", "url": "resources/customisation/character_color/character_color28.png", "position": 28 }, { "id": "body30", - "name": "body30", + "name": "Body 30", "url": "resources/customisation/character_color/character_color29.png", "position": 29 }, { "id": "body31", - "name": "body31", + "name": "Body 31", "url": "resources/customisation/character_color/character_color30.png", "position": 30 }, { "id": "body32", - "name": "body32", + "name": "Body 32", "url": "resources/customisation/character_color/character_color31.png", "position": 31 }, { "id": "body33", - "name": "body33", + "name": "Body 33", "url": "resources/customisation/character_color/character_color32.png", "position": 32 } @@ -371,181 +371,181 @@ "textures": [ { "id": "eyes1", - "name": "eyes1", + "name": "Eyes 1", "url": "resources/customisation/character_eyes/character_eyes1.png", "position": 0 }, { "id": "eyes2", - "name": "eyes2", + "name": "Eyes 2", "url": "resources/customisation/character_eyes/character_eyes2.png", "position": 1 }, { "id": "eyes3", - "name": "eyes3", + "name": "Eyes 3", "url": "resources/customisation/character_eyes/character_eyes3.png", "position": 2 }, { "id": "eyes4", - "name": "eyes4", + "name": "Eyes 4", "url": "resources/customisation/character_eyes/character_eyes4.png", "position": 3 }, { "id": "eyes5", - "name": "eyes5", + "name": "Eyes 5", "url": "resources/customisation/character_eyes/character_eyes5.png", "position": 4 }, { "id": "eyes6", - "name": "eyes6", + "name": "Eyes 6", "url": "resources/customisation/character_eyes/character_eyes6.png", "position": 5 }, { "id": "eyes7", - "name": "eyes7", + "name": "Eyes 7", "url": "resources/customisation/character_eyes/character_eyes7.png", "position": 6 }, { "id": "eyes8", - "name": "eyes8", + "name": "Eyes 8", "url": "resources/customisation/character_eyes/character_eyes8.png", "position": 7 }, { "id": "eyes9", - "name": "eyes9", + "name": "Eyes 9", "url": "resources/customisation/character_eyes/character_eyes9.png", "position": 8 }, { "id": "eyes10", - "name": "eyes10", + "name": "Eyes 10", "url": "resources/customisation/character_eyes/character_eyes10.png", "position": 9 }, { "id": "eyes11", - "name": "eyes11", + "name": "Eyes 11", "url": "resources/customisation/character_eyes/character_eyes11.png", "position": 10 }, { "id": "eyes12", - "name": "eyes12", + "name": "Eyes 12", "url": "resources/customisation/character_eyes/character_eyes12.png", "position": 11 }, { "id": "eyes13", - "name": "eyes13", + "name": "Eyes 13", "url": "resources/customisation/character_eyes/character_eyes13.png", "position": 12 }, { "id": "eyes14", - "name": "eyes14", + "name": "Eyes 14", "url": "resources/customisation/character_eyes/character_eyes14.png", "position": 13 }, { "id": "eyes15", - "name": "eyes15", + "name": "Eyes 15", "url": "resources/customisation/character_eyes/character_eyes15.png", "position": 14 }, { "id": "eyes16", - "name": "eyes16", + "name": "Eyes 16", "url": "resources/customisation/character_eyes/character_eyes16.png", "position": 15 }, { "id": "eyes17", - "name": "eyes17", + "name": "Eyes 17", "url": "resources/customisation/character_eyes/character_eyes17.png", "position": 16 }, { "id": "eyes18", - "name": "eyes18", + "name": "Eyes 18", "url": "resources/customisation/character_eyes/character_eyes18.png", "position": 17 }, { "id": "eyes19", - "name": "eyes19", + "name": "Eyes 19", "url": "resources/customisation/character_eyes/character_eyes19.png", "position": 18 }, { "id": "eyes20", - "name": "eyes20", + "name": "Eyes 20", "url": "resources/customisation/character_eyes/character_eyes20.png", "position": 19 }, { "id": "eyes21", - "name": "eyes21", + "name": "Eyes 21", "url": "resources/customisation/character_eyes/character_eyes21.png", "position": 20 }, { "id": "eyes22", - "name": "eyes22", + "name": "Eyes 22", "url": "resources/customisation/character_eyes/character_eyes22.png", "position": 21 }, { "id": "eyes23", - "name": "eyes23", + "name": "Eyes 23", "url": "resources/customisation/character_eyes/character_eyes23.png", "position": 22 }, { "id": "eyes24", - "name": "eyes24", + "name": "Eyes 24", "url": "resources/customisation/character_eyes/character_eyes24.png", "position": 23 }, { "id": "eyes25", - "name": "eyes25", + "name": "Eyes 25", "url": "resources/customisation/character_eyes/character_eyes25.png", "position": 24 }, { "id": "eyes26", - "name": "eyes26", + "name": "Eyes 26", "url": "resources/customisation/character_eyes/character_eyes26.png", "position": 25 }, { "id": "eyes27", - "name": "eyes27", + "name": "Eyes 27", "url": "resources/customisation/character_eyes/character_eyes27.png", "position": 26 }, { "id": "eyes28", - "name": "eyes28", + "name": "Eyes 28", "url": "resources/customisation/character_eyes/character_eyes28.png", "position": 27 }, { "id": "eyes29", - "name": "eyes29", + "name": "Eyes 29", "url": "resources/customisation/character_eyes/character_eyes29.png", "position": 28 }, { "id": "eyes30", - "name": "eyes30", + "name": "Eyes 30", "url": "resources/customisation/character_eyes/character_eyes30.png", "position": 29 } @@ -561,445 +561,445 @@ "textures": [ { "id": "hair1", - "name": "hair1", + "name": "Hair 1", "url": "resources/customisation/character_hairs/character_hairs0.png", "position": 0 }, { "id": "hair2", - "name": "hair2", + "name": "Hair 2", "url": "resources/customisation/character_hairs/character_hairs1.png", "position": 1 }, { "id": "hair3", - "name": "hair3", + "name": "Hair 3", "url": "resources/customisation/character_hairs/character_hairs2.png", "position": 2 }, { "id": "hair4", - "name": "hair4", + "name": "Hair 4", "url": "resources/customisation/character_hairs/character_hairs3.png", "position": 3 }, { "id": "hair5", - "name": "hair5", + "name": "Hair 5", "url": "resources/customisation/character_hairs/character_hairs4.png", "position": 4 }, { "id": "hair6", - "name": "hair6", + "name": "Hair 6", "url": "resources/customisation/character_hairs/character_hairs5.png", "position": 5 }, { "id": "hair7", - "name": "hair7", + "name": "Hair 7", "url": "resources/customisation/character_hairs/character_hairs6.png", "position": 6 }, { "id": "hair8", - "name": "hair8", + "name": "Hair 8", "url": "resources/customisation/character_hairs/character_hairs7.png", "position": 7 }, { "id": "hair9", - "name": "hair9", + "name": "Hair 9", "url": "resources/customisation/character_hairs/character_hairs8.png", "position": 8 }, { "id": "hair10", - "name": "hair10", + "name": "Hair 10", "url": "resources/customisation/character_hairs/character_hairs9.png", "position": 9 }, { "id": "hair11", - "name": "hair11", + "name": "Hair 11", "url": "resources/customisation/character_hairs/character_hairs10.png", "position": 10 }, { "id": "hair12", - "name": "hair12", + "name": "Hair 12", "url": "resources/customisation/character_hairs/character_hairs11.png", "position": 11 }, { "id": "hair13", - "name": "hair13", + "name": "Hair 13", "url": "resources/customisation/character_hairs/character_hairs12.png", "position": 12 }, { "id": "hair14", - "name": "hair14", + "name": "Hair 14", "url": "resources/customisation/character_hairs/character_hairs13.png", "position": 13 }, { "id": "hair15", - "name": "hair15", + "name": "Hair 15", "url": "resources/customisation/character_hairs/character_hairs14.png", "position": 14 }, { "id": "hair16", - "name": "hair16", + "name": "Hair 16", "url": "resources/customisation/character_hairs/character_hairs15.png", "position": 15 }, { "id": "hair17", - "name": "hair17", + "name": "Hair 17", "url": "resources/customisation/character_hairs/character_hairs16.png", "position": 16 }, { "id": "hair18", - "name": "hair18", + "name": "Hair 18", "url": "resources/customisation/character_hairs/character_hairs17.png", "position": 17 }, { "id": "hair19", - "name": "hair19", + "name": "Hair 19", "url": "resources/customisation/character_hairs/character_hairs18.png", "position": 18 }, { "id": "hair20", - "name": "hair20", + "name": "Hair 20", "url": "resources/customisation/character_hairs/character_hairs19.png", "position": 19 }, { "id": "hair21", - "name": "hair21", + "name": "Hair 21", "url": "resources/customisation/character_hairs/character_hairs20.png", "position": 20 }, { "id": "hair22", - "name": "hair22", + "name": "Hair 22", "url": "resources/customisation/character_hairs/character_hairs21.png", "position": 21 }, { "id": "hair23", - "name": "hair23", + "name": "Hair 23", "url": "resources/customisation/character_hairs/character_hairs22.png", "position": 22 }, { "id": "hair24", - "name": "hair24", + "name": "Hair 24", "url": "resources/customisation/character_hairs/character_hairs23.png", "position": 23 }, { "id": "hair25", - "name": "hair25", + "name": "Hair 25", "url": "resources/customisation/character_hairs/character_hairs24.png", "position": 24 }, { "id": "hair26", - "name": "hair26", + "name": "Hair 26", "url": "resources/customisation/character_hairs/character_hairs25.png", "position": 25 }, { "id": "hair27", - "name": "hair27", + "name": "Hair 27", "url": "resources/customisation/character_hairs/character_hairs26.png", "position": 26 }, { "id": "hair28", - "name": "hair28", + "name": "Hair 28", "url": "resources/customisation/character_hairs/character_hairs27.png", "position": 27 }, { "id": "hair29", - "name": "hair29", + "name": "Hair 29", "url": "resources/customisation/character_hairs/character_hairs28.png", "position": 28 }, { "id": "hair30", - "name": "hair30", + "name": "Hair 30", "url": "resources/customisation/character_hairs/character_hairs29.png", "position": 29 }, { "id": "hair31", - "name": "hair31", + "name": "Hair 31", "url": "resources/customisation/character_hairs/character_hairs30.png", "position": 30 }, { "id": "hair32", - "name": "hair32", + "name": "Hair 32", "url": "resources/customisation/character_hairs/character_hairs31.png", "position": 31 }, { "id": "hair33", - "name": "hair33", + "name": "Hair 33", "url": "resources/customisation/character_hairs/character_hairs32.png", "position": 32 }, { "id": "hair34", - "name": "hair34", + "name": "Hair 34", "url": "resources/customisation/character_hairs/character_hairs33.png", "position": 33 }, { "id": "hair35", - "name": "hair35", + "name": "Hair 35", "url": "resources/customisation/character_hairs/character_hairs34.png", "position": 34 }, { "id": "hair36", - "name": "hair36", + "name": "Hair 36", "url": "resources/customisation/character_hairs/character_hairs35.png", "position": 35 }, { "id": "hair37", - "name": "hair37", + "name": "Hair 37", "url": "resources/customisation/character_hairs/character_hairs36.png", "position": 36 }, { "id": "hair38", - "name": "hair38", + "name": "Hair 38", "url": "resources/customisation/character_hairs/character_hairs37.png", "position": 37 }, { "id": "hair39", - "name": "hair39", + "name": "Hair 39", "url": "resources/customisation/character_hairs/character_hairs38.png", "position": 38 }, { "id": "hair40", - "name": "hair40", + "name": "Hair 40", "url": "resources/customisation/character_hairs/character_hairs39.png", "position": 39 }, { "id": "hair41", - "name": "hair41", + "name": "Hair 41", "url": "resources/customisation/character_hairs/character_hairs40.png", "position": 40 }, { "id": "hair42", - "name": "hair42", + "name": "Hair 42", "url": "resources/customisation/character_hairs/character_hairs41.png", "position": 41 }, { "id": "hair43", - "name": "hair43", + "name": "Hair 43", "url": "resources/customisation/character_hairs/character_hairs42.png", "position": 42 }, { "id": "hair44", - "name": "hair44", + "name": "Hair 44", "url": "resources/customisation/character_hairs/character_hairs43.png", "position": 43 }, { "id": "hair45", - "name": "hair45", + "name": "Hair 45", "url": "resources/customisation/character_hairs/character_hairs44.png", "position": 44 }, { "id": "hair46", - "name": "hair46", + "name": "Hair 46", "url": "resources/customisation/character_hairs/character_hairs45.png", "position": 45 }, { "id": "hair47", - "name": "hair47", + "name": "Hair 47", "url": "resources/customisation/character_hairs/character_hairs46.png", "position": 46 }, { "id": "hair48", - "name": "hair48", + "name": "Hair 48", "url": "resources/customisation/character_hairs/character_hairs47.png", "position": 47 }, { "id": "hair49", - "name": "hair49", + "name": "Hair 49", "url": "resources/customisation/character_hairs/character_hairs48.png", "position": 48 }, { "id": "hair50", - "name": "hair50", + "name": "Hair 50", "url": "resources/customisation/character_hairs/character_hairs49.png", "position": 49 }, { "id": "hair51", - "name": "hair51", + "name": "Hair 51", "url": "resources/customisation/character_hairs/character_hairs50.png", "position": 50 }, { "id": "hair52", - "name": "hair52", + "name": "Hair 52", "url": "resources/customisation/character_hairs/character_hairs51.png", "position": 51 }, { "id": "hair53", - "name": "hair53", + "name": "Hair 53", "url": "resources/customisation/character_hairs/character_hairs52.png", "position": 52 }, { "id": "hair54", - "name": "hair54", + "name": "Hair 54", "url": "resources/customisation/character_hairs/character_hairs53.png", "position": 53 }, { "id": "hair55", - "name": "hair55", + "name": "Hair 55", "url": "resources/customisation/character_hairs/character_hairs54.png", "position": 54 }, { "id": "hair56", - "name": "hair56", + "name": "Hair 56", "url": "resources/customisation/character_hairs/character_hairs55.png", "position": 55 }, { "id": "hair57", - "name": "hair57", + "name": "Hair 57", "url": "resources/customisation/character_hairs/character_hairs56.png", "position": 56 }, { "id": "hair58", - "name": "hair58", + "name": "Hair 58", "url": "resources/customisation/character_hairs/character_hairs57.png", "position": 57 }, { "id": "hair59", - "name": "hair59", + "name": "Hair 59", "url": "resources/customisation/character_hairs/character_hairs58.png", "position": 58 }, { "id": "hair60", - "name": "hair60", + "name": "Hair 60", "url": "resources/customisation/character_hairs/character_hairs59.png", "position": 59 }, { "id": "hair61", - "name": "hair61", + "name": "Hair 61", "url": "resources/customisation/character_hairs/character_hairs60.png", "position": 60 }, { "id": "hair62", - "name": "hair62", + "name": "Hair 62", "url": "resources/customisation/character_hairs/character_hairs61.png", "position": 61 }, { "id": "hair63", - "name": "hair63", + "name": "Hair 63", "url": "resources/customisation/character_hairs/character_hairs62.png", "position": 62 }, { "id": "hair64", - "name": "hair64", + "name": "Hair 64", "url": "resources/customisation/character_hairs/character_hairs63.png", "position": 63 }, { "id": "hair65", - "name": "hair65", + "name": "Hair 65", "url": "resources/customisation/character_hairs/character_hairs64.png", "position": 64 }, { "id": "hair66", - "name": "hair66", + "name": "Hair 66", "url": "resources/customisation/character_hairs/character_hairs65.png", "position": 65 }, { "id": "hair67", - "name": "hair67", + "name": "Hair 67", "url": "resources/customisation/character_hairs/character_hairs66.png", "position": 66 }, { "id": "hair68", - "name": "hair68", + "name": "Hair 68", "url": "resources/customisation/character_hairs/character_hairs67.png", "position": 67 }, { "id": "hair69", - "name": "hair69", + "name": "Hair 69", "url": "resources/customisation/character_hairs/character_hairs68.png", "position": 68 }, { "id": "hair70", - "name": "hair70", + "name": "Hair 70", "url": "resources/customisation/character_hairs/character_hairs69.png", "position": 69 }, { "id": "hair71", - "name": "hair71", + "name": "Hair 71", "url": "resources/customisation/character_hairs/character_hairs70.png", "position": 70 }, { "id": "hair72", - "name": "hair72", + "name": "Hair 72", "url": "resources/customisation/character_hairs/character_hairs71.png", "position": 71 }, { "id": "hair73", - "name": "hair73", + "name": "Hair 73", "url": "resources/customisation/character_hairs/character_hairs72.png", "position": 72 }, { "id": "hair74", - "name": "hair74", + "name": "Hair 74", "url": "resources/customisation/character_hairs/character_hairs73.png", "position": 73 } @@ -1015,445 +1015,445 @@ "textures": [ { "id": "clothes1", - "name": "clothes1", + "name": "Clothes 1", "url": "resources/customisation/character_clothes/character_clothes0.png", "position": 0 }, { "id": "clothes2", - "name": "clothes2", + "name": "Clothes 2", "url": "resources/customisation/character_clothes/character_clothes1.png", "position": 1 }, { "id": "clothes3", - "name": "clothes3", + "name": "Clothes 3", "url": "resources/customisation/character_clothes/character_clothes2.png", "position": 2 }, { "id": "clothes4", - "name": "clothes4", + "name": "Clothes 4", "url": "resources/customisation/character_clothes/character_clothes3.png", "position": 3 }, { "id": "clothes5", - "name": "clothes5", + "name": "Clothes 5", "url": "resources/customisation/character_clothes/character_clothes4.png", "position": 4 }, { "id": "clothes6", - "name": "clothes6", + "name": "Clothes 6", "url": "resources/customisation/character_clothes/character_clothes5.png", "position": 5 }, { "id": "clothes7", - "name": "clothes7", + "name": "Clothes 7", "url": "resources/customisation/character_clothes/character_clothes6.png", "position": 6 }, { "id": "clothes8", - "name": "clothes8", + "name": "Clothes 8", "url": "resources/customisation/character_clothes/character_clothes7.png", "position": 7 }, { "id": "clothes9", - "name": "clothes9", + "name": "Clothes 9", "url": "resources/customisation/character_clothes/character_clothes8.png", "position": 8 }, { "id": "clothes10", - "name": "clothes10", + "name": "Clothes 10", "url": "resources/customisation/character_clothes/character_clothes9.png", "position": 9 }, { "id": "clothes11", - "name": "clothes11", + "name": "Clothes 11", "url": "resources/customisation/character_clothes/character_clothes10.png", "position": 10 }, { "id": "clothes12", - "name": "clothes12", + "name": "Clothes 12", "url": "resources/customisation/character_clothes/character_clothes11.png", "position": 11 }, { "id": "clothes13", - "name": "clothes13", + "name": "Clothes 13", "url": "resources/customisation/character_clothes/character_clothes12.png", "position": 12 }, { "id": "clothes14", - "name": "clothes14", + "name": "Clothes 14", "url": "resources/customisation/character_clothes/character_clothes13.png", "position": 13 }, { "id": "clothes15", - "name": "clothes15", + "name": "Clothes 15", "url": "resources/customisation/character_clothes/character_clothes14.png", "position": 14 }, { "id": "clothes16", - "name": "clothes16", + "name": "Clothes 16", "url": "resources/customisation/character_clothes/character_clothes15.png", "position": 15 }, { "id": "clothes17", - "name": "clothes17", + "name": "Clothes 17", "url": "resources/customisation/character_clothes/character_clothes16.png", "position": 16 }, { "id": "clothes18", - "name": "clothes18", + "name": "Clothes 18", "url": "resources/customisation/character_clothes/character_clothes17.png", "position": 17 }, { "id": "clothes19", - "name": "clothes19", + "name": "Clothes 19", "url": "resources/customisation/character_clothes/character_clothes18.png", "position": 18 }, { "id": "clothes20", - "name": "clothes20", + "name": "Clothes 20", "url": "resources/customisation/character_clothes/character_clothes19.png", "position": 19 }, { "id": "clothes21", - "name": "clothes21", + "name": "Clothes 21", "url": "resources/customisation/character_clothes/character_clothes20.png", "position": 20 }, { "id": "clothes22", - "name": "clothes22", + "name": "Clothes 22", "url": "resources/customisation/character_clothes/character_clothes21.png", "position": 21 }, { "id": "clothes23", - "name": "clothes23", + "name": "Clothes 23", "url": "resources/customisation/character_clothes/character_clothes22.png", "position": 22 }, { "id": "clothes24", - "name": "clothes24", + "name": "Clothes 24", "url": "resources/customisation/character_clothes/character_clothes23.png", "position": 23 }, { "id": "clothes25", - "name": "clothes25", + "name": "Clothes 25", "url": "resources/customisation/character_clothes/character_clothes24.png", "position": 24 }, { "id": "clothes26", - "name": "clothes26", + "name": "Clothes 26", "url": "resources/customisation/character_clothes/character_clothes25.png", "position": 25 }, { "id": "clothes27", - "name": "clothes27", + "name": "Clothes 27", "url": "resources/customisation/character_clothes/character_clothes26.png", "position": 26 }, { "id": "clothes28", - "name": "clothes28", + "name": "Clothes 28", "url": "resources/customisation/character_clothes/character_clothes27.png", "position": 27 }, { "id": "clothes29", - "name": "clothes29", + "name": "Clothes 29", "url": "resources/customisation/character_clothes/character_clothes28.png", "position": 28 }, { "id": "clothes30", - "name": "clothes30", + "name": "Clothes 30", "url": "resources/customisation/character_clothes/character_clothes29.png", "position": 29 }, { "id": "clothes31", - "name": "clothes31", + "name": "Clothes 31", "url": "resources/customisation/character_clothes/character_clothes30.png", "position": 30 }, { "id": "clothes32", - "name": "clothes32", + "name": "Clothes 32", "url": "resources/customisation/character_clothes/character_clothes31.png", "position": 31 }, { "id": "clothes33", - "name": "clothes33", + "name": "Clothes 33", "url": "resources/customisation/character_clothes/character_clothes32.png", "position": 32 }, { "id": "clothes34", - "name": "clothes34", + "name": "Clothes 34", "url": "resources/customisation/character_clothes/character_clothes33.png", "position": 33 }, { "id": "clothes35", - "name": "clothes35", + "name": "Clothes 35", "url": "resources/customisation/character_clothes/character_clothes34.png", "position": 34 }, { "id": "clothes36", - "name": "clothes36", + "name": "Clothes 36", "url": "resources/customisation/character_clothes/character_clothes35.png", "position": 35 }, { "id": "clothes37", - "name": "clothes37", + "name": "Clothes 37", "url": "resources/customisation/character_clothes/character_clothes36.png", "position": 36 }, { "id": "clothes38", - "name": "clothes38", + "name": "Clothes 38", "url": "resources/customisation/character_clothes/character_clothes37.png", "position": 37 }, { "id": "clothes39", - "name": "clothes39", + "name": "Clothes 39", "url": "resources/customisation/character_clothes/character_clothes38.png", "position": 38 }, { "id": "clothes40", - "name": "clothes40", + "name": "Clothes 40", "url": "resources/customisation/character_clothes/character_clothes39.png", "position": 39 }, { "id": "clothes41", - "name": "clothes41", + "name": "Clothes 41", "url": "resources/customisation/character_clothes/character_clothes40.png", "position": 40 }, { "id": "clothes42", - "name": "clothes42", + "name": "Clothes 42", "url": "resources/customisation/character_clothes/character_clothes41.png", "position": 41 }, { "id": "clothes43", - "name": "clothes43", + "name": "Clothes 43", "url": "resources/customisation/character_clothes/character_clothes42.png", "position": 42 }, { "id": "clothes44", - "name": "clothes44", + "name": "Clothes 44", "url": "resources/customisation/character_clothes/character_clothes43.png", "position": 43 }, { "id": "clothes45", - "name": "clothes45", + "name": "Clothes 45", "url": "resources/customisation/character_clothes/character_clothes44.png", "position": 44 }, { "id": "clothes46", - "name": "clothes46", + "name": "Clothes 46", "url": "resources/customisation/character_clothes/character_clothes45.png", "position": 45 }, { "id": "clothes47", - "name": "clothes47", + "name": "Clothes 47", "url": "resources/customisation/character_clothes/character_clothes46.png", "position": 46 }, { "id": "clothes48", - "name": "clothes48", + "name": "Clothes 48", "url": "resources/customisation/character_clothes/character_clothes47.png", "position": 47 }, { "id": "clothes49", - "name": "clothes49", + "name": "Clothes 49", "url": "resources/customisation/character_clothes/character_clothes48.png", "position": 48 }, { "id": "clothes50", - "name": "clothes50", + "name": "Clothes 50", "url": "resources/customisation/character_clothes/character_clothes49.png", "position": 49 }, { "id": "clothes51", - "name": "clothes51", + "name": "Clothes 51", "url": "resources/customisation/character_clothes/character_clothes50.png", "position": 50 }, { "id": "clothes52", - "name": "clothes52", + "name": "Clothes 52", "url": "resources/customisation/character_clothes/character_clothes51.png", "position": 51 }, { "id": "clothes53", - "name": "clothes53", + "name": "Clothes 53", "url": "resources/customisation/character_clothes/character_clothes52.png", "position": 52 }, { "id": "clothes54", - "name": "clothes54", + "name": "Clothes 54", "url": "resources/customisation/character_clothes/character_clothes53.png", "position": 53 }, { "id": "clothes55", - "name": "clothes55", + "name": "Clothes 55", "url": "resources/customisation/character_clothes/character_clothes54.png", "position": 54 }, { "id": "clothes56", - "name": "clothes56", + "name": "Clothes 56", "url": "resources/customisation/character_clothes/character_clothes55.png", "position": 55 }, { "id": "clothes57", - "name": "clothes57", + "name": "Clothes 57", "url": "resources/customisation/character_clothes/character_clothes56.png", "position": 56 }, { "id": "clothes58", - "name": "clothes58", + "name": "Clothes 58", "url": "resources/customisation/character_clothes/character_clothes57.png", "position": 57 }, { "id": "clothes59", - "name": "clothes59", + "name": "Clothes 59", "url": "resources/customisation/character_clothes/character_clothes58.png", "position": 58 }, { "id": "clothes60", - "name": "clothes60", + "name": "Clothes 60", "url": "resources/customisation/character_clothes/character_clothes59.png", "position": 59 }, { "id": "clothes61", - "name": "clothes61", + "name": "Clothes 61", "url": "resources/customisation/character_clothes/character_clothes60.png", "position": 60 }, { "id": "clothes62", - "name": "clothes62", + "name": "Clothes 62", "url": "resources/customisation/character_clothes/character_clothes61.png", "position": 61 }, { "id": "clothes63", - "name": "clothes63", + "name": "Clothes 63", "url": "resources/customisation/character_clothes/character_clothes62.png", "position": 62 }, { "id": "clothes64", - "name": "clothes64", + "name": "Clothes 64", "url": "resources/customisation/character_clothes/character_clothes63.png", "position": 63 }, { "id": "clothes65", - "name": "clothes65", + "name": "Clothes 65", "url": "resources/customisation/character_clothes/character_clothes64.png", "position": 64 }, { "id": "clothes66", - "name": "clothes66", + "name": "Clothes 66", "url": "resources/customisation/character_clothes/character_clothes65.png", "position": 65 }, { "id": "clothes67", - "name": "clothes67", + "name": "Clothes 67", "url": "resources/customisation/character_clothes/character_clothes66.png", "position": 66 }, { "id": "clothes68", - "name": "clothes68", + "name": "Clothes 68", "url": "resources/customisation/character_clothes/character_clothes67.png", "position": 67 }, { "id": "clothes69", - "name": "clothes69", + "name": "Clothes 69", "url": "resources/customisation/character_clothes/character_clothes68.png", "position": 68 }, { "id": "clothes70", - "name": "clothes70", + "name": "Clothes 70", "url": "resources/customisation/character_clothes/character_clothes69.png", "position": 69 }, { "id": "clothes_pride_shirt", - "name": "clothes_pride_shirt", + "name": "Pride Shirt", "url": "resources/customisation/character_clothes/pride_shirt.png", "position": 70 }, { "id": "clothes_black_hoodie", - "name": "clothes_black_hoodie", + "name": "Black Hoodie", "url": "resources/customisation/character_clothes/black_hoodie.png", "position": 71 }, { "id": "clothes_white_hoodie", - "name": "clothes_white_hoodie", + "name": "White Hoodie", "url": "resources/customisation/character_clothes/white_hoodie.png", "position": 72 }, { "id": "clothes_engelbert", - "name": "clothes_engelbert", + "name": "Engelbert", "url": "resources/customisation/character_clothes/engelbert.png", "position": 73 } @@ -1469,163 +1469,163 @@ "textures": [ { "id": "hat1", - "name": "hat1", + "name": "Hat 1", "url": "resources/customisation/character_hats/character_hats1.png", "position": 0 }, { "id": "hat2", - "name": "hat2", + "name": "Hat 2", "url": "resources/customisation/character_hats/character_hats2.png", "position": 1 }, { "id": "hat3", - "name": "hat3", + "name": "Hat 3", "url": "resources/customisation/character_hats/character_hats3.png", "position": 2 }, { "id": "hat4", - "name": "hat4", + "name": "Hat 4", "url": "resources/customisation/character_hats/character_hats4.png", "position": 3 }, { "id": "hat5", - "name": "hat5", + "name": "Hat 5", "url": "resources/customisation/character_hats/character_hats5.png", "position": 4 }, { "id": "hat6", - "name": "hat6", + "name": "Hat 6", "url": "resources/customisation/character_hats/character_hats6.png", "position": 5 }, { "id": "hat7", - "name": "hat7", + "name": "Hat 7", "url": "resources/customisation/character_hats/character_hats7.png", "position": 6 }, { "id": "hat8", - "name": "hat8", + "name": "Hat 8", "url": "resources/customisation/character_hats/character_hats8.png", "position": 7 }, { "id": "hat9", - "name": "hat9", + "name": "Hat 9", "url": "resources/customisation/character_hats/character_hats9.png", "position": 8 }, { "id": "hat10", - "name": "hat10", + "name": "Hat 10", "url": "resources/customisation/character_hats/character_hats10.png", "position": 9 }, { "id": "hat11", - "name": "hat11", + "name": "Hat 11", "url": "resources/customisation/character_hats/character_hats11.png", "position": 10 }, { "id": "hat12", - "name": "hat12", + "name": "Hat 12", "url": "resources/customisation/character_hats/character_hats12.png", "position": 11 }, { "id": "hat13", - "name": "hat13", + "name": "Hat 13", "url": "resources/customisation/character_hats/character_hats13.png", "position": 12 }, { "id": "hat14", - "name": "hat14", + "name": "Hat 14", "url": "resources/customisation/character_hats/character_hats14.png", "position": 13 }, { "id": "hat15", - "name": "hat15", + "name": "Hat 15", "url": "resources/customisation/character_hats/character_hats15.png", "position": 14 }, { "id": "hat16", - "name": "hat16", + "name": "Hat 16", "url": "resources/customisation/character_hats/character_hats16.png", "position": 15 }, { "id": "hat17", - "name": "hat17", + "name": "Hat 17", "url": "resources/customisation/character_hats/character_hats17.png", "position": 16 }, { "id": "hat18", - "name": "hat18", + "name": "Hat 18", "url": "resources/customisation/character_hats/character_hats18.png", "position": 17 }, { "id": "hat19", - "name": "hat19", + "name": "Hat 19", "url": "resources/customisation/character_hats/character_hats19.png", "position": 18 }, { "id": "hat20", - "name": "hat20", + "name": "Hat 20", "url": "resources/customisation/character_hats/character_hats20.png", "position": 19 }, { "id": "hat21", - "name": "hat21", + "name": "Hat 21", "url": "resources/customisation/character_hats/character_hats21.png", "position": 20 }, { "id": "hat22", - "name": "hat22", + "name": "Hat 22", "url": "resources/customisation/character_hats/character_hats22.png", "position": 21 }, { "id": "hat23", - "name": "hat23", + "name": "Hat 23", "url": "resources/customisation/character_hats/character_hats23.png", "position": 22 }, { "id": "hat24", - "name": "hat24", + "name": "Hat 24", "url": "resources/customisation/character_hats/character_hats24.png", "position": 23 }, { "id": "hat25", - "name": "hat25", + "name": "Hat 25", "url": "resources/customisation/character_hats/character_hats25.png", "position": 24 }, { "id": "hat26", - "name": "hat26", + "name": "Hat 26", "url": "resources/customisation/character_hats/character_hats26.png", "position": 25 }, { "id": "tinfoil_hat1", - "name": "tinfoil_hat1", + "name": "Paper Boat Hat", "url": "resources/customisation/character_hats/tinfoil_hat1.png", "position": 26 } @@ -1642,205 +1642,205 @@ "textures": [ { "id": "accessory1", - "name": "accessory1", + "name": "Accessory 1", "url": "resources/customisation/character_accessories/character_accessories1.png", "position": 0 }, { "id": "accessory2", - "name": "accessory2", + "name": "Accessory 2", "url": "resources/customisation/character_accessories/character_accessories2.png", "position": 1 }, { "id": "accessory3", - "name": "accessory3", + "name": "Accessory 3", "url": "resources/customisation/character_accessories/character_accessories3.png", "position": 2 }, { "id": "accessory4", - "name": "accessory4", + "name": "Accessory 4", "url": "resources/customisation/character_accessories/character_accessories4.png", "position": 3 }, { "id": "accessory5", - "name": "accessory5", + "name": "Accessory 5", "url": "resources/customisation/character_accessories/character_accessories5.png", "position": 4 }, { "id": "accessory6", - "name": "accessory6", + "name": "Accessory 6", "url": "resources/customisation/character_accessories/character_accessories6.png", "position": 5 }, { "id": "accessory7", - "name": "accessory7", + "name": "Accessory 7", "url": "resources/customisation/character_accessories/character_accessories7.png", "position": 6 }, { "id": "accessory8", - "name": "accessory8", + "name": "Accessory 8", "url": "resources/customisation/character_accessories/character_accessories8.png", "position": 7 }, { "id": "accessory9", - "name": "accessory9", + "name": "Accessory 9", "url": "resources/customisation/character_accessories/character_accessories9.png", "position": 8 }, { "id": "accessory10", - "name": "accessory10", + "name": "Accessory 10", "url": "resources/customisation/character_accessories/character_accessories10.png", "position": 9 }, { "id": "accessory11", - "name": "accessory11", + "name": "Accessory 11", "url": "resources/customisation/character_accessories/character_accessories11.png", "position": 10 }, { "id": "accessory12", - "name": "accessory12", + "name": "Accessory 12", "url": "resources/customisation/character_accessories/character_accessories12.png", "position": 11 }, { "id": "accessory13", - "name": "accessory13", + "name": "Accessory 13", "url": "resources/customisation/character_accessories/character_accessories13.png", "position": 12 }, { "id": "accessory14", - "name": "accessory14", + "name": "Accessory 14", "url": "resources/customisation/character_accessories/character_accessories14.png", "position": 13 }, { "id": "accessory15", - "name": "accessory15", + "name": "Accessory 15", "url": "resources/customisation/character_accessories/character_accessories15.png", "position": 14 }, { "id": "accessory16", - "name": "accessory16", + "name": "Accessory 16", "url": "resources/customisation/character_accessories/character_accessories16.png", "position": 15 }, { "id": "accessory17", - "name": "accessory17", + "name": "Accessory 17", "url": "resources/customisation/character_accessories/character_accessories17.png", "position": 16 }, { "id": "accessory18", - "name": "accessory18", + "name": "Accessory 18", "url": "resources/customisation/character_accessories/character_accessories18.png", "position": 17 }, { "id": "accessory19", - "name": "accessory19", + "name": "Accessory 19", "url": "resources/customisation/character_accessories/character_accessories19.png", "position": 18 }, { "id": "accessory20", - "name": "accessory20", + "name": "Accessory 20", "url": "resources/customisation/character_accessories/character_accessories20.png", "position": 19 }, { "id": "accessory21", - "name": "accessory21", + "name": "Accessory 21", "url": "resources/customisation/character_accessories/character_accessories21.png", "position": 20 }, { "id": "accessory22", - "name": "accessory22", + "name": "Accessory 22", "url": "resources/customisation/character_accessories/character_accessories22.png", "position": 21 }, { "id": "accessory23", - "name": "accessory23", + "name": "Accessory 23", "url": "resources/customisation/character_accessories/character_accessories23.png", "position": 22 }, { "id": "accessory24", - "name": "accessory24", + "name": "Accessory 24", "url": "resources/customisation/character_accessories/character_accessories24.png", "position": 23 }, { "id": "accessory25", - "name": "accessory25", + "name": "Accessory 25", "url": "resources/customisation/character_accessories/character_accessories25.png", "position": 24 }, { "id": "accessory26", - "name": "accessory26", + "name": "Accessory 26", "url": "resources/customisation/character_accessories/character_accessories26.png", "position": 25 }, { "id": "accessory27", - "name": "accessory27", + "name": "Accessory 27", "url": "resources/customisation/character_accessories/character_accessories27.png", "position": 26 }, { "id": "accessory28", - "name": "accessory28", + "name": "Accessory 28", "url": "resources/customisation/character_accessories/character_accessories28.png", "position": 27 }, { "id": "accessory29", - "name": "accessory29", + "name": "Accessory 29", "url": "resources/customisation/character_accessories/character_accessories29.png", "position": 28 }, { "id": "accessory30", - "name": "accessory30", + "name": "Accessory 30", "url": "resources/customisation/character_accessories/character_accessories30.png", "position": 29 }, { "id": "accessory31", - "name": "accessory31", + "name": "Accessory 31", "url": "resources/customisation/character_accessories/character_accessories31.png", "position": 30 }, { "id": "accessory32", - "name": "accessory32", + "name": "Accessory 32", "url": "resources/customisation/character_accessories/character_accessories32.png", "position": 31 }, { "id": "accessory_mate_bottle", - "name": "accessory_mate_bottle", + "name": "Mate Bottle", "url": "resources/customisation/character_accessories/mate_bottle1.png", "position": 32 }, { "id": "accessory_mask", - "name": "accessory_mask", + "name": "Mask", "url": "resources/customisation/character_accessories/mask.png", "position": 33 } From 43bac6c5cf627d5f8e83570c2cc74e89952c216c Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Tue, 15 Mar 2022 11:31:01 +0100 Subject: [PATCH 34/61] get rid of zoom factor --- front/package.json | 1 + .../CustomizeWoka/CustomWokaPreviewer.ts | 83 +++++++++----- .../CustomizeWoka/WokaBodyPartSlot.ts | 96 ++++++++++++++++ front/src/Phaser/Login/CustomizeScene.ts | 107 ++++++++++++++++-- front/yarn.lock | 15 +++ 5 files changed, 266 insertions(+), 36 deletions(-) create mode 100644 front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts diff --git a/front/package.json b/front/package.json index 4a0ea649..ce3dfaa0 100644 --- a/front/package.json +++ b/front/package.json @@ -5,6 +5,7 @@ "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { "@geprog/vite-plugin-env-config": "^4.0.0", + "@home-based-studio/phaser3-utils": "0.3.0", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.36", "@tsconfig/svelte": "^1.0.10", "@types/google-protobuf": "^3.7.3", diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index f822c97e..50913e72 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -9,25 +9,49 @@ export enum CustomWokaBodyPart { Accessory = "Accessory", } +export interface CustomWokaPreviewerConfig { + width: number; + height: number; + color: number; + borderThickness: number; + borderColor: number; + bodyPartsScaleModifier: number; + bodyPartsOffsetX: number; +} + export class CustomWokaPreviewer extends Phaser.GameObjects.Container { private background: Phaser.GameObjects.Graphics; private sprites: Record; - private currentAnimationDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; - private currentlyMoving: boolean = true; + private animationDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; + private moving: boolean = true; - constructor(scene: Phaser.Scene, x: number, y: number) { + private config: CustomWokaPreviewerConfig; + + constructor(scene: Phaser.Scene, x: number, y: number, config: CustomWokaPreviewerConfig) { super(scene, x, y); - const spritesOffset = -2; + this.config = config; this.sprites = { - [CustomWokaBodyPart.Accessory]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), - [CustomWokaBodyPart.Body]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), - [CustomWokaBodyPart.Clothes]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), - [CustomWokaBodyPart.Eyes]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), - [CustomWokaBodyPart.Hair]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), - [CustomWokaBodyPart.Hat]: this.scene.add.sprite(spritesOffset, 0, "").setScale(4), + [CustomWokaBodyPart.Accessory]: this.scene.add + .sprite(this.config.bodyPartsOffsetX, 0, "") + .setScale(this.config.bodyPartsScaleModifier), + [CustomWokaBodyPart.Body]: this.scene.add + .sprite(this.config.bodyPartsOffsetX, 0, "") + .setScale(this.config.bodyPartsScaleModifier), + [CustomWokaBodyPart.Clothes]: this.scene.add + .sprite(this.config.bodyPartsOffsetX, 0, "") + .setScale(this.config.bodyPartsScaleModifier), + [CustomWokaBodyPart.Eyes]: this.scene.add + .sprite(this.config.bodyPartsOffsetX, 0, "") + .setScale(this.config.bodyPartsScaleModifier), + [CustomWokaBodyPart.Hair]: this.scene.add + .sprite(this.config.bodyPartsOffsetX, 0, "") + .setScale(this.config.bodyPartsScaleModifier), + [CustomWokaBodyPart.Hat]: this.scene.add + .sprite(this.config.bodyPartsOffsetX, 0, "") + .setScale(this.config.bodyPartsScaleModifier), }; this.updateSprite("accessory1", CustomWokaBodyPart.Accessory); @@ -37,11 +61,8 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.updateSprite("hair3", CustomWokaBodyPart.Hair); this.updateSprite("hat2", CustomWokaBodyPart.Hat); - const width = 150; - const height = 200; - - this.background = this.createBackground(width, height); - this.setSize(width, height); + this.background = this.createBackground(); + this.setSize(this.config.width, this.config.height); this.add([ this.background, @@ -61,14 +82,17 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { } public changeAnimation(direction: PlayerAnimationDirections, moving: boolean): void { - this.currentAnimationDirection = direction; - this.currentlyMoving = moving; + this.animationDirection = direction; + this.moving = moving; } - private createBackground(width: number, height: number): Phaser.GameObjects.Graphics { + private createBackground(): Phaser.GameObjects.Graphics { const background = this.scene.add.graphics(); background.fillStyle(0xffffff); - background.lineStyle(5, 0xadafbc); + background.lineStyle(this.config.borderThickness, 0xadafbc); + + const width = this.config.width; + const height = this.config.height; background.fillRect(-width / 2, -height / 2, width, height); background.strokeRect(-width / 2, -height / 2, width, height); @@ -85,15 +109,12 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { } const textureKey = sprite.texture.key; if ( - this.currentlyMoving && - (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== this.currentAnimationDirection) + this.moving && + (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== this.animationDirection) ) { - sprite.play(textureKey + "-" + this.currentAnimationDirection + "-" + PlayerAnimationTypes.Walk, true); - } else if (!this.currentlyMoving) { - sprite.anims.play( - textureKey + "-" + this.currentAnimationDirection + "-" + PlayerAnimationTypes.Idle, - true - ); + sprite.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Walk, true); + } else if (!this.moving) { + sprite.anims.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Idle, true); } } } @@ -113,4 +134,12 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.scene.sys.updateList.add(this.sprites[bodyPart]); } } + + public isMoving(): boolean { + return this.moving; + } + + public getAnimationDirection(): PlayerAnimationDirections { + return this.animationDirection; + } } diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts new file mode 100644 index 00000000..b772e524 --- /dev/null +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -0,0 +1,96 @@ +import { GridItem } from "@home-based-studio/phaser3-utils"; +import { GridItemEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/GridItem"; + +export interface WokaBodyPartSlotConfig { + width: number; + height: number; + color: number; + borderThickness: number; + borderColor: number; + borderSelectedColor: number; + offsetX: number; + offsetY: number; + bodyImageKey?: string; + imageKey?: string; + selected?: boolean; +} + +export class WokaBodyPartSlot extends GridItem { + private background: Phaser.GameObjects.Rectangle; + private bodyImage: Phaser.GameObjects.Image; + private image: Phaser.GameObjects.Image; + + private config: WokaBodyPartSlotConfig; + + private selected: boolean; + + constructor(scene: Phaser.Scene, x: number, y: number, config: WokaBodyPartSlotConfig) { + super(scene, undefined, { x, y }); + + this.config = config; + + const offsetY = -3; + const offsetX = -2; + this.selected = this.config.selected ?? false; + + this.background = this.scene.add + .rectangle(0, 0, this.config.width, this.config.height, this.config.color) + .setStrokeStyle(this.config.borderThickness, this.config.borderColor); + + this.bodyImage = this.scene.add + .image(offsetX, offsetY, config.bodyImageKey ?? `body${Math.floor(Math.random() * 33) + 1}`) + .setScale(2); + + this.image = this.scene.add + .image(offsetX, offsetY, config.imageKey ?? "") + .setScale(2) + .setVisible(config.imageKey !== undefined); + + this.setSize(this.config.width + this.config.borderThickness, this.config.height + this.config.borderThickness); + + this.add([this.background, this.bodyImage, this.image]); + + this.setInteractive(); + this.scene.input.setDraggable(this); + + this.bindEventHandlers(); + + this.scene.add.existing(this); + } + + public setBodyTexture(textureKey: string, frame?: string | number): void { + this.bodyImage.setTexture(textureKey, frame); + } + + public setImageTexture(textureKey?: string, frame?: string | number): void { + this.image.setVisible(textureKey !== undefined || textureKey !== ""); + if (textureKey) { + this.bodyImage.setTexture(textureKey, frame); + } + } + + public select(select: boolean = true): void { + if (this.selected === select) { + return; + } + this.selected = select; + this.updateSelected(); + } + + public isSelected(): boolean { + return this.selected; + } + + protected bindEventHandlers(): void { + super.bindEventHandlers(); + + this.on(GridItemEvent.Clicked, () => { + this.select(!this.selected); + // this.emit(CategoryGridItemEvent.Selected, this.categoryName); + }); + } + + private updateSelected(): void { + this.background.setStrokeStyle(2.5, this.selected ? this.config.borderSelectedColor : this.config.borderColor); + } +} diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 8f1e598d..0096d43a 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -1,7 +1,5 @@ import { EnableCameraSceneName } from "./EnableCameraScene"; -import Rectangle = Phaser.GameObjects.Rectangle; import { loadAllLayers } from "../Entity/PlayerTexturesLoadingManager"; -import Sprite = Phaser.GameObjects.Sprite; import { gameManager } from "../Game/GameManager"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { Loader } from "../Components/Loader"; @@ -17,13 +15,17 @@ import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; import { CustomWokaBodyPart, CustomWokaPreviewer } from "../Components/CustomizeWoka/CustomWokaPreviewer"; +import { DraggableGrid } from "@home-based-studio/phaser3-utils"; +import { WokaBodyPartSlot, WokaBodyPartSlotConfig } from "../Components/CustomizeWoka/WokaBodyPartSlot"; export const CustomizeSceneName = "CustomizeScene"; export class CustomizeScene extends AbstractCharacterScene { - private Rectangle!: Rectangle; + private Rectangle!: Phaser.GameObjects.Rectangle; - private customWokaPreviewer: CustomWokaPreviewer; + private customWokaPreviewer!: CustomWokaPreviewer; + private bodyPartsDraggableGrid!: DraggableGrid; + private bodyPartsSlots!: Record; private selectedLayers: number[] = [0]; private containersRow: CustomizedCharacter[][] = []; @@ -88,8 +90,7 @@ export class CustomizeScene extends AbstractCharacterScene { public create(): void { console.log(this.layers); - waScaleManager.saveZoom(); - waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1; + const isVertical = isMediaBreakpointUp("md"); this.Rectangle = this.add.rectangle( this.cameras.main.worldView.x + this.cameras.main.width / 2, @@ -116,7 +117,55 @@ export class CustomizeScene extends AbstractCharacterScene { this.updateSelectedLayer(); } - this.customWokaPreviewer = new CustomWokaPreviewer(this, 300, 300); + this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, { + width: 150, + height: 200, + color: 0xffffff, + borderThickness: 5, + borderColor: 0xadafbc, + bodyPartsScaleModifier: 4, + bodyPartsOffsetX: -2, + }); + this.bodyPartsDraggableGrid = new DraggableGrid(this, { + position: { x: 0, y: 0 }, + maskPosition: { x: 0, y: 0 }, + dimension: { x: 485, y: 165 }, + horizontal: true, + repositionToCenter: true, + itemsInRow: 2, + margin: { + left: 5, + right: 5, + }, + spacing: 5, + debug: { + showDraggableSpace: false, + }, + }); + + const defaultWokaBodyPartSlotConfig: WokaBodyPartSlotConfig = { + width: 72.5, + height: 72.5, + color: 0xffffff, + borderThickness: 2.5, + borderColor: 0xadafbc, + borderSelectedColor: 0x00ffff, + offsetX: -3, + offsetY: -2, + }; + + for (let i = 0; i < 50; i += 1) { + this.bodyPartsDraggableGrid.addItem(new WokaBodyPartSlot(this, 0, 0, defaultWokaBodyPartSlotConfig)); + } + + this.bodyPartsSlots = { + [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 220, 50, defaultWokaBodyPartSlotConfig), + [CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 220, 130, defaultWokaBodyPartSlotConfig), + [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 220, 210, defaultWokaBodyPartSlotConfig), + [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 520, 50, defaultWokaBodyPartSlotConfig), + [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 520, 130, defaultWokaBodyPartSlotConfig), + [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 520, 210, defaultWokaBodyPartSlotConfig), + }; this.onResize(); @@ -151,13 +200,16 @@ export class CustomizeScene extends AbstractCharacterScene { } public onResize(): void { + const isVertical = this.cameras.main.height > this.cameras.main.width; + console.log(`isVertical: ${isVertical}`); this.moveLayers(); this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3; - this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.customWokaPreviewer.y = this.cameras.main.worldView.y + this.cameras.main.height / 2; + this.repositionCustomWokaPreviewer(isVertical); + this.repositionBodyPartSlots(isVertical); + this.repositionBodyPartsDraggableGrid(isVertical); } public nextSceneToCamera() { @@ -187,6 +239,43 @@ export class CustomizeScene extends AbstractCharacterScene { this.scene.run(SelectCharacterSceneName); } + private repositionCustomWokaPreviewer(isVertical: boolean): void { + this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; + this.customWokaPreviewer.y = + this.cameras.main.worldView.y + + this.customWokaPreviewer.displayHeight * 0.5 + + this.cameras.main.height * 0.1; + } + + private repositionBodyPartSlots(isVertical: boolean): void { + const slotWidth = this.bodyPartsSlots.Accessory.displayWidth; + + const left = this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotWidth; + const right = this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotWidth; + const top = this.customWokaPreviewer.y - this.customWokaPreviewer.displayHeight * 0.5; + const middle = this.customWokaPreviewer.y; + const bottom = this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5; + + this.bodyPartsSlots.Hair.setPosition(left, top); + this.bodyPartsSlots.Body.setPosition(left, middle); + this.bodyPartsSlots.Accessory.setPosition(left, bottom); + this.bodyPartsSlots.Hat.setPosition(right, top); + this.bodyPartsSlots.Clothes.setPosition(right, middle); + this.bodyPartsSlots.Eyes.setPosition(right, bottom); + } + + private repositionBodyPartsDraggableGrid(isVertical: boolean): void { + const gridPos = { + x: this.cameras.main.worldView.x + this.cameras.main.width / 2, + y: + this.cameras.main.worldView.y + + this.cameras.main.height - + this.bodyPartsDraggableGrid.displayHeight * 0.5 - + this.cameras.main.height * 0.02, + }; + this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: 485, y: 165 }, gridPos); + } + private bindEventHandlers(): void { this.events.addListener("wake", () => { waScaleManager.saveZoom(); diff --git a/front/yarn.lock b/front/yarn.lock index 083881d5..b53a76e0 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -98,6 +98,13 @@ resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.0.tgz#989d95f23fbab5eae7c4c96d04a18abdc289b81e" integrity sha512-25ZMNdpssqkyv1sxfa6gBhmL8yCxCqjRRc1c05GJfhPkqD6Cn9dnG6xnHHHfJaEqrDFCViD0Bcnr+tgs76OZ2Q== +"@home-based-studio/phaser3-utils@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.3.0.tgz#e23337162252e9cb5db0dc0b4f3393e95369f254" + integrity sha512-p7FQKyLP2xjQsGxgQwAhlghLE3vdkSCLPSFvYohrNwglkUigbSJrpv7iq6oxK/0Uvmby8S6fU2Sv1BIbDysKoA== + dependencies: + phaser "3.55.1" + "@humanwhocodes/config-array@^0.9.2": version "0.9.2" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.2.tgz#68be55c737023009dfc5fe245d51181bb6476914" @@ -2218,6 +2225,14 @@ phaser3-rex-plugins@^1.1.42: papaparse "^5.3.0" webfontloader "^1.6.28" +phaser@3.55.1: + version "3.55.1" + resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.55.1.tgz#25923fe845f6598aec57cfb37a5641834e9943a7" + integrity sha512-A5J9/diRz05qc498UNJAaXp85JVkBAEMqxP8pmcRMu1RCLBs4Kx7axd7YxNbXnQuK58JBhTRucngLt8LSpsUlQ== + dependencies: + eventemitter3 "^4.0.7" + path "^0.12.7" + phaser@^3.54.0: version "3.54.0" resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.54.0.tgz#46b191e46059aab2a9a57f78525c60b595767eee" From fb020e22c7f632b802dc3243b167d0d81b045a2e Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 16 Mar 2022 13:25:39 +0100 Subject: [PATCH 35/61] entirely different approach to layout --- .../CustomizeWoka/CustomWokaPreviewer.ts | 51 ++--- .../CustomizeWoka/WokaBodyPartSlot.ts | 17 +- front/src/Phaser/Login/CustomizeScene.ts | 179 +++++++++++++----- front/src/Phaser/Services/WaScaleManager.ts | 4 + 4 files changed, 157 insertions(+), 94 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index 50913e72..f667ad27 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -10,12 +10,9 @@ export enum CustomWokaBodyPart { } export interface CustomWokaPreviewerConfig { - width: number; - height: number; color: number; borderThickness: number; borderColor: number; - bodyPartsScaleModifier: number; bodyPartsOffsetX: number; } @@ -28,30 +25,20 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { private config: CustomWokaPreviewerConfig; + public readonly SIZE: number = 50; + constructor(scene: Phaser.Scene, x: number, y: number, config: CustomWokaPreviewerConfig) { super(scene, x, y); this.config = config; this.sprites = { - [CustomWokaBodyPart.Accessory]: this.scene.add - .sprite(this.config.bodyPartsOffsetX, 0, "") - .setScale(this.config.bodyPartsScaleModifier), - [CustomWokaBodyPart.Body]: this.scene.add - .sprite(this.config.bodyPartsOffsetX, 0, "") - .setScale(this.config.bodyPartsScaleModifier), - [CustomWokaBodyPart.Clothes]: this.scene.add - .sprite(this.config.bodyPartsOffsetX, 0, "") - .setScale(this.config.bodyPartsScaleModifier), - [CustomWokaBodyPart.Eyes]: this.scene.add - .sprite(this.config.bodyPartsOffsetX, 0, "") - .setScale(this.config.bodyPartsScaleModifier), - [CustomWokaBodyPart.Hair]: this.scene.add - .sprite(this.config.bodyPartsOffsetX, 0, "") - .setScale(this.config.bodyPartsScaleModifier), - [CustomWokaBodyPart.Hat]: this.scene.add - .sprite(this.config.bodyPartsOffsetX, 0, "") - .setScale(this.config.bodyPartsScaleModifier), + [CustomWokaBodyPart.Accessory]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), + [CustomWokaBodyPart.Body]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), + [CustomWokaBodyPart.Clothes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), + [CustomWokaBodyPart.Eyes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), + [CustomWokaBodyPart.Hair]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), + [CustomWokaBodyPart.Hat]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), }; this.updateSprite("accessory1", CustomWokaBodyPart.Accessory); @@ -61,8 +48,9 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.updateSprite("hair3", CustomWokaBodyPart.Hair); this.updateSprite("hat2", CustomWokaBodyPart.Hat); - this.background = this.createBackground(); - this.setSize(this.config.width, this.config.height); + this.background = this.scene.add.graphics(); + this.drawBackground(); + this.setSize(this.SIZE, this.SIZE); this.add([ this.background, @@ -86,18 +74,13 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.moving = moving; } - private createBackground(): Phaser.GameObjects.Graphics { - const background = this.scene.add.graphics(); - background.fillStyle(0xffffff); - background.lineStyle(this.config.borderThickness, 0xadafbc); + private drawBackground(): void { + this.background.clear(); + this.background.fillStyle(0xffffff); + this.background.lineStyle(this.config.borderThickness, 0xadafbc); - const width = this.config.width; - const height = this.config.height; - - background.fillRect(-width / 2, -height / 2, width, height); - background.strokeRect(-width / 2, -height / 2, width, height); - - return background; + this.background.fillRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); + this.background.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); } private animate(): void { diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index b772e524..cc7b77e3 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -2,8 +2,6 @@ import { GridItem } from "@home-based-studio/phaser3-utils"; import { GridItemEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/GridItem"; export interface WokaBodyPartSlotConfig { - width: number; - height: number; color: number; borderThickness: number; borderColor: number; @@ -24,6 +22,8 @@ export class WokaBodyPartSlot extends GridItem { private selected: boolean; + public readonly SIZE: number = 50; + constructor(scene: Phaser.Scene, x: number, y: number, config: WokaBodyPartSlotConfig) { super(scene, undefined, { x, y }); @@ -34,19 +34,20 @@ export class WokaBodyPartSlot extends GridItem { this.selected = this.config.selected ?? false; this.background = this.scene.add - .rectangle(0, 0, this.config.width, this.config.height, this.config.color) + .rectangle(0, 0, this.SIZE, this.SIZE, this.config.color) .setStrokeStyle(this.config.borderThickness, this.config.borderColor); - this.bodyImage = this.scene.add - .image(offsetX, offsetY, config.bodyImageKey ?? `body${Math.floor(Math.random() * 33) + 1}`) - .setScale(2); + this.bodyImage = this.scene.add.image( + offsetX, + offsetY, + config.bodyImageKey ?? `body${Math.floor(Math.random() * 33) + 1}` + ); this.image = this.scene.add .image(offsetX, offsetY, config.imageKey ?? "") - .setScale(2) .setVisible(config.imageKey !== undefined); - this.setSize(this.config.width + this.config.borderThickness, this.config.height + this.config.borderThickness); + this.setSize(this.SIZE + this.config.borderThickness, this.SIZE + this.config.borderThickness); this.add([this.background, this.bodyImage, this.image]); diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 0096d43a..e9d6a4cd 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -14,7 +14,11 @@ import { get } from "svelte/store"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; -import { CustomWokaBodyPart, CustomWokaPreviewer } from "../Components/CustomizeWoka/CustomWokaPreviewer"; +import { + CustomWokaBodyPart, + CustomWokaPreviewer, + CustomWokaPreviewerConfig, +} from "../Components/CustomizeWoka/CustomWokaPreviewer"; import { DraggableGrid } from "@home-based-studio/phaser3-utils"; import { WokaBodyPartSlot, WokaBodyPartSlotConfig } from "../Components/CustomizeWoka/WokaBodyPartSlot"; @@ -117,15 +121,8 @@ export class CustomizeScene extends AbstractCharacterScene { this.updateSelectedLayer(); } - this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, { - width: 150, - height: 200, - color: 0xffffff, - borderThickness: 5, - borderColor: 0xadafbc, - bodyPartsScaleModifier: 4, - bodyPartsOffsetX: -2, - }); + this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, this.getCustomWokaPreviewerConfig()); + this.bodyPartsDraggableGrid = new DraggableGrid(this, { position: { x: 0, y: 0 }, maskPosition: { x: 0, y: 0 }, @@ -139,32 +136,53 @@ export class CustomizeScene extends AbstractCharacterScene { }, spacing: 5, debug: { - showDraggableSpace: false, + showDraggableSpace: true, }, }); - const defaultWokaBodyPartSlotConfig: WokaBodyPartSlotConfig = { - width: 72.5, - height: 72.5, - color: 0xffffff, - borderThickness: 2.5, - borderColor: 0xadafbc, - borderSelectedColor: 0x00ffff, - offsetX: -3, - offsetY: -2, - }; - for (let i = 0; i < 50; i += 1) { - this.bodyPartsDraggableGrid.addItem(new WokaBodyPartSlot(this, 0, 0, defaultWokaBodyPartSlotConfig)); + this.bodyPartsDraggableGrid.addItem( + new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(isVertical)) + ); } this.bodyPartsSlots = { - [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 220, 50, defaultWokaBodyPartSlotConfig), - [CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 220, 130, defaultWokaBodyPartSlotConfig), - [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 220, 210, defaultWokaBodyPartSlotConfig), - [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 520, 50, defaultWokaBodyPartSlotConfig), - [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 520, 130, defaultWokaBodyPartSlotConfig), - [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 520, 210, defaultWokaBodyPartSlotConfig), + [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot( + this, + 0, + 0, + this.getDefaultWokaBodyPartSlotConfig(isVertical) + ), + [CustomWokaBodyPart.Body]: new WokaBodyPartSlot( + this, + 0, + 0, + this.getDefaultWokaBodyPartSlotConfig(isVertical) + ), + [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot( + this, + 0, + 0, + this.getDefaultWokaBodyPartSlotConfig(isVertical) + ), + [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot( + this, + 0, + 0, + this.getDefaultWokaBodyPartSlotConfig(isVertical) + ), + [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot( + this, + 0, + 0, + this.getDefaultWokaBodyPartSlotConfig(isVertical) + ), + [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot( + this, + 0, + 0, + this.getDefaultWokaBodyPartSlotConfig(isVertical) + ), }; this.onResize(); @@ -201,15 +219,14 @@ export class CustomizeScene extends AbstractCharacterScene { public onResize(): void { const isVertical = this.cameras.main.height > this.cameras.main.width; - console.log(`isVertical: ${isVertical}`); this.moveLayers(); this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3; - this.repositionCustomWokaPreviewer(isVertical); - this.repositionBodyPartSlots(isVertical); - this.repositionBodyPartsDraggableGrid(isVertical); + this.handleCustomWokaPreviewerOnResize(isVertical); + this.handleBodyPartSlotsOnResize(isVertical); + this.handleBodyPartsDraggableGridOnResize(isVertical); } public nextSceneToCamera() { @@ -239,22 +256,47 @@ export class CustomizeScene extends AbstractCharacterScene { this.scene.run(SelectCharacterSceneName); } - private repositionCustomWokaPreviewer(isVertical: boolean): void { + private handleCustomWokaPreviewerOnResize(isVertical: boolean): void { + const boxDimension = Math.min(innerWidth * 0.3, innerHeight * 0.3) / waScaleManager.getActualZoom(); + const boxScale = boxDimension / this.customWokaPreviewer.SIZE; + + this.customWokaPreviewer.setScale(boxScale); this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.customWokaPreviewer.y = - this.cameras.main.worldView.y + - this.customWokaPreviewer.displayHeight * 0.5 + - this.cameras.main.height * 0.1; + this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 10; } - private repositionBodyPartSlots(isVertical: boolean): void { - const slotWidth = this.bodyPartsSlots.Accessory.displayWidth; + private handleBodyPartSlotsOnResize(isVertical: boolean): void { + const slotDimension = Math.min(innerWidth * 0.15, innerHeight * 0.15) / waScaleManager.getActualZoom(); + const slotScale = slotDimension / this.customWokaPreviewer.SIZE; - const left = this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotWidth; - const right = this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotWidth; - const top = this.customWokaPreviewer.y - this.customWokaPreviewer.displayHeight * 0.5; - const middle = this.customWokaPreviewer.y; - const bottom = this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5; + for (const part in this.bodyPartsSlots) { + this.bodyPartsSlots[part as CustomWokaBodyPart].setScale(slotScale); + } + + const slotSize = this.bodyPartsSlots.Accessory.displayHeight; + + if (isVertical) { + const left = this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5; + const right = this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5; + const middle = this.customWokaPreviewer.x; + const top = this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5; + const bottom = top + slotSize; + + this.bodyPartsSlots.Hair.setPosition(left, top); + this.bodyPartsSlots.Hat.setPosition(middle, top); + this.bodyPartsSlots.Eyes.setPosition(right, top); + this.bodyPartsSlots.Body.setPosition(left, bottom); + this.bodyPartsSlots.Clothes.setPosition(middle, bottom); + this.bodyPartsSlots.Accessory.setPosition(right, bottom); + + return; + } + + const left = this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize; + const right = this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize; + const top = 0 + slotSize * 0.5 + 10; + const middle = top + slotSize + 10; + const bottom = middle + slotSize + 10; this.bodyPartsSlots.Hair.setPosition(left, top); this.bodyPartsSlots.Body.setPosition(left, middle); @@ -264,16 +306,49 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsSlots.Eyes.setPosition(right, bottom); } - private repositionBodyPartsDraggableGrid(isVertical: boolean): void { + private handleBodyPartsDraggableGridOnResize(isVertical: boolean): void { + const gridHeight = (innerHeight * 0.35) / waScaleManager.getActualZoom(); + const gridWidth = (innerWidth * 0.7) / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, - y: - this.cameras.main.worldView.y + - this.cameras.main.height - - this.bodyPartsDraggableGrid.displayHeight * 0.5 - - this.cameras.main.height * 0.02, + y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5 - 10, }; - this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: 485, y: 165 }, gridPos); + + this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: gridWidth, y: gridHeight }, gridPos); + + const slotDimension = Math.min(innerWidth * 0.15, innerHeight * 0.15) / waScaleManager.getActualZoom(); + const slotScale = slotDimension / this.customWokaPreviewer.SIZE; + + this.bodyPartsDraggableGrid.clearAllItems(); + for (let i = 0; i < 50; i += 1) { + this.bodyPartsDraggableGrid.addItem( + new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(isVertical)).setScale(slotScale) + ); + } + } + + private getCustomWokaPreviewerConfig(): CustomWokaPreviewerConfig { + return { + color: 0xffffff, + borderThickness: 2.5, + borderColor: 0xadafbc, + bodyPartsOffsetX: -1, + }; + } + + private getDefaultWokaBodyPartSlotConfig(isVertical: boolean): WokaBodyPartSlotConfig { + return { + color: 0xffffff, + borderThickness: this.countZoom(isVertical ? 4 : 4), + borderColor: 0xadafbc, + borderSelectedColor: 0x00ffff, + offsetX: this.countZoom(isVertical ? -4 : -3), + offsetY: this.countZoom(isVertical ? -3 : -2), + }; + } + + private countZoom(value: number): number { + return Math.floor(value / waScaleManager.getActualZoom()); } private bindEventHandlers(): void { diff --git a/front/src/Phaser/Services/WaScaleManager.ts b/front/src/Phaser/Services/WaScaleManager.ts index 7958e79d..27e8f2ba 100644 --- a/front/src/Phaser/Services/WaScaleManager.ts +++ b/front/src/Phaser/Services/WaScaleManager.ts @@ -128,6 +128,10 @@ export class WaScaleManager { this.applyNewSize(); } + public getActualZoom(): number { + return this.actualZoom; + } + /** * This is used to scale back the ui components to counter-act the zoom. */ From ac48a429035440ec11219fa3d7e94a4116a1b0be Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 16 Mar 2022 14:05:35 +0100 Subject: [PATCH 36/61] further work on vertical layout --- front/package.json | 2 +- front/src/Phaser/Login/CustomizeScene.ts | 30 +++++++++++------------- front/yarn.lock | 8 +++---- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/front/package.json b/front/package.json index ce3dfaa0..7b824c7b 100644 --- a/front/package.json +++ b/front/package.json @@ -5,7 +5,7 @@ "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { "@geprog/vite-plugin-env-config": "^4.0.0", - "@home-based-studio/phaser3-utils": "0.3.0", + "@home-based-studio/phaser3-utils": "0.3.2", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.36", "@tsconfig/svelte": "^1.0.10", "@types/google-protobuf": "^3.7.3", diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index e9d6a4cd..7a131b2e 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -140,12 +140,6 @@ export class CustomizeScene extends AbstractCharacterScene { }, }); - for (let i = 0; i < 50; i += 1) { - this.bodyPartsDraggableGrid.addItem( - new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(isVertical)) - ); - } - this.bodyPartsSlots = { [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot( this, @@ -218,7 +212,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public onResize(): void { - const isVertical = this.cameras.main.height > this.cameras.main.width; + const isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; this.moveLayers(); this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; @@ -257,7 +251,9 @@ export class CustomizeScene extends AbstractCharacterScene { } private handleCustomWokaPreviewerOnResize(isVertical: boolean): void { - const boxDimension = Math.min(innerWidth * 0.3, innerHeight * 0.3) / waScaleManager.getActualZoom(); + const boxDimension = + Math.min(innerWidth * (isVertical ? 0.4 : 0.3), innerHeight * (isVertical ? 0.4 : 0.3)) / + waScaleManager.getActualZoom(); const boxScale = boxDimension / this.customWokaPreviewer.SIZE; this.customWokaPreviewer.setScale(boxScale); @@ -266,7 +262,9 @@ export class CustomizeScene extends AbstractCharacterScene { } private handleBodyPartSlotsOnResize(isVertical: boolean): void { - const slotDimension = Math.min(innerWidth * 0.15, innerHeight * 0.15) / waScaleManager.getActualZoom(); + const slotDimension = + Math.min(innerWidth * (isVertical ? 0.2 : 0.15), innerHeight * (isVertical ? 0.2 : 0.15)) / + waScaleManager.getActualZoom(); const slotScale = slotDimension / this.customWokaPreviewer.SIZE; for (const part in this.bodyPartsSlots) { @@ -276,11 +274,11 @@ export class CustomizeScene extends AbstractCharacterScene { const slotSize = this.bodyPartsSlots.Accessory.displayHeight; if (isVertical) { - const left = this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5; - const right = this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5; const middle = this.customWokaPreviewer.x; - const top = this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5; - const bottom = top + slotSize; + const left = middle - slotSize - 10; + const right = middle + slotSize + 10; + const top = this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 0.5 + 10; + const bottom = top + slotSize + 10; this.bodyPartsSlots.Hair.setPosition(left, top); this.bodyPartsSlots.Hat.setPosition(middle, top); @@ -307,8 +305,8 @@ export class CustomizeScene extends AbstractCharacterScene { } private handleBodyPartsDraggableGridOnResize(isVertical: boolean): void { - const gridHeight = (innerHeight * 0.35) / waScaleManager.getActualZoom(); - const gridWidth = (innerWidth * 0.7) / waScaleManager.getActualZoom(); + const gridHeight = (innerHeight * (isVertical ? 0.3 : 0.35)) / waScaleManager.getActualZoom(); + const gridWidth = (innerWidth * (isVertical ? 0.9 : 0.8)) / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5 - 10, @@ -316,7 +314,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: gridWidth, y: gridHeight }, gridPos); - const slotDimension = Math.min(innerWidth * 0.15, innerHeight * 0.15) / waScaleManager.getActualZoom(); + const slotDimension = (innerHeight * (isVertical ? 0.125 : 0.15)) / waScaleManager.getActualZoom(); const slotScale = slotDimension / this.customWokaPreviewer.SIZE; this.bodyPartsDraggableGrid.clearAllItems(); diff --git a/front/yarn.lock b/front/yarn.lock index b53a76e0..3be8b346 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -98,10 +98,10 @@ resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.0.tgz#989d95f23fbab5eae7c4c96d04a18abdc289b81e" integrity sha512-25ZMNdpssqkyv1sxfa6gBhmL8yCxCqjRRc1c05GJfhPkqD6Cn9dnG6xnHHHfJaEqrDFCViD0Bcnr+tgs76OZ2Q== -"@home-based-studio/phaser3-utils@0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.3.0.tgz#e23337162252e9cb5db0dc0b4f3393e95369f254" - integrity sha512-p7FQKyLP2xjQsGxgQwAhlghLE3vdkSCLPSFvYohrNwglkUigbSJrpv7iq6oxK/0Uvmby8S6fU2Sv1BIbDysKoA== +"@home-based-studio/phaser3-utils@0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.3.2.tgz#2240819473fbdb66123cee4b37b3d18be03d1dbe" + integrity sha512-qbv2H2IOSCyG+8hzBsZHPx4HWvlH1G4AfXuc3DyorI4QQMlvcVq5sZIjVaz6RIzeC7czivXOWsW5bApVhGa55A== dependencies: phaser "3.55.1" From a22504b354581fed0589a840a4244b91b424b933 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 16 Mar 2022 15:05:47 +0100 Subject: [PATCH 37/61] fix bug with grid not showing starting positions on resize --- front/src/Phaser/Login/CustomizeScene.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 7a131b2e..a2c7ea0b 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -323,6 +323,7 @@ export class CustomizeScene extends AbstractCharacterScene { new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(isVertical)).setScale(slotScale) ); } + this.bodyPartsDraggableGrid.moveContentToBeginning(); } private getCustomWokaPreviewerConfig(): CustomWokaPreviewerConfig { From 7703a766899da1c173c180171ecd9e16b2adfff8 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 17 Mar 2022 11:03:04 +0100 Subject: [PATCH 38/61] show body parts previews --- front/src/Components/App.svelte | 6 - .../CustomCharacterScene.svelte | 142 ----------- .../CustomizeWoka/CustomWokaPreviewer.ts | 100 +++++--- .../CustomizeWoka/WokaBodyPartSlot.ts | 26 +- front/src/Phaser/Login/CustomizeScene.ts | 237 ++++-------------- front/src/Stores/CustomCharacterStore.ts | 2 - 6 files changed, 119 insertions(+), 394 deletions(-) delete mode 100644 front/src/Components/CustomCharacterScene/CustomCharacterScene.svelte diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index e9fc0bfa..c596c8ee 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -1,14 +1,12 @@ - -
-
-

{$LL.woka.customWoka.title()}

-
-
- - -
-
- {#if $activeRowStore === 0} - - {/if} - {#if $activeRowStore !== 0} - - {/if} - {#if $activeRowStore === 5} - - {/if} - {#if $activeRowStore !== 5} - - {/if} -
-
- - diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index f667ad27..6030a03e 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -9,6 +9,15 @@ export enum CustomWokaBodyPart { Accessory = "Accessory", } +export enum CustomWokaBodyPartOrder { + Body = 0, + Eyes = 1, + Hair = 2, + Clothes = 3, + Hat = 4, + Accessory = 5, +} + export interface CustomWokaPreviewerConfig { color: number; borderThickness: number; @@ -33,20 +42,22 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.config = config; this.sprites = { - [CustomWokaBodyPart.Accessory]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), - [CustomWokaBodyPart.Body]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), - [CustomWokaBodyPart.Clothes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), - [CustomWokaBodyPart.Eyes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), - [CustomWokaBodyPart.Hair]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), - [CustomWokaBodyPart.Hat]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, ""), + [CustomWokaBodyPart.Accessory]: this.scene.add + .sprite(this.config.bodyPartsOffsetX, 0, "") + .setVisible(false), + [CustomWokaBodyPart.Body]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), + [CustomWokaBodyPart.Clothes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), + [CustomWokaBodyPart.Eyes]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), + [CustomWokaBodyPart.Hair]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), + [CustomWokaBodyPart.Hat]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), }; - this.updateSprite("accessory1", CustomWokaBodyPart.Accessory); - this.updateSprite("body1", CustomWokaBodyPart.Body); - this.updateSprite("clothes4", CustomWokaBodyPart.Clothes); - this.updateSprite("eyes5", CustomWokaBodyPart.Eyes); - this.updateSprite("hair3", CustomWokaBodyPart.Hair); - this.updateSprite("hat2", CustomWokaBodyPart.Hat); + // this.updateSprite("accessory1", CustomWokaBodyPart.Accessory); + // this.updateSprite("body1", CustomWokaBodyPart.Body); + // this.updateSprite("clothes4", CustomWokaBodyPart.Clothes); + // this.updateSprite("eyes5", CustomWokaBodyPart.Eyes); + // this.updateSprite("hair3", CustomWokaBodyPart.Hair); + // this.updateSprite("hat2", CustomWokaBodyPart.Hat); this.background = this.scene.add.graphics(); this.drawBackground(); @@ -74,36 +85,12 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.moving = moving; } - private drawBackground(): void { - this.background.clear(); - this.background.fillStyle(0xffffff); - this.background.lineStyle(this.config.borderThickness, 0xadafbc); - - this.background.fillRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); - this.background.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); - } - - private animate(): void { - for (const bodyPartKey in this.sprites) { - const sprite = this.sprites[bodyPartKey as CustomWokaBodyPart]; - if (!sprite.anims) { - console.error("ANIMS IS NOT DEFINED!!!"); - return; - } - const textureKey = sprite.texture.key; - if ( - this.moving && - (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== this.animationDirection) - ) { - sprite.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Walk, true); - } else if (!this.moving) { - sprite.anims.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Idle, true); - } - } - } - public updateSprite(textureKey: string, bodyPart: CustomWokaBodyPart): void { - this.sprites[bodyPart].setTexture(textureKey); + this.sprites[bodyPart].setTexture(textureKey).setVisible(textureKey !== ""); + console.log(this.sprites[bodyPart].texture.key); + if (textureKey === "") { + return; + } getPlayerAnimations(textureKey).forEach((d) => { this.scene.anims.create({ key: d.key, @@ -125,4 +112,35 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { public getAnimationDirection(): PlayerAnimationDirections { return this.animationDirection; } + + private drawBackground(): void { + this.background.clear(); + this.background.fillStyle(0xffffff); + this.background.lineStyle(this.config.borderThickness, 0xadafbc); + + this.background.fillRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); + this.background.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); + } + + private animate(): void { + for (const bodyPartKey in this.sprites) { + const sprite = this.sprites[bodyPartKey as CustomWokaBodyPart]; + if (!sprite.anims) { + console.error("ANIMS IS NOT DEFINED!!!"); + return; + } + const textureKey = sprite.texture.key; + if (textureKey === "__MISSING") { + continue; + } + if ( + this.moving && + (!sprite.anims.currentAnim || sprite.anims.currentAnim.key !== this.animationDirection) + ) { + sprite.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Walk, true); + } else if (!this.moving) { + sprite.anims.play(textureKey + "-" + this.animationDirection + "-" + PlayerAnimationTypes.Idle, true); + } + } + } } diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index cc7b77e3..6c37628d 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -37,15 +37,13 @@ export class WokaBodyPartSlot extends GridItem { .rectangle(0, 0, this.SIZE, this.SIZE, this.config.color) .setStrokeStyle(this.config.borderThickness, this.config.borderColor); - this.bodyImage = this.scene.add.image( - offsetX, - offsetY, - config.bodyImageKey ?? `body${Math.floor(Math.random() * 33) + 1}` - ); + this.bodyImage = this.scene.add + .image(offsetX, offsetY, config.bodyImageKey ?? "") + .setVisible(config.imageKey !== undefined); this.image = this.scene.add .image(offsetX, offsetY, config.imageKey ?? "") - .setVisible(config.imageKey !== undefined); + .setVisible(config.bodyImageKey !== undefined); this.setSize(this.SIZE + this.config.borderThickness, this.SIZE + this.config.borderThickness); @@ -59,14 +57,22 @@ export class WokaBodyPartSlot extends GridItem { this.scene.add.existing(this); } - public setBodyTexture(textureKey: string, frame?: string | number): void { - this.bodyImage.setTexture(textureKey, frame); + public setTextures(bodyTextureKey?: string, imageTextureKey?: string): void { + this.setBodyTexture(bodyTextureKey); + this.setImageTexture(imageTextureKey); + } + + public setBodyTexture(textureKey?: string, frame?: string | number): void { + this.bodyImage.setVisible(textureKey !== undefined && textureKey !== ""); + if (textureKey) { + this.bodyImage.setTexture(textureKey, frame); + } } public setImageTexture(textureKey?: string, frame?: string | number): void { - this.image.setVisible(textureKey !== undefined || textureKey !== ""); + this.image.setVisible(textureKey !== undefined && textureKey !== ""); if (textureKey) { - this.bodyImage.setTexture(textureKey, frame); + this.image.setTexture(textureKey, frame); } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index a2c7ea0b..615ad220 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -7,15 +7,14 @@ import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures" import { AbstractCharacterScene } from "./AbstractCharacterScene"; import { areCharacterLayersValid } from "../../Connexion/LocalUser"; import { SelectCharacterSceneName } from "./SelectCharacterScene"; -import { activeRowStore } from "../../Stores/CustomCharacterStore"; import { waScaleManager } from "../Services/WaScaleManager"; import { CustomizedCharacter } from "../Entity/CustomizedCharacter"; -import { get } from "svelte/store"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; import { CustomWokaBodyPart, + CustomWokaBodyPartOrder, CustomWokaPreviewer, CustomWokaPreviewerConfig, } from "../Components/CustomizeWoka/CustomWokaPreviewer"; @@ -25,21 +24,18 @@ import { WokaBodyPartSlot, WokaBodyPartSlotConfig } from "../Components/Customiz export const CustomizeSceneName = "CustomizeScene"; export class CustomizeScene extends AbstractCharacterScene { - private Rectangle!: Phaser.GameObjects.Rectangle; - private customWokaPreviewer!: CustomWokaPreviewer; + private bodyPartsDraggableGridBackground!: Phaser.GameObjects.Rectangle; + private bodyPartsDraggableGridForeground!: Phaser.GameObjects.Rectangle; private bodyPartsDraggableGrid!: DraggableGrid; private bodyPartsSlots!: Record; - private selectedLayers: number[] = [0]; + private selectedLayers: number[] = [0, 1, 2, 3, 4, 5]; private containersRow: CustomizedCharacter[][] = []; private layers: BodyResourceDescriptionInterface[][] = []; protected lazyloadingAttempt = true; //permit to update texture loaded after renderer - private moveHorizontally: number = 0; - private moveVertically: number = 0; - private loader: Loader; constructor() { @@ -96,33 +92,9 @@ export class CustomizeScene extends AbstractCharacterScene { const isVertical = isMediaBreakpointUp("md"); - this.Rectangle = this.add.rectangle( - this.cameras.main.worldView.x + this.cameras.main.width / 2, - this.cameras.main.worldView.y + this.cameras.main.height / 3, - 32, - 33 - ); - this.Rectangle.setStrokeStyle(2, 0xffffff); - - this.createCustomizeLayer(0, 0, 0); - this.createCustomizeLayer(0, 0, 1); - this.createCustomizeLayer(0, 0, 2); - this.createCustomizeLayer(0, 0, 3); - this.createCustomizeLayer(0, 0, 4); - this.createCustomizeLayer(0, 0, 5); - - this.moveLayers(); - - const customCursorPosition = localUserStore.getCustomCursorPosition(); - if (customCursorPosition) { - activeRowStore.set(customCursorPosition.activeRow); - this.selectedLayers = customCursorPosition.selectedLayers; - this.moveLayers(); - this.updateSelectedLayer(); - } - this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, this.getCustomWokaPreviewerConfig()); + this.bodyPartsDraggableGridBackground = this.add.rectangle(0, 0, 485, 165, 0xf9f9f9); this.bodyPartsDraggableGrid = new DraggableGrid(this, { position: { x: 0, y: 0 }, maskPosition: { x: 0, y: 0 }, @@ -136,9 +108,10 @@ export class CustomizeScene extends AbstractCharacterScene { }, spacing: 5, debug: { - showDraggableSpace: true, + showDraggableSpace: false, }, }); + this.bodyPartsDraggableGridForeground = this.add.rectangle(0, 0, 485, 165, 0xffffff, 0); this.bodyPartsSlots = { [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot( @@ -179,6 +152,8 @@ export class CustomizeScene extends AbstractCharacterScene { ), }; + this.setPlayerCurrentOutfit(); + this.onResize(); this.bindEventHandlers(); @@ -186,37 +161,10 @@ export class CustomizeScene extends AbstractCharacterScene { public update(time: number, dt: number): void { this.customWokaPreviewer.update(); - - if (this.lazyloadingAttempt) { - this.moveLayers(); - this.doMoveCursorHorizontally(this.moveHorizontally); - this.lazyloadingAttempt = false; - } - - if (this.moveHorizontally !== 0) { - this.doMoveCursorHorizontally(this.moveHorizontally); - this.moveHorizontally = 0; - } - if (this.moveVertically !== 0) { - this.doMoveCursorVertically(this.moveVertically); - this.moveVertically = 0; - } - } - - public moveCursorHorizontally(index: number): void { - this.moveHorizontally = index; - } - - public moveCursorVertically(index: number): void { - this.moveVertically = index; } public onResize(): void { const isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; - this.moveLayers(); - - this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3; this.handleCustomWokaPreviewerOnResize(isVertical); this.handleBodyPartSlotsOnResize(isVertical); @@ -250,25 +198,42 @@ export class CustomizeScene extends AbstractCharacterScene { this.scene.run(SelectCharacterSceneName); } + private setPlayerCurrentOutfit(): void { + let i = 0; + for (const layerItem of this.selectedLayers) { + const bodyPart = CustomWokaBodyPart[CustomWokaBodyPartOrder[i] as CustomWokaBodyPart]; + this.customWokaPreviewer.updateSprite(this.layers[i][layerItem].id, bodyPart); + this.bodyPartsSlots[bodyPart].setTextures( + this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id, + this.layers[i][layerItem].id + ); + i += 1; + } + } + private handleCustomWokaPreviewerOnResize(isVertical: boolean): void { + const slotDimension = + Math.min(innerWidth * (isVertical ? 0.2 : 0.15), innerHeight * (isVertical ? 0.2 : 0.15)) / + waScaleManager.getActualZoom(); + const boxDimension = Math.min(innerWidth * (isVertical ? 0.4 : 0.3), innerHeight * (isVertical ? 0.4 : 0.3)) / waScaleManager.getActualZoom(); - const boxScale = boxDimension / this.customWokaPreviewer.SIZE; - this.customWokaPreviewer.setScale(boxScale); + this.customWokaPreviewer.setDisplaySize(boxDimension, boxDimension); this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 10; + this.customWokaPreviewer.y = isVertical + ? this.customWokaPreviewer.displayHeight * 0.5 + 20 + : slotDimension * 1.5 + 20; } private handleBodyPartSlotsOnResize(isVertical: boolean): void { const slotDimension = Math.min(innerWidth * (isVertical ? 0.2 : 0.15), innerHeight * (isVertical ? 0.2 : 0.15)) / waScaleManager.getActualZoom(); - const slotScale = slotDimension / this.customWokaPreviewer.SIZE; for (const part in this.bodyPartsSlots) { - this.bodyPartsSlots[part as CustomWokaBodyPart].setScale(slotScale); + this.bodyPartsSlots[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension); } const slotSize = this.bodyPartsSlots.Accessory.displayHeight; @@ -306,23 +271,28 @@ export class CustomizeScene extends AbstractCharacterScene { private handleBodyPartsDraggableGridOnResize(isVertical: boolean): void { const gridHeight = (innerHeight * (isVertical ? 0.3 : 0.35)) / waScaleManager.getActualZoom(); - const gridWidth = (innerWidth * (isVertical ? 0.9 : 0.8)) / waScaleManager.getActualZoom(); + const gridWidth = (innerWidth * (isVertical ? 1 : 0.8)) / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, - y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5 - 10, + y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5, }; + this.bodyPartsDraggableGridBackground.setPosition(gridPos.x, gridPos.y).setDisplaySize(gridWidth, gridHeight); + this.bodyPartsDraggableGridForeground + .setPosition(gridPos.x, gridPos.y) + .setDisplaySize(gridWidth, gridHeight) + .setStrokeStyle(4, 0xaaaaaa); this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: gridWidth, y: gridHeight }, gridPos); const slotDimension = (innerHeight * (isVertical ? 0.125 : 0.15)) / waScaleManager.getActualZoom(); const slotScale = slotDimension / this.customWokaPreviewer.SIZE; - this.bodyPartsDraggableGrid.clearAllItems(); - for (let i = 0; i < 50; i += 1) { - this.bodyPartsDraggableGrid.addItem( - new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(isVertical)).setScale(slotScale) - ); - } + // this.bodyPartsDraggableGrid.clearAllItems(); + // for (let i = 0; i < 50; i += 1) { + // this.bodyPartsDraggableGrid.addItem( + // new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(isVertical)).setScale(slotScale) + // ); + // } this.bodyPartsDraggableGrid.moveContentToBeginning(); } @@ -363,14 +333,6 @@ export class CustomizeScene extends AbstractCharacterScene { this.backToPreviousScene(); }); - // Note: the key bindings are not directly put on the moveCursorVertically or moveCursorHorizontally methods - // because if 2 such events are fired close to one another, it makes the whole application crawl to a halt (for a reason I cannot - // explain, the list of sprites managed by the update list become immense - this.input.keyboard.on("keyup-RIGHT", () => (this.moveHorizontally = 1)); - this.input.keyboard.on("keyup-LEFT", () => (this.moveHorizontally = -1)); - this.input.keyboard.on("keyup-DOWN", () => (this.moveVertically = 1)); - this.input.keyboard.on("keyup-UP", () => (this.moveVertically = -1)); - this.input.keyboard.on("keydown-R", () => { this.randomizeOutfit(); }); @@ -402,115 +364,4 @@ export class CustomizeScene extends AbstractCharacterScene { CustomWokaBodyPart.Accessory ); } - - private doMoveCursorHorizontally(index: number): void { - this.selectedLayers[get(activeRowStore)] += index; - if (this.selectedLayers[get(activeRowStore)] < 0) { - this.selectedLayers[get(activeRowStore)] = 0; - } else if (this.selectedLayers[get(activeRowStore)] > this.layers[get(activeRowStore)].length - 1) { - this.selectedLayers[get(activeRowStore)] = this.layers[get(activeRowStore)].length - 1; - } - this.moveLayers(); - this.updateSelectedLayer(); - this.saveInLocalStorage(); - } - - private doMoveCursorVertically(index: number): void { - activeRowStore.set(get(activeRowStore) + index); - if (get(activeRowStore) < 0) { - activeRowStore.set(0); - } else if (get(activeRowStore) > this.layers.length - 1) { - activeRowStore.set(this.layers.length - 1); - } - this.moveLayers(); - this.saveInLocalStorage(); - } - - private saveInLocalStorage() { - localUserStore.setCustomCursorPosition(get(activeRowStore), this.selectedLayers); - } - - /** - * @param x, the layer's vertical position - * @param y, the layer's horizontal position - * @param layerNumber, index of the this.layers array - * create the layer and display it on the scene - */ - private createCustomizeLayer(x: number, y: number, layerNumber: number): void { - this.containersRow[layerNumber] = []; - this.selectedLayers[layerNumber] = 0; - let alpha = 0; - let layerPosX = 0; - for (let i = 0; i < this.layers[layerNumber].length; i++) { - const container = this.generateCharacter(300 + x + layerPosX, y, layerNumber, i); - - this.containersRow[layerNumber][i] = container; - this.add.existing(container); - layerPosX += 30; - alpha += 0.1; - } - } - - /** - * Generates a character from the current selected items BUT replaces - * one layer item with an item we pass in parameter. - * - * Current selected items are fetched from this.selectedLayers - * - * @param x, - * @param y, - * @param layerNumber, The selected layer number (0 for body...) - * @param selectedItem, The number of the item select (0 for black body...) - */ - private generateCharacter(x: number, y: number, layerNumber: number, selectedItem: number) { - return new CustomizedCharacter(this, x, y, this.getContainerChildren(layerNumber, selectedItem)); - } - - private getContainerChildren(layerNumber: number, selectedItem: number): Array { - const children: Array = new Array(); - for (let j = 0; j <= layerNumber; j++) { - if (j === layerNumber) { - children.push(this.layers[j][selectedItem].id); - } else { - const layer = this.selectedLayers[j]; - if (layer === undefined) { - continue; - } - children.push(this.layers[j][layer].id); - } - } - return children; - } - - /** - * Move the layer left, right, up and down and update the selected layer - */ - private moveLayers(): void { - const screenCenterX = this.cameras.main.worldView.x + this.cameras.main.width / 2; - const screenCenterY = this.cameras.main.worldView.y + this.cameras.main.height / 3; - const screenWidth = this.game.renderer.width; - const screenHeight = this.game.renderer.height; - for (let i = 0; i < this.containersRow.length; i++) { - for (let j = 0; j < this.containersRow[i].length; j++) { - let selectedX = this.selectedLayers[i]; - if (selectedX === undefined) { - selectedX = 0; - } - this.containersRow[i][j].x = screenCenterX + (j - selectedX) * 40; - this.containersRow[i][j].y = screenCenterY + (i - get(activeRowStore)) * 40; - const alpha1 = (Math.abs(selectedX - j) * 47 * 2) / screenWidth; - const alpha2 = (Math.abs(get(activeRowStore) - i) * 49 * 2) / screenHeight; - this.containersRow[i][j].setAlpha((1 - alpha1) * (1 - alpha2)); - } - } - } - - private updateSelectedLayer() { - for (let i = 0; i < this.containersRow.length; i++) { - for (let j = 0; j < this.containersRow[i].length; j++) { - const children = this.getContainerChildren(i, j); - this.containersRow[i][j].updateSprites(children); - } - } - } } diff --git a/front/src/Stores/CustomCharacterStore.ts b/front/src/Stores/CustomCharacterStore.ts index 2c7d75e8..0ade6ea8 100644 --- a/front/src/Stores/CustomCharacterStore.ts +++ b/front/src/Stores/CustomCharacterStore.ts @@ -1,5 +1,3 @@ import { derived, writable, Writable } from "svelte/store"; -export const customCharacterSceneVisibleStore = writable(false); - export const activeRowStore = writable(0); From 701c5f65cd97e565bf6b5d76ac067318c4282818 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 17 Mar 2022 11:58:12 +0100 Subject: [PATCH 39/61] independent parts preview --- .../CustomizeWoka/CustomWokaPreviewer.ts | 13 ++++---- front/src/Phaser/Login/CustomizeScene.ts | 33 +++++-------------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index 6030a03e..aa9b588d 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -10,12 +10,12 @@ export enum CustomWokaBodyPart { } export enum CustomWokaBodyPartOrder { - Body = 0, - Eyes = 1, - Hair = 2, - Clothes = 3, - Hat = 4, - Accessory = 5, + Body, + Eyes, + Hair, + Clothes, + Hat, + Accessory, } export interface CustomWokaPreviewerConfig { @@ -87,7 +87,6 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { public updateSprite(textureKey: string, bodyPart: CustomWokaBodyPart): void { this.sprites[bodyPart].setTexture(textureKey).setVisible(textureKey !== ""); - console.log(this.sprites[bodyPart].texture.key); if (textureKey === "") { return; } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 615ad220..a100fa50 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -46,6 +46,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public preload(): void { + this.input.dragDistanceThreshold = 10; const wokaMetadataKey = "woka-list"; this.cache.json.remove(wokaMetadataKey); // FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!) @@ -335,33 +336,17 @@ export class CustomizeScene extends AbstractCharacterScene { this.input.keyboard.on("keydown-R", () => { this.randomizeOutfit(); + this.setPlayerCurrentOutfit(); }); } private randomizeOutfit(): void { - this.customWokaPreviewer.updateSprite( - this.layers[0][Math.floor(Math.random() * this.layers[0].length)].id, - CustomWokaBodyPart.Body - ); - this.customWokaPreviewer.updateSprite( - this.layers[1][Math.floor(Math.random() * this.layers[1].length)].id, - CustomWokaBodyPart.Eyes - ); - this.customWokaPreviewer.updateSprite( - this.layers[2][Math.floor(Math.random() * this.layers[2].length)].id, - CustomWokaBodyPart.Hair - ); - this.customWokaPreviewer.updateSprite( - this.layers[3][Math.floor(Math.random() * this.layers[3].length)].id, - CustomWokaBodyPart.Clothes - ); - this.customWokaPreviewer.updateSprite( - this.layers[4][Math.floor(Math.random() * this.layers[4].length)].id, - CustomWokaBodyPart.Hat - ); - this.customWokaPreviewer.updateSprite( - this.layers[5][Math.floor(Math.random() * this.layers[5].length)].id, - CustomWokaBodyPart.Accessory - ); + for (let i = 0; i < 6; i += 1) { + this.selectedLayers[i] = Math.floor(Math.random() * this.layers[i].length); + this.customWokaPreviewer.updateSprite( + this.layers[i][Math.floor(Math.random() * this.layers[i].length)].id, + CustomWokaBodyPart[CustomWokaBodyPartOrder[i] as CustomWokaBodyPart] + ); + } } } From a3fcf2dc3f080afbf9129933d0bd1a5c24fc8c7e Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 17 Mar 2022 13:58:54 +0100 Subject: [PATCH 40/61] populating grid --- .../CustomizeWoka/CustomWokaPreviewer.ts | 7 - .../CustomizeWoka/WokaBodyPartSlot.ts | 7 +- front/src/Phaser/Login/CustomizeScene.ts | 150 ++++++++++-------- 3 files changed, 85 insertions(+), 79 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index aa9b588d..29c010bd 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -52,13 +52,6 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { [CustomWokaBodyPart.Hat]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), }; - // this.updateSprite("accessory1", CustomWokaBodyPart.Accessory); - // this.updateSprite("body1", CustomWokaBodyPart.Body); - // this.updateSprite("clothes4", CustomWokaBodyPart.Clothes); - // this.updateSprite("eyes5", CustomWokaBodyPart.Eyes); - // this.updateSprite("hair3", CustomWokaBodyPart.Hair); - // this.updateSprite("hat2", CustomWokaBodyPart.Hat); - this.background = this.scene.add.graphics(); this.drawBackground(); this.setSize(this.SIZE, this.SIZE); diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 6c37628d..833784dd 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -13,6 +13,10 @@ export interface WokaBodyPartSlotConfig { selected?: boolean; } +export enum WokaBodyPartSlotEvent { + Clicked = "WokaBodyPartSlotEvent:Clicked", +} + export class WokaBodyPartSlot extends GridItem { private background: Phaser.GameObjects.Rectangle; private bodyImage: Phaser.GameObjects.Image; @@ -92,8 +96,7 @@ export class WokaBodyPartSlot extends GridItem { super.bindEventHandlers(); this.on(GridItemEvent.Clicked, () => { - this.select(!this.selected); - // this.emit(CategoryGridItemEvent.Selected, this.categoryName); + this.emit(WokaBodyPartSlotEvent.Clicked, this.selected); }); } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index a100fa50..b437735f 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -19,7 +19,11 @@ import { CustomWokaPreviewerConfig, } from "../Components/CustomizeWoka/CustomWokaPreviewer"; import { DraggableGrid } from "@home-based-studio/phaser3-utils"; -import { WokaBodyPartSlot, WokaBodyPartSlotConfig } from "../Components/CustomizeWoka/WokaBodyPartSlot"; +import { + WokaBodyPartSlot, + WokaBodyPartSlotConfig, + WokaBodyPartSlotEvent, +} from "../Components/CustomizeWoka/WokaBodyPartSlot"; export const CustomizeSceneName = "CustomizeScene"; @@ -36,6 +40,8 @@ export class CustomizeScene extends AbstractCharacterScene { protected lazyloadingAttempt = true; //permit to update texture loaded after renderer + private isVertical: boolean = false; + private loader: Loader; constructor() { @@ -89,10 +95,9 @@ export class CustomizeScene extends AbstractCharacterScene { } public create(): void { + this.isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; console.log(this.layers); - const isVertical = isMediaBreakpointUp("md"); - this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, this.getCustomWokaPreviewerConfig()); this.bodyPartsDraggableGridBackground = this.add.rectangle(0, 0, 485, 165, 0xf9f9f9); @@ -115,42 +120,12 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGridForeground = this.add.rectangle(0, 0, 485, 165, 0xffffff, 0); this.bodyPartsSlots = { - [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot( - this, - 0, - 0, - this.getDefaultWokaBodyPartSlotConfig(isVertical) - ), - [CustomWokaBodyPart.Body]: new WokaBodyPartSlot( - this, - 0, - 0, - this.getDefaultWokaBodyPartSlotConfig(isVertical) - ), - [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot( - this, - 0, - 0, - this.getDefaultWokaBodyPartSlotConfig(isVertical) - ), - [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot( - this, - 0, - 0, - this.getDefaultWokaBodyPartSlotConfig(isVertical) - ), - [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot( - this, - 0, - 0, - this.getDefaultWokaBodyPartSlotConfig(isVertical) - ), - [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot( - this, - 0, - 0, - this.getDefaultWokaBodyPartSlotConfig(isVertical) - ), + [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), + [CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), + [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), + [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), + [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), + [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), }; this.setPlayerCurrentOutfit(); @@ -165,11 +140,11 @@ export class CustomizeScene extends AbstractCharacterScene { } public onResize(): void { - const isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; + this.isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; - this.handleCustomWokaPreviewerOnResize(isVertical); - this.handleBodyPartSlotsOnResize(isVertical); - this.handleBodyPartsDraggableGridOnResize(isVertical); + this.handleCustomWokaPreviewerOnResize(); + this.handleBodyPartSlotsOnResize(); + this.handleBodyPartsDraggableGridOnResize(); } public nextSceneToCamera() { @@ -212,25 +187,25 @@ export class CustomizeScene extends AbstractCharacterScene { } } - private handleCustomWokaPreviewerOnResize(isVertical: boolean): void { + private handleCustomWokaPreviewerOnResize(): void { const slotDimension = - Math.min(innerWidth * (isVertical ? 0.2 : 0.15), innerHeight * (isVertical ? 0.2 : 0.15)) / + Math.min(innerWidth * (this.isVertical ? 0.2 : 0.15), innerHeight * (this.isVertical ? 0.2 : 0.15)) / waScaleManager.getActualZoom(); const boxDimension = - Math.min(innerWidth * (isVertical ? 0.4 : 0.3), innerHeight * (isVertical ? 0.4 : 0.3)) / + Math.min(innerWidth * (this.isVertical ? 0.4 : 0.3), innerHeight * (this.isVertical ? 0.4 : 0.3)) / waScaleManager.getActualZoom(); this.customWokaPreviewer.setDisplaySize(boxDimension, boxDimension); this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.customWokaPreviewer.y = isVertical + this.customWokaPreviewer.y = this.isVertical ? this.customWokaPreviewer.displayHeight * 0.5 + 20 : slotDimension * 1.5 + 20; } - private handleBodyPartSlotsOnResize(isVertical: boolean): void { + private handleBodyPartSlotsOnResize(): void { const slotDimension = - Math.min(innerWidth * (isVertical ? 0.2 : 0.15), innerHeight * (isVertical ? 0.2 : 0.15)) / + Math.min(innerWidth * (this.isVertical ? 0.2 : 0.15), innerHeight * (this.isVertical ? 0.2 : 0.15)) / waScaleManager.getActualZoom(); for (const part in this.bodyPartsSlots) { @@ -239,7 +214,7 @@ export class CustomizeScene extends AbstractCharacterScene { const slotSize = this.bodyPartsSlots.Accessory.displayHeight; - if (isVertical) { + if (this.isVertical) { const middle = this.customWokaPreviewer.x; const left = middle - slotSize - 10; const right = middle + slotSize + 10; @@ -270,9 +245,9 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsSlots.Eyes.setPosition(right, bottom); } - private handleBodyPartsDraggableGridOnResize(isVertical: boolean): void { - const gridHeight = (innerHeight * (isVertical ? 0.3 : 0.35)) / waScaleManager.getActualZoom(); - const gridWidth = (innerWidth * (isVertical ? 1 : 0.8)) / waScaleManager.getActualZoom(); + private handleBodyPartsDraggableGridOnResize(): void { + const gridHeight = (innerHeight * (this.isVertical ? 0.3 : 0.35)) / waScaleManager.getActualZoom(); + const gridWidth = (innerWidth * (this.isVertical ? 1 : 0.8)) / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5, @@ -285,15 +260,6 @@ export class CustomizeScene extends AbstractCharacterScene { .setStrokeStyle(4, 0xaaaaaa); this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: gridWidth, y: gridHeight }, gridPos); - const slotDimension = (innerHeight * (isVertical ? 0.125 : 0.15)) / waScaleManager.getActualZoom(); - const slotScale = slotDimension / this.customWokaPreviewer.SIZE; - - // this.bodyPartsDraggableGrid.clearAllItems(); - // for (let i = 0; i < 50; i += 1) { - // this.bodyPartsDraggableGrid.addItem( - // new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(isVertical)).setScale(slotScale) - // ); - // } this.bodyPartsDraggableGrid.moveContentToBeginning(); } @@ -306,14 +272,14 @@ export class CustomizeScene extends AbstractCharacterScene { }; } - private getDefaultWokaBodyPartSlotConfig(isVertical: boolean): WokaBodyPartSlotConfig { + private getDefaultWokaBodyPartSlotConfig(): WokaBodyPartSlotConfig { return { color: 0xffffff, - borderThickness: this.countZoom(isVertical ? 4 : 4), + borderThickness: this.countZoom(this.isVertical ? 4 : 4), borderColor: 0xadafbc, borderSelectedColor: 0x00ffff, - offsetX: this.countZoom(isVertical ? -4 : -3), - offsetY: this.countZoom(isVertical ? -3 : -2), + offsetX: this.countZoom(this.isVertical ? -4 : -3), + offsetY: this.countZoom(this.isVertical ? -3 : -2), }; } @@ -336,17 +302,61 @@ export class CustomizeScene extends AbstractCharacterScene { this.input.keyboard.on("keydown-R", () => { this.randomizeOutfit(); + this.clearGrid(); + this.deselectAllSlots(); this.setPlayerCurrentOutfit(); }); + + for (const bodyPart in CustomWokaBodyPart) { + const slot = this.bodyPartsSlots[bodyPart as CustomWokaBodyPart]; + slot.on(WokaBodyPartSlotEvent.Clicked, (selected: boolean) => { + if (!selected) { + this.deselectAllSlots(); + slot.select(true); + this.populateGrid(bodyPart as CustomWokaBodyPart); + } else { + slot.select(false); + this.clearGrid(); + } + }); + } } private randomizeOutfit(): void { for (let i = 0; i < 6; i += 1) { this.selectedLayers[i] = Math.floor(Math.random() * this.layers[i].length); - this.customWokaPreviewer.updateSprite( - this.layers[i][Math.floor(Math.random() * this.layers[i].length)].id, - CustomWokaBodyPart[CustomWokaBodyPartOrder[i] as CustomWokaBodyPart] - ); + } + } + + private populateGrid(bodyParts: CustomWokaBodyPart): void { + const slotDimension = (innerHeight * (this.isVertical ? 0.125 : 0.15)) / waScaleManager.getActualZoom(); + const slotScale = slotDimension / this.customWokaPreviewer.SIZE; + + const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[bodyParts]]; + + this.bodyPartsDraggableGrid.clearAllItems(); + for (let i = 0; i < bodyPartsLayer.length; i += 1) { + const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()).setScale(slotScale); + if (bodyParts === CustomWokaBodyPart.Body) { + slot.setBodyTexture(bodyPartsLayer[i].id); + slot.setImageTexture(); + } else { + slot.setBodyTexture( + this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id + ); + slot.setImageTexture(bodyPartsLayer[i].id); + } + this.bodyPartsDraggableGrid.addItem(slot); + } + } + + private clearGrid(): void { + this.bodyPartsDraggableGrid.clearAllItems(); + } + + private deselectAllSlots(): void { + for (const bodyPart in CustomWokaBodyPart) { + this.bodyPartsSlots[bodyPart as CustomWokaBodyPart].select(false); } } } From 744c15920b5d7b591f775470b7684da5ba784e72 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 17 Mar 2022 15:49:05 +0100 Subject: [PATCH 41/61] fixed bug with animation not showing up to date state of the outfit --- .../CustomizeWoka/CustomWokaPreviewer.ts | 1 + .../CustomizeWoka/WokaBodyPartSlot.ts | 4 +-- front/src/Phaser/Login/CustomizeScene.ts | 30 +++++++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index 29c010bd..a015082c 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -79,6 +79,7 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { } public updateSprite(textureKey: string, bodyPart: CustomWokaBodyPart): void { + this.sprites[bodyPart].anims.stop(); this.sprites[bodyPart].setTexture(textureKey).setVisible(textureKey !== ""); if (textureKey === "") { return; diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 833784dd..e2bad69c 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -28,8 +28,8 @@ export class WokaBodyPartSlot extends GridItem { public readonly SIZE: number = 50; - constructor(scene: Phaser.Scene, x: number, y: number, config: WokaBodyPartSlotConfig) { - super(scene, undefined, { x, y }); + constructor(scene: Phaser.Scene, x: number, y: number, config: WokaBodyPartSlotConfig, id?: number) { + super(scene, `${id}`, { x, y }); this.config = config; diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index b437735f..e4813119 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -24,6 +24,7 @@ import { WokaBodyPartSlotConfig, WokaBodyPartSlotEvent, } from "../Components/CustomizeWoka/WokaBodyPartSlot"; +import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid"; export const CustomizeSceneName = "CustomizeScene"; @@ -37,6 +38,7 @@ export class CustomizeScene extends AbstractCharacterScene { private selectedLayers: number[] = [0, 1, 2, 3, 4, 5]; private containersRow: CustomizedCharacter[][] = []; private layers: BodyResourceDescriptionInterface[][] = []; + private selectedBodyPartType?: CustomWokaBodyPart; protected lazyloadingAttempt = true; //permit to update texture loaded after renderer @@ -96,7 +98,6 @@ export class CustomizeScene extends AbstractCharacterScene { public create(): void { this.isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; - console.log(this.layers); this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, this.getCustomWokaPreviewerConfig()); @@ -128,7 +129,7 @@ export class CustomizeScene extends AbstractCharacterScene { [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), }; - this.setPlayerCurrentOutfit(); + this.refreshPlayerCurrentOutfit(); this.onResize(); @@ -174,7 +175,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.scene.run(SelectCharacterSceneName); } - private setPlayerCurrentOutfit(): void { + private refreshPlayerCurrentOutfit(): void { let i = 0; for (const layerItem of this.selectedLayers) { const bodyPart = CustomWokaBodyPart[CustomWokaBodyPartOrder[i] as CustomWokaBodyPart]; @@ -304,22 +305,31 @@ export class CustomizeScene extends AbstractCharacterScene { this.randomizeOutfit(); this.clearGrid(); this.deselectAllSlots(); - this.setPlayerCurrentOutfit(); + this.refreshPlayerCurrentOutfit(); }); for (const bodyPart in CustomWokaBodyPart) { const slot = this.bodyPartsSlots[bodyPart as CustomWokaBodyPart]; slot.on(WokaBodyPartSlotEvent.Clicked, (selected: boolean) => { if (!selected) { + this.selectedBodyPartType = bodyPart as CustomWokaBodyPart; this.deselectAllSlots(); slot.select(true); this.populateGrid(bodyPart as CustomWokaBodyPart); } else { + this.selectedBodyPartType = undefined; slot.select(false); this.clearGrid(); } }); } + + this.bodyPartsDraggableGrid.on(DraggableGridEvent.ItemClicked, (item: WokaBodyPartSlot) => { + this.bodyPartsDraggableGrid.getAllItems().forEach((slot) => (slot as WokaBodyPartSlot).select(false)); + this.changeOutfitPart(Number(item.getId())); + this.refreshPlayerCurrentOutfit(); + item.select(true); + }); } private randomizeOutfit(): void { @@ -328,6 +338,13 @@ export class CustomizeScene extends AbstractCharacterScene { } } + private changeOutfitPart(index: number): void { + if (this.selectedBodyPartType === undefined) { + return; + } + this.selectedLayers[CustomWokaBodyPartOrder[this.selectedBodyPartType]] = index; + } + private populateGrid(bodyParts: CustomWokaBodyPart): void { const slotDimension = (innerHeight * (this.isVertical ? 0.125 : 0.15)) / waScaleManager.getActualZoom(); const slotScale = slotDimension / this.customWokaPreviewer.SIZE; @@ -336,7 +353,9 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGrid.clearAllItems(); for (let i = 0; i < bodyPartsLayer.length; i += 1) { - const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()).setScale(slotScale); + const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(), i).setScale( + slotScale + ); if (bodyParts === CustomWokaBodyPart.Body) { slot.setBodyTexture(bodyPartsLayer[i].id); slot.setImageTexture(); @@ -348,6 +367,7 @@ export class CustomizeScene extends AbstractCharacterScene { } this.bodyPartsDraggableGrid.addItem(slot); } + this.bodyPartsDraggableGrid.moveContentToBeginning(); } private clearGrid(): void { From 9a7e5cae4c61f90c365eaafd9fb8fe83ef752f04 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 17 Mar 2022 16:48:40 +0100 Subject: [PATCH 42/61] populate grid on resize --- front/src/Phaser/Login/CustomizeScene.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index e4813119..30f4e86f 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -261,6 +261,7 @@ export class CustomizeScene extends AbstractCharacterScene { .setStrokeStyle(4, 0xaaaaaa); this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: gridWidth, y: gridHeight }, gridPos); + this.populateGrid(); this.bodyPartsDraggableGrid.moveContentToBeginning(); } @@ -315,7 +316,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.selectedBodyPartType = bodyPart as CustomWokaBodyPart; this.deselectAllSlots(); slot.select(true); - this.populateGrid(bodyPart as CustomWokaBodyPart); + this.populateGrid(); } else { this.selectedBodyPartType = undefined; slot.select(false); @@ -345,18 +346,21 @@ export class CustomizeScene extends AbstractCharacterScene { this.selectedLayers[CustomWokaBodyPartOrder[this.selectedBodyPartType]] = index; } - private populateGrid(bodyParts: CustomWokaBodyPart): void { + private populateGrid(): void { + if (this.selectedBodyPartType === undefined) { + return; + } const slotDimension = (innerHeight * (this.isVertical ? 0.125 : 0.15)) / waScaleManager.getActualZoom(); const slotScale = slotDimension / this.customWokaPreviewer.SIZE; - const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[bodyParts]]; + const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[this.selectedBodyPartType]]; this.bodyPartsDraggableGrid.clearAllItems(); for (let i = 0; i < bodyPartsLayer.length; i += 1) { const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(), i).setScale( slotScale ); - if (bodyParts === CustomWokaBodyPart.Body) { + if (this.selectedBodyPartType === CustomWokaBodyPart.Body) { slot.setBodyTexture(bodyPartsLayer[i].id); slot.setImageTexture(); } else { From 159bf0351feed2d092d9842bb973d0035d9cf07b Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Mon, 21 Mar 2022 12:38:12 +0100 Subject: [PATCH 43/61] fighting with not cripsy pixels when resizing --- .../CustomizeWoka/CustomWokaPreviewer.ts | 6 +++ .../CustomizeWoka/WokaBodyPartSlot.ts | 6 +++ front/src/Phaser/Login/CustomizeScene.ts | 10 +++-- front/src/Utils/MathUtils.ts | 39 +++++++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index a015082c..570e07eb 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -1,3 +1,4 @@ +import { MathUtils } from "../../../Utils/MathUtils"; import { getPlayerAnimations, PlayerAnimationDirections, PlayerAnimationTypes } from "../../Player/Animation"; export enum CustomWokaBodyPart { @@ -73,6 +74,11 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.animate(); } + public setDisplaySize(width: number, height: number): this { + const [newWidth, newHeight] = MathUtils.getWholePixelsNewSize(this.SIZE, this.SIZE, width, height); + return super.setDisplaySize(newWidth, newHeight); + } + public changeAnimation(direction: PlayerAnimationDirections, moving: boolean): void { this.animationDirection = direction; this.moving = moving; diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index e2bad69c..5aceb34d 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -1,5 +1,6 @@ import { GridItem } from "@home-based-studio/phaser3-utils"; import { GridItemEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/GridItem"; +import { MathUtils } from "../../../Utils/MathUtils"; export interface WokaBodyPartSlotConfig { color: number; @@ -61,6 +62,11 @@ export class WokaBodyPartSlot extends GridItem { this.scene.add.existing(this); } + public setDisplaySize(width: number, height: number): this { + const [newWidth, newHeight] = MathUtils.getWholePixelsNewSize(this.SIZE, this.SIZE, width, height, 32, 32); + return super.setDisplaySize(newWidth, newHeight); + } + public setTextures(bodyTextureKey?: string, imageTextureKey?: string): void { this.setBodyTexture(bodyTextureKey); this.setImageTexture(imageTextureKey); diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 30f4e86f..ab65a619 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -190,11 +190,11 @@ export class CustomizeScene extends AbstractCharacterScene { private handleCustomWokaPreviewerOnResize(): void { const slotDimension = - Math.min(innerWidth * (this.isVertical ? 0.2 : 0.15), innerHeight * (this.isVertical ? 0.2 : 0.15)) / + Math.min(innerWidth * (this.isVertical ? 0.2 : 0.2), innerHeight * (this.isVertical ? 0.2 : 0.2)) / waScaleManager.getActualZoom(); const boxDimension = - Math.min(innerWidth * (this.isVertical ? 0.4 : 0.3), innerHeight * (this.isVertical ? 0.4 : 0.3)) / + Math.min(innerWidth * (this.isVertical ? 0.4 : 0.5), innerHeight * (this.isVertical ? 0.4 : 0.5)) / waScaleManager.getActualZoom(); this.customWokaPreviewer.setDisplaySize(boxDimension, boxDimension); @@ -206,12 +206,16 @@ export class CustomizeScene extends AbstractCharacterScene { private handleBodyPartSlotsOnResize(): void { const slotDimension = - Math.min(innerWidth * (this.isVertical ? 0.2 : 0.15), innerHeight * (this.isVertical ? 0.2 : 0.15)) / + Math.min(innerWidth * (this.isVertical ? 0.2 : 0.25), innerHeight * (this.isVertical ? 0.2 : 0.25)) / waScaleManager.getActualZoom(); + // 1; + console.log("zoom: ", waScaleManager.getActualZoom()); + console.log("slotDimension: ", slotDimension); for (const part in this.bodyPartsSlots) { this.bodyPartsSlots[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension); } + console.log(this.bodyPartsSlots.Body.displayWidth); const slotSize = this.bodyPartsSlots.Accessory.displayHeight; diff --git a/front/src/Utils/MathUtils.ts b/front/src/Utils/MathUtils.ts index fc055d11..ebe8fb34 100644 --- a/front/src/Utils/MathUtils.ts +++ b/front/src/Utils/MathUtils.ts @@ -35,4 +35,43 @@ export class MathUtils { public static randomFromArray(array: T[]): T { return array[Math.floor(Math.random() * array.length)]; } + + /** + * + * @param baseWidth Object's default width not affected by any scaling + * @param baseHeight Object's default height not affected by any scaling + * @param requestedWidth Width we would like to achieve + * @param requestedHeight Height we would like to achieve + * @param unitSizeWidth Smallest possible unit of our 'scale step' for width + * @param unitSizeHeight Smallest possible unit of our 'scale step' for height + * @returns [ newWidth, newHeight ] + */ + public static getWholePixelsNewSize( + baseWidth: number, + baseHeight: number, + requestedWidth: number, + requestedHeight: number, + unitSizeWidth: number = 32, + unitSizeHeight: number = 32 + ): [number, number] { + // Demanded scale to be applied + const newScaleW = requestedWidth / baseWidth; + const newScaleH = requestedHeight / baseHeight; + + // How would it affect our sprites + const spriteWidth = Math.floor(unitSizeWidth * newScaleW); + const spriteHeight = Math.floor(unitSizeHeight * newScaleH); + + // Expected nearest sprite size to maintain crisp pixels + const expectedSpriteWidth = spriteWidth - (spriteWidth % unitSizeWidth); + const expectedSpriteHeight = spriteHeight - (spriteHeight % unitSizeHeight); + + // Expected nearest scale + const neededScaleWidth = expectedSpriteWidth / unitSizeWidth; + const neededScaleHeight = expectedSpriteHeight / unitSizeHeight; + console.log(neededScaleWidth, neededScaleHeight); + + // Calculate new width and height and apply it to the whole container + return [baseWidth * neededScaleWidth, baseHeight * neededScaleHeight]; + } } From a340c9bd969209e0d30faa71827840ed55d455bf Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Mon, 21 Mar 2022 14:53:37 +0100 Subject: [PATCH 44/61] simplification of resizing --- front/src/Phaser/Login/CustomizeScene.ts | 32 +++++++++++------------- front/src/Utils/MathUtils.ts | 8 +++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index ab65a619..4cead922 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -97,7 +97,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public create(): void { - this.isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; + this.isVertical = innerHeight / innerWidth > 1; this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, this.getCustomWokaPreviewerConfig()); @@ -108,7 +108,7 @@ export class CustomizeScene extends AbstractCharacterScene { dimension: { x: 485, y: 165 }, horizontal: true, repositionToCenter: true, - itemsInRow: 2, + itemsInRow: 1, margin: { left: 5, right: 5, @@ -141,7 +141,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public onResize(): void { - this.isVertical = this.cameras.main.width / this.cameras.main.height < 0.75; + this.isVertical = innerHeight / innerWidth > 1; this.handleCustomWokaPreviewerOnResize(); this.handleBodyPartSlotsOnResize(); @@ -205,17 +205,11 @@ export class CustomizeScene extends AbstractCharacterScene { } private handleBodyPartSlotsOnResize(): void { - const slotDimension = - Math.min(innerWidth * (this.isVertical ? 0.2 : 0.25), innerHeight * (this.isVertical ? 0.2 : 0.25)) / - waScaleManager.getActualZoom(); - // 1; + const slotDimension = 100; - console.log("zoom: ", waScaleManager.getActualZoom()); - console.log("slotDimension: ", slotDimension); for (const part in this.bodyPartsSlots) { this.bodyPartsSlots[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension); } - console.log(this.bodyPartsSlots.Body.displayWidth); const slotSize = this.bodyPartsSlots.Accessory.displayHeight; @@ -236,22 +230,26 @@ export class CustomizeScene extends AbstractCharacterScene { return; } + const ratio = innerHeight / innerWidth; + const left = this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize; + const leftEdge = left - slotSize - 10; const right = this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize; + const rightEdge = right + slotSize + 10; const top = 0 + slotSize * 0.5 + 10; const middle = top + slotSize + 10; const bottom = middle + slotSize + 10; this.bodyPartsSlots.Hair.setPosition(left, top); this.bodyPartsSlots.Body.setPosition(left, middle); - this.bodyPartsSlots.Accessory.setPosition(left, bottom); + this.bodyPartsSlots.Accessory.setPosition(ratio < 0.6 ? leftEdge : left, ratio < 0.6 ? middle : bottom); this.bodyPartsSlots.Hat.setPosition(right, top); this.bodyPartsSlots.Clothes.setPosition(right, middle); - this.bodyPartsSlots.Eyes.setPosition(right, bottom); + this.bodyPartsSlots.Eyes.setPosition(ratio < 0.6 ? rightEdge : right, ratio < 0.6 ? middle : bottom); } private handleBodyPartsDraggableGridOnResize(): void { - const gridHeight = (innerHeight * (this.isVertical ? 0.3 : 0.35)) / waScaleManager.getActualZoom(); + const gridHeight = 125; const gridWidth = (innerWidth * (this.isVertical ? 1 : 0.8)) / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, @@ -354,15 +352,15 @@ export class CustomizeScene extends AbstractCharacterScene { if (this.selectedBodyPartType === undefined) { return; } - const slotDimension = (innerHeight * (this.isVertical ? 0.125 : 0.15)) / waScaleManager.getActualZoom(); - const slotScale = slotDimension / this.customWokaPreviewer.SIZE; + const slotDimension = 100; const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[this.selectedBodyPartType]]; this.bodyPartsDraggableGrid.clearAllItems(); for (let i = 0; i < bodyPartsLayer.length; i += 1) { - const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(), i).setScale( - slotScale + const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(), i).setDisplaySize( + slotDimension, + slotDimension ); if (this.selectedBodyPartType === CustomWokaBodyPart.Body) { slot.setBodyTexture(bodyPartsLayer[i].id); diff --git a/front/src/Utils/MathUtils.ts b/front/src/Utils/MathUtils.ts index ebe8fb34..77e184cd 100644 --- a/front/src/Utils/MathUtils.ts +++ b/front/src/Utils/MathUtils.ts @@ -62,14 +62,16 @@ export class MathUtils { const spriteWidth = Math.floor(unitSizeWidth * newScaleW); const spriteHeight = Math.floor(unitSizeHeight * newScaleH); + const restWidth = spriteWidth % unitSizeWidth; + const restHeight = spriteWidth % unitSizeHeight; + // Expected nearest sprite size to maintain crisp pixels - const expectedSpriteWidth = spriteWidth - (spriteWidth % unitSizeWidth); - const expectedSpriteHeight = spriteHeight - (spriteHeight % unitSizeHeight); + const expectedSpriteWidth = spriteWidth - restWidth + (restWidth > unitSizeWidth / 2 ? unitSizeWidth : 0); + const expectedSpriteHeight = spriteHeight - restHeight + (restHeight > unitSizeHeight / 2 ? unitSizeHeight : 0); // Expected nearest scale const neededScaleWidth = expectedSpriteWidth / unitSizeWidth; const neededScaleHeight = expectedSpriteHeight / unitSizeHeight; - console.log(neededScaleWidth, neededScaleHeight); // Calculate new width and height and apply it to the whole container return [baseWidth * neededScaleWidth, baseHeight * neededScaleHeight]; From ccf897f3201d04d081f8f1196e973a0d990f5964 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Mon, 21 Mar 2022 16:44:51 +0100 Subject: [PATCH 45/61] fixing grid. wip --- .../CustomizeWoka/CustomWokaPreviewer.ts | 8 +- .../CustomizeWoka/WokaBodyPartSlot.ts | 28 +++-- front/src/Phaser/Login/CustomizeScene.ts | 102 +++++++++++------- 3 files changed, 86 insertions(+), 52 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index 570e07eb..a0eb4e9a 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -74,10 +74,10 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.animate(); } - public setDisplaySize(width: number, height: number): this { - const [newWidth, newHeight] = MathUtils.getWholePixelsNewSize(this.SIZE, this.SIZE, width, height); - return super.setDisplaySize(newWidth, newHeight); - } + // public setDisplaySize(width: number, height: number): this { + // const [newWidth, newHeight] = MathUtils.getWholePixelsNewSize(this.SIZE, this.SIZE, width, height); + // return super.setDisplaySize(newWidth, newHeight); + // } public changeAnimation(direction: PlayerAnimationDirections, moving: boolean): void { this.animationDirection = direction; diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 5aceb34d..f71b7ad1 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -19,7 +19,7 @@ export enum WokaBodyPartSlotEvent { } export class WokaBodyPartSlot extends GridItem { - private background: Phaser.GameObjects.Rectangle; + private background: Phaser.GameObjects.Graphics; private bodyImage: Phaser.GameObjects.Image; private image: Phaser.GameObjects.Image; @@ -38,9 +38,8 @@ export class WokaBodyPartSlot extends GridItem { const offsetX = -2; this.selected = this.config.selected ?? false; - this.background = this.scene.add - .rectangle(0, 0, this.SIZE, this.SIZE, this.config.color) - .setStrokeStyle(this.config.borderThickness, this.config.borderColor); + this.background = this.scene.add.graphics(); + this.drawBackground(); this.bodyImage = this.scene.add .image(offsetX, offsetY, config.bodyImageKey ?? "") @@ -50,7 +49,7 @@ export class WokaBodyPartSlot extends GridItem { .image(offsetX, offsetY, config.imageKey ?? "") .setVisible(config.bodyImageKey !== undefined); - this.setSize(this.SIZE + this.config.borderThickness, this.SIZE + this.config.borderThickness); + this.setSize(this.SIZE, this.SIZE); this.add([this.background, this.bodyImage, this.image]); @@ -62,11 +61,6 @@ export class WokaBodyPartSlot extends GridItem { this.scene.add.existing(this); } - public setDisplaySize(width: number, height: number): this { - const [newWidth, newHeight] = MathUtils.getWholePixelsNewSize(this.SIZE, this.SIZE, width, height, 32, 32); - return super.setDisplaySize(newWidth, newHeight); - } - public setTextures(bodyTextureKey?: string, imageTextureKey?: string): void { this.setBodyTexture(bodyTextureKey); this.setImageTexture(imageTextureKey); @@ -106,7 +100,19 @@ export class WokaBodyPartSlot extends GridItem { }); } + private drawBackground(): void { + this.background.clear(); + this.background.fillStyle(0xffffff); + this.background.lineStyle( + this.config.borderThickness, + this.selected ? this.config.borderSelectedColor : this.config.borderColor + ); + + this.background.fillRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); + this.background.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); + } + private updateSelected(): void { - this.background.setStrokeStyle(2.5, this.selected ? this.config.borderSelectedColor : this.config.borderColor); + this.drawBackground(); } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 4cead922..56804e05 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -30,8 +30,8 @@ export const CustomizeSceneName = "CustomizeScene"; export class CustomizeScene extends AbstractCharacterScene { private customWokaPreviewer!: CustomWokaPreviewer; - private bodyPartsDraggableGridBackground!: Phaser.GameObjects.Rectangle; - private bodyPartsDraggableGridForeground!: Phaser.GameObjects.Rectangle; + private bodyPartsDraggableGridBackground!: Phaser.GameObjects.Graphics; + private bodyPartsDraggableGridForeground!: Phaser.GameObjects.Graphics; private bodyPartsDraggableGrid!: DraggableGrid; private bodyPartsSlots!: Record; @@ -101,7 +101,18 @@ export class CustomizeScene extends AbstractCharacterScene { this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, this.getCustomWokaPreviewerConfig()); - this.bodyPartsDraggableGridBackground = this.add.rectangle(0, 0, 485, 165, 0xf9f9f9); + this.bodyPartsDraggableGridBackground = this.add.graphics(); + + const gridBackgroundWidth = 500; + const gridBackgroundHeight = 170; + this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9); + this.bodyPartsDraggableGridBackground.fillRect( + -gridBackgroundWidth / 2, + -gridBackgroundHeight / 2, + gridBackgroundWidth, + gridBackgroundHeight + ); + this.bodyPartsDraggableGrid = new DraggableGrid(this, { position: { x: 0, y: 0 }, maskPosition: { x: 0, y: 0 }, @@ -115,10 +126,10 @@ export class CustomizeScene extends AbstractCharacterScene { }, spacing: 5, debug: { - showDraggableSpace: false, + showDraggableSpace: true, }, }); - this.bodyPartsDraggableGridForeground = this.add.rectangle(0, 0, 485, 165, 0xffffff, 0); + this.bodyPartsDraggableGridForeground = this.add.graphics(); this.bodyPartsSlots = { [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), @@ -137,7 +148,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public update(time: number, dt: number): void { - this.customWokaPreviewer.update(); + // this.customWokaPreviewer.update(); } public onResize(): void { @@ -175,6 +186,32 @@ export class CustomizeScene extends AbstractCharacterScene { this.scene.run(SelectCharacterSceneName); } + private drawGridBackground(gridPosition: { x: number; y: number }): void { + const gridBackgroundWidth = 500; + const gridBackgroundHeight = 170; + this.bodyPartsDraggableGridBackground.clear(); + this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9); + this.bodyPartsDraggableGridBackground.fillRect( + gridPosition.x - gridBackgroundWidth / 2, + gridPosition.y - gridBackgroundHeight / 2, + gridBackgroundWidth, + gridBackgroundHeight + ); + } + + private drawGridForeground(gridPosition: { x: number; y: number }): void { + const gridBackgroundWidth = 500; + const gridBackgroundHeight = 170; + this.bodyPartsDraggableGridForeground.clear(); + this.bodyPartsDraggableGridForeground.lineStyle(2, 0xadafbc); + this.bodyPartsDraggableGridForeground.strokeRect( + gridPosition.x - gridBackgroundWidth / 2, + gridPosition.y - gridBackgroundHeight / 2, + gridBackgroundWidth, + gridBackgroundHeight + ); + } + private refreshPlayerCurrentOutfit(): void { let i = 0; for (const layerItem of this.selectedLayers) { @@ -189,13 +226,9 @@ export class CustomizeScene extends AbstractCharacterScene { } private handleCustomWokaPreviewerOnResize(): void { - const slotDimension = - Math.min(innerWidth * (this.isVertical ? 0.2 : 0.2), innerHeight * (this.isVertical ? 0.2 : 0.2)) / - waScaleManager.getActualZoom(); + const slotDimension = 100; - const boxDimension = - Math.min(innerWidth * (this.isVertical ? 0.4 : 0.5), innerHeight * (this.isVertical ? 0.4 : 0.5)) / - waScaleManager.getActualZoom(); + const boxDimension = 200; this.customWokaPreviewer.setDisplaySize(boxDimension, boxDimension); this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; @@ -214,11 +247,13 @@ export class CustomizeScene extends AbstractCharacterScene { const slotSize = this.bodyPartsSlots.Accessory.displayHeight; if (this.isVertical) { - const middle = this.customWokaPreviewer.x; - const left = middle - slotSize - 10; - const right = middle + slotSize + 10; - const top = this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 0.5 + 10; - const bottom = top + slotSize + 10; + const middle = Math.floor(this.customWokaPreviewer.x); + const left = Math.floor(middle - slotSize - 10); + const right = Math.floor(middle + slotSize + 10); + const top = Math.floor( + this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 0.5 + 10 + ); + const bottom = Math.floor(top + slotSize + 10); this.bodyPartsSlots.Hair.setPosition(left, top); this.bodyPartsSlots.Hat.setPosition(middle, top); @@ -232,13 +267,13 @@ export class CustomizeScene extends AbstractCharacterScene { const ratio = innerHeight / innerWidth; - const left = this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize; - const leftEdge = left - slotSize - 10; - const right = this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize; - const rightEdge = right + slotSize + 10; - const top = 0 + slotSize * 0.5 + 10; - const middle = top + slotSize + 10; - const bottom = middle + slotSize + 10; + const left = Math.floor(this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize); + const leftEdge = Math.floor(left - slotSize - 10); + const right = Math.floor(this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize); + const rightEdge = Math.floor(right + slotSize + 10); + const top = Math.floor(0 + slotSize * 0.5 + 10); + const middle = Math.floor(top + slotSize + 10); + const bottom = Math.floor(middle + slotSize + 10); this.bodyPartsSlots.Hair.setPosition(left, top); this.bodyPartsSlots.Body.setPosition(left, middle); @@ -256,11 +291,8 @@ export class CustomizeScene extends AbstractCharacterScene { y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5, }; - this.bodyPartsDraggableGridBackground.setPosition(gridPos.x, gridPos.y).setDisplaySize(gridWidth, gridHeight); - this.bodyPartsDraggableGridForeground - .setPosition(gridPos.x, gridPos.y) - .setDisplaySize(gridWidth, gridHeight) - .setStrokeStyle(4, 0xaaaaaa); + this.drawGridBackground(gridPos); + this.drawGridForeground(gridPos); this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: gridWidth, y: gridHeight }, gridPos); this.populateGrid(); @@ -270,7 +302,7 @@ export class CustomizeScene extends AbstractCharacterScene { private getCustomWokaPreviewerConfig(): CustomWokaPreviewerConfig { return { color: 0xffffff, - borderThickness: 2.5, + borderThickness: 1, borderColor: 0xadafbc, bodyPartsOffsetX: -1, }; @@ -279,18 +311,14 @@ export class CustomizeScene extends AbstractCharacterScene { private getDefaultWokaBodyPartSlotConfig(): WokaBodyPartSlotConfig { return { color: 0xffffff, - borderThickness: this.countZoom(this.isVertical ? 4 : 4), + borderThickness: 1, borderColor: 0xadafbc, borderSelectedColor: 0x00ffff, - offsetX: this.countZoom(this.isVertical ? -4 : -3), - offsetY: this.countZoom(this.isVertical ? -3 : -2), + offsetX: -4, + offsetY: -3, }; } - private countZoom(value: number): number { - return Math.floor(value / waScaleManager.getActualZoom()); - } - private bindEventHandlers(): void { this.events.addListener("wake", () => { waScaleManager.saveZoom(); From 2cf55cac7e8938a6e170c741f666a3005d286cc4 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Tue, 22 Mar 2022 09:44:26 +0100 Subject: [PATCH 46/61] crispy pixels --- front/src/Phaser/Login/CustomizeScene.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 56804e05..0a449fb3 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -126,7 +126,7 @@ export class CustomizeScene extends AbstractCharacterScene { }, spacing: 5, debug: { - showDraggableSpace: true, + showDraggableSpace: false, }, }); this.bodyPartsDraggableGridForeground = this.add.graphics(); @@ -187,8 +187,8 @@ export class CustomizeScene extends AbstractCharacterScene { } private drawGridBackground(gridPosition: { x: number; y: number }): void { - const gridBackgroundWidth = 500; - const gridBackgroundHeight = 170; + const gridBackgroundWidth = innerWidth / waScaleManager.getActualZoom(); + const gridBackgroundHeight = 130; this.bodyPartsDraggableGridBackground.clear(); this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9); this.bodyPartsDraggableGridBackground.fillRect( @@ -200,8 +200,8 @@ export class CustomizeScene extends AbstractCharacterScene { } private drawGridForeground(gridPosition: { x: number; y: number }): void { - const gridBackgroundWidth = 500; - const gridBackgroundHeight = 170; + const gridBackgroundWidth = innerWidth / waScaleManager.getActualZoom(); + const gridBackgroundHeight = 130; this.bodyPartsDraggableGridForeground.clear(); this.bodyPartsDraggableGridForeground.lineStyle(2, 0xadafbc); this.bodyPartsDraggableGridForeground.strokeRect( @@ -285,7 +285,7 @@ export class CustomizeScene extends AbstractCharacterScene { private handleBodyPartsDraggableGridOnResize(): void { const gridHeight = 125; - const gridWidth = (innerWidth * (this.isVertical ? 1 : 0.8)) / waScaleManager.getActualZoom(); + const gridWidth = innerWidth / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5, From d971c7e064d19ae27e0a05a341a6b757897ffcef Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Tue, 22 Mar 2022 11:56:22 +0100 Subject: [PATCH 47/61] randomize and finish buttons --- .../CustomizeWoka/CustomWokaPreviewer.ts | 29 ++++- .../CustomizeWoka/WokaBodyPartSlot.ts | 2 +- front/src/Phaser/Components/Ui/Button.ts | 53 ++++++++ .../src/Phaser/Components/Ui/StatesButton.ts | 0 front/src/Phaser/Login/CustomizeScene.ts | 123 ++++++++++++++---- front/src/Utils/MathUtils.ts | 41 ------ 6 files changed, 178 insertions(+), 70 deletions(-) create mode 100644 front/src/Phaser/Components/Ui/Button.ts delete mode 100644 front/src/Phaser/Components/Ui/StatesButton.ts diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index a0eb4e9a..828e03ec 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -56,6 +56,7 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.background = this.scene.add.graphics(); this.drawBackground(); this.setSize(this.SIZE, this.SIZE); + this.setInteractive({ cursor: "pointer" }); this.add([ this.background, @@ -67,6 +68,8 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.sprites.Accessory, ]); + this.bindEventHandlers(); + this.scene.add.existing(this); } @@ -74,11 +77,6 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.animate(); } - // public setDisplaySize(width: number, height: number): this { - // const [newWidth, newHeight] = MathUtils.getWholePixelsNewSize(this.SIZE, this.SIZE, width, height); - // return super.setDisplaySize(newWidth, newHeight); - // } - public changeAnimation(direction: PlayerAnimationDirections, moving: boolean): void { this.animationDirection = direction; this.moving = moving; @@ -112,6 +110,14 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { return this.animationDirection; } + private bindEventHandlers(): void { + this.on(Phaser.Input.Events.POINTER_UP, () => { + const direction = this.getNextAnimationDirection(); + const moving = direction === PlayerAnimationDirections.Down ? !this.moving : this.moving; + this.changeAnimation(direction, moving); + }); + } + private drawBackground(): void { this.background.clear(); this.background.fillStyle(0xffffff); @@ -142,4 +148,17 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { } } } + + private getNextAnimationDirection(): PlayerAnimationDirections { + switch (this.animationDirection) { + case PlayerAnimationDirections.Down: + return PlayerAnimationDirections.Left; + case PlayerAnimationDirections.Left: + return PlayerAnimationDirections.Up; + case PlayerAnimationDirections.Up: + return PlayerAnimationDirections.Right; + case PlayerAnimationDirections.Right: + return PlayerAnimationDirections.Down; + } + } } diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index f71b7ad1..14fc865b 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -53,7 +53,7 @@ export class WokaBodyPartSlot extends GridItem { this.add([this.background, this.bodyImage, this.image]); - this.setInteractive(); + this.setInteractive({ cursor: "pointer" }); this.scene.input.setDraggable(this); this.bindEventHandlers(); diff --git a/front/src/Phaser/Components/Ui/Button.ts b/front/src/Phaser/Components/Ui/Button.ts new file mode 100644 index 00000000..6fb39571 --- /dev/null +++ b/front/src/Phaser/Components/Ui/Button.ts @@ -0,0 +1,53 @@ +export interface ButtonConfig { + width: number; + height: number; + idle: ButtonAppearanceConfig; + hover: ButtonAppearanceConfig; + pressed: ButtonAppearanceConfig; +} + +export interface ButtonAppearanceConfig { + color: number; + borderThickness: number; + borderColor: number; +} + +export class Button extends Phaser.GameObjects.Container { + private background: Phaser.GameObjects.Graphics; + private text: Phaser.GameObjects.Text; + + private config: ButtonConfig; + + constructor(scene: Phaser.Scene, x: number, y: number, config: ButtonConfig) { + super(scene, x, y); + + this.config = config; + + this.background = this.scene.add.graphics(); + this.drawBackground(this.config.idle); + this.text = this.scene.add.text(0, 0, "", { color: "0x000000" }).setOrigin(0.5); + + this.add([this.background, this.text]); + + this.setSize(this.config.width, this.config.height); + this.setInteractive({ cursor: "pointer" }); + + this.scene.add.existing(this); + } + + public setText(text: string): void { + this.text.setText(text); + } + + private drawBackground(appearance: ButtonAppearanceConfig): void { + this.background.clear(); + this.background.fillStyle(appearance.color); + this.background.lineStyle(appearance.borderThickness, appearance.borderColor); + + const w = this.config.width; + const h = this.config.height; + + this.background.fillRect(-w / 2, -h / 2, w, h); + this.background.strokeRect(-w / 2, -h / 2, w, h); + } +} diff --git a/front/src/Phaser/Components/Ui/StatesButton.ts b/front/src/Phaser/Components/Ui/StatesButton.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 0a449fb3..7dfb1fb2 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -25,6 +25,7 @@ import { WokaBodyPartSlotEvent, } from "../Components/CustomizeWoka/WokaBodyPartSlot"; import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid"; +import { Button } from "../Components/Ui/Button"; export const CustomizeSceneName = "CustomizeScene"; @@ -35,6 +36,9 @@ export class CustomizeScene extends AbstractCharacterScene { private bodyPartsDraggableGrid!: DraggableGrid; private bodyPartsSlots!: Record; + private randomizeButton!: Button; + private finishButton!: Button; + private selectedLayers: number[] = [0, 1, 2, 3, 4, 5]; private containersRow: CustomizedCharacter[][] = []; private layers: BodyResourceDescriptionInterface[][] = []; @@ -42,8 +46,6 @@ export class CustomizeScene extends AbstractCharacterScene { protected lazyloadingAttempt = true; //permit to update texture loaded after renderer - private isVertical: boolean = false; - private loader: Loader; constructor() { @@ -97,9 +99,12 @@ export class CustomizeScene extends AbstractCharacterScene { } public create(): void { - this.isVertical = innerHeight / innerWidth > 1; - - this.customWokaPreviewer = new CustomWokaPreviewer(this, 0, 0, this.getCustomWokaPreviewerConfig()); + this.customWokaPreviewer = new CustomWokaPreviewer( + this, + 0, + 0, + this.getCustomWokaPreviewerConfig() + ).setDisplaySize(200, 200); this.bodyPartsDraggableGridBackground = this.add.graphics(); @@ -140,6 +145,9 @@ export class CustomizeScene extends AbstractCharacterScene { [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), }; + this.initializeRandomizeButton(); + this.initializeFinishButton(); + this.refreshPlayerCurrentOutfit(); this.onResize(); @@ -148,15 +156,15 @@ export class CustomizeScene extends AbstractCharacterScene { } public update(time: number, dt: number): void { - // this.customWokaPreviewer.update(); + this.customWokaPreviewer.update(); } public onResize(): void { - this.isVertical = innerHeight / innerWidth > 1; - this.handleCustomWokaPreviewerOnResize(); this.handleBodyPartSlotsOnResize(); this.handleBodyPartsDraggableGridOnResize(); + this.handleRandomizeButtonOnResize(); + this.handleFinishButtonOnResize(); } public nextSceneToCamera() { @@ -212,6 +220,52 @@ export class CustomizeScene extends AbstractCharacterScene { ); } + private initializeRandomizeButton(): void { + this.randomizeButton = new Button(this, 50, 50, { + width: 95, + height: 50, + idle: { + color: 0xffffff, + borderThickness: 1, + borderColor: 0xadafbc, + }, + hover: { + color: 0xffffff, + borderThickness: 1, + borderColor: 0xadafbc, + }, + pressed: { + color: 0xffffff, + borderThickness: 1, + borderColor: 0xadafbc, + }, + }); + this.randomizeButton.setText("Randomize"); + } + + private initializeFinishButton(): void { + this.finishButton = new Button(this, 50, 50, { + width: 95, + height: 50, + idle: { + color: 0xffffff, + borderThickness: 1, + borderColor: 0xadafbc, + }, + hover: { + color: 0xffffff, + borderThickness: 1, + borderColor: 0xadafbc, + }, + pressed: { + color: 0xffffff, + borderThickness: 1, + borderColor: 0xadafbc, + }, + }); + this.finishButton.setText("Finish"); + } + private refreshPlayerCurrentOutfit(): void { let i = 0; for (const layerItem of this.selectedLayers) { @@ -226,18 +280,13 @@ export class CustomizeScene extends AbstractCharacterScene { } private handleCustomWokaPreviewerOnResize(): void { - const slotDimension = 100; - - const boxDimension = 200; - - this.customWokaPreviewer.setDisplaySize(boxDimension, boxDimension); this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.customWokaPreviewer.y = this.isVertical - ? this.customWokaPreviewer.displayHeight * 0.5 + 20 - : slotDimension * 1.5 + 20; + this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 20; } private handleBodyPartSlotsOnResize(): void { + const ratio = innerHeight / innerWidth; + console.log(ratio); const slotDimension = 100; for (const part in this.bodyPartsSlots) { @@ -246,12 +295,12 @@ export class CustomizeScene extends AbstractCharacterScene { const slotSize = this.bodyPartsSlots.Accessory.displayHeight; - if (this.isVertical) { + if (ratio > 1.6) { const middle = Math.floor(this.customWokaPreviewer.x); const left = Math.floor(middle - slotSize - 10); const right = Math.floor(middle + slotSize + 10); const top = Math.floor( - this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 0.5 + 10 + this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 1.5 + 10 ); const bottom = Math.floor(top + slotSize + 10); @@ -265,11 +314,13 @@ export class CustomizeScene extends AbstractCharacterScene { return; } - const ratio = innerHeight / innerWidth; - - const left = Math.floor(this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize); + const left = Math.floor( + this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize * 0.5 - 10 + ); const leftEdge = Math.floor(left - slotSize - 10); - const right = Math.floor(this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize); + const right = Math.floor( + this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize * 0.5 + 10 + ); const rightEdge = Math.floor(right + slotSize + 10); const top = Math.floor(0 + slotSize * 0.5 + 10); const middle = Math.floor(top + slotSize + 10); @@ -299,6 +350,28 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGrid.moveContentToBeginning(); } + private handleRandomizeButtonOnResize(): void { + const x = + this.customWokaPreviewer.x + + (this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5; + const y = + this.customWokaPreviewer.y + + (this.customWokaPreviewer.displayHeight + this.randomizeButton.displayHeight) * 0.5 + + 10; + this.randomizeButton.setPosition(x, y); + } + + private handleFinishButtonOnResize(): void { + const x = + this.customWokaPreviewer.x - + (this.customWokaPreviewer.displayWidth - this.randomizeButton.displayWidth) * 0.5; + const y = + this.customWokaPreviewer.y + + (this.customWokaPreviewer.displayHeight + this.randomizeButton.displayHeight) * 0.5 + + 10; + this.finishButton.setPosition(x, y); + } + private getCustomWokaPreviewerConfig(): CustomWokaPreviewerConfig { return { color: 0xffffff, @@ -332,13 +405,17 @@ export class CustomizeScene extends AbstractCharacterScene { this.backToPreviousScene(); }); - this.input.keyboard.on("keydown-R", () => { + this.randomizeButton.on(Phaser.Input.Events.POINTER_UP, () => { this.randomizeOutfit(); this.clearGrid(); this.deselectAllSlots(); this.refreshPlayerCurrentOutfit(); }); + this.finishButton.on(Phaser.Input.Events.POINTER_UP, () => { + this.nextSceneToCamera(); + }); + for (const bodyPart in CustomWokaBodyPart) { const slot = this.bodyPartsSlots[bodyPart as CustomWokaBodyPart]; slot.on(WokaBodyPartSlotEvent.Clicked, (selected: boolean) => { diff --git a/front/src/Utils/MathUtils.ts b/front/src/Utils/MathUtils.ts index 77e184cd..fc055d11 100644 --- a/front/src/Utils/MathUtils.ts +++ b/front/src/Utils/MathUtils.ts @@ -35,45 +35,4 @@ export class MathUtils { public static randomFromArray(array: T[]): T { return array[Math.floor(Math.random() * array.length)]; } - - /** - * - * @param baseWidth Object's default width not affected by any scaling - * @param baseHeight Object's default height not affected by any scaling - * @param requestedWidth Width we would like to achieve - * @param requestedHeight Height we would like to achieve - * @param unitSizeWidth Smallest possible unit of our 'scale step' for width - * @param unitSizeHeight Smallest possible unit of our 'scale step' for height - * @returns [ newWidth, newHeight ] - */ - public static getWholePixelsNewSize( - baseWidth: number, - baseHeight: number, - requestedWidth: number, - requestedHeight: number, - unitSizeWidth: number = 32, - unitSizeHeight: number = 32 - ): [number, number] { - // Demanded scale to be applied - const newScaleW = requestedWidth / baseWidth; - const newScaleH = requestedHeight / baseHeight; - - // How would it affect our sprites - const spriteWidth = Math.floor(unitSizeWidth * newScaleW); - const spriteHeight = Math.floor(unitSizeHeight * newScaleH); - - const restWidth = spriteWidth % unitSizeWidth; - const restHeight = spriteWidth % unitSizeHeight; - - // Expected nearest sprite size to maintain crisp pixels - const expectedSpriteWidth = spriteWidth - restWidth + (restWidth > unitSizeWidth / 2 ? unitSizeWidth : 0); - const expectedSpriteHeight = spriteHeight - restHeight + (restHeight > unitSizeHeight / 2 ? unitSizeHeight : 0); - - // Expected nearest scale - const neededScaleWidth = expectedSpriteWidth / unitSizeWidth; - const neededScaleHeight = expectedSpriteHeight / unitSizeHeight; - - // Calculate new width and height and apply it to the whole container - return [baseWidth * neededScaleWidth, baseHeight * neededScaleHeight]; - } } From 11a262b1787d5c70e441d2df6fc818dc9f4b4067 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Tue, 22 Mar 2022 13:37:41 +0100 Subject: [PATCH 48/61] make sure horizontal iPhoneX displays view properly --- front/src/Phaser/Components/Ui/Button.ts | 42 +++++++++++++++++++- front/src/Phaser/Login/CustomizeScene.ts | 50 +++++++++++++----------- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/front/src/Phaser/Components/Ui/Button.ts b/front/src/Phaser/Components/Ui/Button.ts index 6fb39571..3c4f80da 100644 --- a/front/src/Phaser/Components/Ui/Button.ts +++ b/front/src/Phaser/Components/Ui/Button.ts @@ -7,6 +7,7 @@ export interface ButtonConfig { } export interface ButtonAppearanceConfig { + textColor: string; color: number; borderThickness: number; borderColor: number; @@ -18,20 +19,25 @@ export class Button extends Phaser.GameObjects.Container { private config: ButtonConfig; + private hovered: boolean = false; + private pressed: boolean = false; + constructor(scene: Phaser.Scene, x: number, y: number, config: ButtonConfig) { super(scene, x, y); this.config = config; this.background = this.scene.add.graphics(); - this.drawBackground(this.config.idle); this.text = this.scene.add.text(0, 0, "", { color: "0x000000" }).setOrigin(0.5); + this.drawBackground(this.config.idle); this.add([this.background, this.text]); this.setSize(this.config.width, this.config.height); this.setInteractive({ cursor: "pointer" }); + this.bindEventHandlers(); + this.scene.add.existing(this); } @@ -39,6 +45,18 @@ export class Button extends Phaser.GameObjects.Container { this.text.setText(text); } + private updateButtonAppearance(): void { + if (this.pressed) { + this.drawBackground(this.config.pressed); + return; + } + if (this.hovered) { + this.drawBackground(this.config.hover); + return; + } + this.drawBackground(this.config.idle); + } + private drawBackground(appearance: ButtonAppearanceConfig): void { this.background.clear(); this.background.fillStyle(appearance.color); @@ -49,5 +67,27 @@ export class Button extends Phaser.GameObjects.Container { this.background.fillRect(-w / 2, -h / 2, w, h); this.background.strokeRect(-w / 2, -h / 2, w, h); + + this.text.setColor(appearance.textColor); + } + + private bindEventHandlers(): void { + this.on(Phaser.Input.Events.POINTER_OVER, () => { + this.hovered = true; + this.updateButtonAppearance(); + }); + this.on(Phaser.Input.Events.POINTER_OUT, () => { + this.hovered = false; + this.pressed = false; + this.updateButtonAppearance(); + }); + this.on(Phaser.Input.Events.POINTER_DOWN, () => { + this.pressed = true; + this.updateButtonAppearance(); + }); + this.on(Phaser.Input.Events.POINTER_UP, () => { + this.pressed = false; + this.updateButtonAppearance(); + }); } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 7dfb1fb2..40c25ec2 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -196,7 +196,7 @@ export class CustomizeScene extends AbstractCharacterScene { private drawGridBackground(gridPosition: { x: number; y: number }): void { const gridBackgroundWidth = innerWidth / waScaleManager.getActualZoom(); - const gridBackgroundHeight = 130; + const gridBackgroundHeight = 115; this.bodyPartsDraggableGridBackground.clear(); this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9); this.bodyPartsDraggableGridBackground.fillRect( @@ -208,10 +208,10 @@ export class CustomizeScene extends AbstractCharacterScene { } private drawGridForeground(gridPosition: { x: number; y: number }): void { - const gridBackgroundWidth = innerWidth / waScaleManager.getActualZoom(); - const gridBackgroundHeight = 130; + const gridBackgroundWidth = (innerWidth + 10) / waScaleManager.getActualZoom(); + const gridBackgroundHeight = 115; this.bodyPartsDraggableGridForeground.clear(); - this.bodyPartsDraggableGridForeground.lineStyle(2, 0xadafbc); + this.bodyPartsDraggableGridForeground.lineStyle(4, 0xadafbc); this.bodyPartsDraggableGridForeground.strokeRect( gridPosition.x - gridBackgroundWidth / 2, gridPosition.y - gridBackgroundHeight / 2, @@ -226,17 +226,20 @@ export class CustomizeScene extends AbstractCharacterScene { height: 50, idle: { color: 0xffffff, - borderThickness: 1, - borderColor: 0xadafbc, + textColor: "#000000", + borderThickness: 3, + borderColor: 0xe7e7e7, }, hover: { - color: 0xffffff, - borderThickness: 1, + color: 0xe7e7e7, + textColor: "#000000", + borderThickness: 3, borderColor: 0xadafbc, }, pressed: { - color: 0xffffff, - borderThickness: 1, + color: 0xadafbc, + textColor: "#000000", + borderThickness: 3, borderColor: 0xadafbc, }, }); @@ -248,19 +251,22 @@ export class CustomizeScene extends AbstractCharacterScene { width: 95, height: 50, idle: { - color: 0xffffff, - borderThickness: 1, - borderColor: 0xadafbc, + color: 0x209cee, + textColor: "#ffffff", + borderThickness: 3, + borderColor: 0x006bb3, }, hover: { - color: 0xffffff, - borderThickness: 1, - borderColor: 0xadafbc, + color: 0x209cee, + textColor: "#ffffff", + borderThickness: 3, + borderColor: 0x006bb3, }, pressed: { - color: 0xffffff, - borderThickness: 1, - borderColor: 0xadafbc, + color: 0x006bb3, + textColor: "#ffffff", + borderThickness: 3, + borderColor: 0x006bb3, }, }); this.finishButton.setText("Finish"); @@ -281,7 +287,7 @@ export class CustomizeScene extends AbstractCharacterScene { private handleCustomWokaPreviewerOnResize(): void { this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; - this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 20; + this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 10; } private handleBodyPartSlotsOnResize(): void { @@ -335,7 +341,7 @@ export class CustomizeScene extends AbstractCharacterScene { } private handleBodyPartsDraggableGridOnResize(): void { - const gridHeight = 125; + const gridHeight = 110; const gridWidth = innerWidth / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, @@ -386,7 +392,7 @@ export class CustomizeScene extends AbstractCharacterScene { color: 0xffffff, borderThickness: 1, borderColor: 0xadafbc, - borderSelectedColor: 0x00ffff, + borderSelectedColor: 0x209cee, offsetX: -4, offsetY: -3, }; From 87034a453e241a54da1ec6c32215a074b422b2fd Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Tue, 22 Mar 2022 14:39:47 +0100 Subject: [PATCH 49/61] set starting parts on 0 indices --- front/src/Phaser/Login/CustomizeScene.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 40c25ec2..4b8f141a 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -39,7 +39,7 @@ export class CustomizeScene extends AbstractCharacterScene { private randomizeButton!: Button; private finishButton!: Button; - private selectedLayers: number[] = [0, 1, 2, 3, 4, 5]; + private selectedLayers: number[] = [0, 0, 0, 0, 0, 0]; private containersRow: CustomizedCharacter[][] = []; private layers: BodyResourceDescriptionInterface[][] = []; private selectedBodyPartType?: CustomWokaBodyPart; @@ -292,7 +292,6 @@ export class CustomizeScene extends AbstractCharacterScene { private handleBodyPartSlotsOnResize(): void { const ratio = innerHeight / innerWidth; - console.log(ratio); const slotDimension = 100; for (const part in this.bodyPartsSlots) { @@ -306,7 +305,7 @@ export class CustomizeScene extends AbstractCharacterScene { const left = Math.floor(middle - slotSize - 10); const right = Math.floor(middle + slotSize + 10); const top = Math.floor( - this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 1.5 + 10 + this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 1.5 + 9 ); const bottom = Math.floor(top + slotSize + 10); @@ -328,7 +327,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize * 0.5 + 10 ); const rightEdge = Math.floor(right + slotSize + 10); - const top = Math.floor(0 + slotSize * 0.5 + 10); + const top = Math.floor(0 + slotSize * 0.5 + 9); const middle = Math.floor(top + slotSize + 10); const bottom = Math.floor(middle + slotSize + 10); @@ -350,7 +349,15 @@ export class CustomizeScene extends AbstractCharacterScene { this.drawGridBackground(gridPos); this.drawGridForeground(gridPos); - this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize(gridPos, { x: gridWidth, y: gridHeight }, gridPos); + try { + this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize( + gridPos, + { x: gridWidth, y: gridHeight }, + gridPos + ); + } catch (error) { + console.warn(error); + } this.populateGrid(); this.bodyPartsDraggableGrid.moveContentToBeginning(); From f446918e42b62c401d6db0bfdfa296efbe238f89 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Tue, 22 Mar 2022 16:49:20 +0100 Subject: [PATCH 50/61] start normally --- .../CustomizeWoka/WokaBodyPartSlot.ts | 13 +++++++- front/src/Phaser/Login/CustomizeScene.ts | 32 +++++++++++++++---- front/src/Phaser/Login/EntryScene.ts | 8 ++--- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 14fc865b..4370f1d8 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -10,6 +10,7 @@ export interface WokaBodyPartSlotConfig { offsetX: number; offsetY: number; bodyImageKey?: string; + categoryImageKey?: string; imageKey?: string; selected?: boolean; } @@ -20,6 +21,7 @@ export enum WokaBodyPartSlotEvent { export class WokaBodyPartSlot extends GridItem { private background: Phaser.GameObjects.Graphics; + private categoryImage?: Phaser.GameObjects.Image; private bodyImage: Phaser.GameObjects.Image; private image: Phaser.GameObjects.Image; @@ -40,6 +42,15 @@ export class WokaBodyPartSlot extends GridItem { this.background = this.scene.add.graphics(); this.drawBackground(); + this.add(this.background); + + if (this.config.categoryImageKey) { + this.categoryImage = this.scene.add + .image(this.SIZE / 2 - 1, -this.SIZE / 2 + 1, this.config.categoryImageKey) + .setDisplaySize(16, 16) + .setOrigin(1, 0); + this.add(this.categoryImage); + } this.bodyImage = this.scene.add .image(offsetX, offsetY, config.bodyImageKey ?? "") @@ -51,7 +62,7 @@ export class WokaBodyPartSlot extends GridItem { this.setSize(this.SIZE, this.SIZE); - this.add([this.background, this.bodyImage, this.image]); + this.add([this.bodyImage, this.image]); this.setInteractive({ cursor: "pointer" }); this.scene.input.setDraggable(this); diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 4b8f141a..011f2ba5 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -137,12 +137,30 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGridForeground = this.add.graphics(); this.bodyPartsSlots = { - [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), - [CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), - [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), - [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), - [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), - [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig()), + [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, { + ...this.getDefaultWokaBodyPartSlotConfig(), + categoryImageKey: "iconTalk", + }), + [CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 0, 0, { + ...this.getDefaultWokaBodyPartSlotConfig(), + categoryImageKey: "iconTalk", + }), + [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 0, 0, { + ...this.getDefaultWokaBodyPartSlotConfig(), + categoryImageKey: "iconTalk", + }), + [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 0, 0, { + ...this.getDefaultWokaBodyPartSlotConfig(), + categoryImageKey: "iconTalk", + }), + [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 0, 0, { + ...this.getDefaultWokaBodyPartSlotConfig(), + categoryImageKey: "iconTalk", + }), + [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, { + ...this.getDefaultWokaBodyPartSlotConfig(), + categoryImageKey: "iconTalk", + }), }; this.initializeRandomizeButton(); @@ -257,7 +275,7 @@ export class CustomizeScene extends AbstractCharacterScene { borderColor: 0x006bb3, }, hover: { - color: 0x209cee, + color: 0x0987db, textColor: "#ffffff", borderThickness: 3, borderColor: 0x006bb3, diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index d54272a9..41910f26 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -1,14 +1,12 @@ import { gameManager } from "../Game/GameManager"; import { Scene } from "phaser"; -import { ErrorScene, ErrorSceneName } from "../Reconnecting/ErrorScene"; +import { ErrorScene } from "../Reconnecting/ErrorScene"; import { WAError } from "../Reconnecting/WAError"; import { waScaleManager } from "../Services/WaScaleManager"; import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene"; import LL from "../../i18n/i18n-svelte"; import { get } from "svelte/store"; import { localeDetector } from "../../i18n/locales"; -import { CustomizeSceneName } from "./CustomizeScene"; -import { SelectCharacterSceneName } from "./SelectCharacterScene"; export const EntrySceneName = "EntryScene"; @@ -46,9 +44,7 @@ export class EntryScene extends Scene { // Let's rescale before starting the game // We can do it at this stage. waScaleManager.applyNewSize(); - // this.scene.start(nextSceneName); - this.scene.start(CustomizeSceneName); - // this.scene.start(SelectCharacterSceneName); + this.scene.start(nextSceneName); }) .catch((err) => { const $LL = get(LL); From fcf865982994aae8cbf3354df3c4050d60a5e3be Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Tue, 22 Mar 2022 16:53:23 +0100 Subject: [PATCH 51/61] cleanup --- .../src/Phaser/Entity/CustomizedCharacter.ts | 27 ------------------- front/src/Phaser/Login/CustomizeScene.ts | 2 -- .../src/Phaser/Login/SelectCharacterScene.ts | 1 - 3 files changed, 30 deletions(-) delete mode 100644 front/src/Phaser/Entity/CustomizedCharacter.ts diff --git a/front/src/Phaser/Entity/CustomizedCharacter.ts b/front/src/Phaser/Entity/CustomizedCharacter.ts deleted file mode 100644 index 8c25450f..00000000 --- a/front/src/Phaser/Entity/CustomizedCharacter.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Container = Phaser.GameObjects.Container; -import type { Scene } from "phaser"; -import Sprite = Phaser.GameObjects.Sprite; -import { getPlayerAnimations, PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; - -/** - * A sprite of a customized character (used in the Customize Scene only) - */ -export class CustomizedCharacter extends Container { - private sprites: Phaser.GameObjects.Sprite[]; - - public constructor(scene: Scene, x: number, y: number, layers: string[]) { - super(scene, x, y); - this.sprites = []; - this.updateSprites(layers); - } - - public updateSprites(layers: string[]): void { - this.sprites = []; - this.removeAll(true); - for (const texture of layers) { - const newSprite = new Sprite(this.scene, 0, 0, texture); - this.sprites.push(newSprite); - } - this.add(this.sprites); - } -} diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 011f2ba5..eeb81933 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -8,7 +8,6 @@ import { AbstractCharacterScene } from "./AbstractCharacterScene"; import { areCharacterLayersValid } from "../../Connexion/LocalUser"; import { SelectCharacterSceneName } from "./SelectCharacterScene"; import { waScaleManager } from "../Services/WaScaleManager"; -import { CustomizedCharacter } from "../Entity/CustomizedCharacter"; import { analyticsClient } from "../../Administration/AnalyticsClient"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; @@ -40,7 +39,6 @@ export class CustomizeScene extends AbstractCharacterScene { private finishButton!: Button; private selectedLayers: number[] = [0, 0, 0, 0, 0, 0]; - private containersRow: CustomizedCharacter[][] = []; private layers: BodyResourceDescriptionInterface[][] = []; private selectedBodyPartType?: CustomWokaBodyPart; diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index d2f1e8f4..aac34f77 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -149,7 +149,6 @@ export class SelectCharacterScene extends AbstractCharacterScene { } createCurrentPlayer(): void { - console.log("CREATE CURRENT PLAYER"); for (let i = 0; i < this.playerModels.length; i++) { const playerResource = this.playerModels[i]; //check already exist texture From f755e68327542a66b035fd72d8d7c1b8c71de7b0 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 23 Mar 2022 10:12:34 +0100 Subject: [PATCH 52/61] Press Start 2P font on buttons --- front/src/Phaser/Components/Ui/Button.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Components/Ui/Button.ts b/front/src/Phaser/Components/Ui/Button.ts index 3c4f80da..02219555 100644 --- a/front/src/Phaser/Components/Ui/Button.ts +++ b/front/src/Phaser/Components/Ui/Button.ts @@ -28,7 +28,13 @@ export class Button extends Phaser.GameObjects.Container { this.config = config; this.background = this.scene.add.graphics(); - this.text = this.scene.add.text(0, 0, "", { color: "0x000000" }).setOrigin(0.5); + this.text = this.scene.add + .text(0, 0, "", { + color: "0x000000", + fontFamily: '"Press Start 2P"', + fontSize: "9px", + }) + .setOrigin(0.5, 0.45); this.drawBackground(this.config.idle); this.add([this.background, this.text]); From b1ddc2210e0bbae3c64ba8b5528efef464600e85 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Thu, 24 Mar 2022 10:30:33 +0100 Subject: [PATCH 53/61] add category icons --- .../public/resources/icons/icon_accessory.png | Bin 0 -> 1313 bytes front/public/resources/icons/icon_body.png | Bin 0 -> 1342 bytes front/public/resources/icons/icon_clothes.png | Bin 0 -> 1267 bytes front/public/resources/icons/icon_eyes.png | Bin 0 -> 1267 bytes front/public/resources/icons/icon_hair.png | Bin 0 -> 1338 bytes front/public/resources/icons/icon_hat.png | Bin 0 -> 1325 bytes .../CustomizeWoka/WokaBodyPartSlot.ts | 7 ++-- front/src/Phaser/Login/CustomizeScene.ts | 37 ++++++++++++------ 8 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 front/public/resources/icons/icon_accessory.png create mode 100644 front/public/resources/icons/icon_body.png create mode 100644 front/public/resources/icons/icon_clothes.png create mode 100644 front/public/resources/icons/icon_eyes.png create mode 100644 front/public/resources/icons/icon_hair.png create mode 100644 front/public/resources/icons/icon_hat.png diff --git a/front/public/resources/icons/icon_accessory.png b/front/public/resources/icons/icon_accessory.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca5f341ba941bbce60caec6839256e5736ca890 GIT binary patch literal 1313 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU`)((b`HpPc6ZLt$u9~nNK8(3 z$t*6&NvsSoW?;~mn4FLhOO|GKyppK57poIDp2l!g{@FekYb)G!R z+Q-Of=b*}CASGI}``7`FMf$Is&RD6MZuQ(6ci?)`vJY3g*SpL%xVo5O-IObjQWXBO zcLY!7?(&k5kO*$bdHQJ*lfZ7~6JnZ7R$M3MF()l|z9H@KMup=yM})7(1mg*{%qPM; z&UvuRI{1V6#5GSRA%FKI=eMkkdDG84YMOJztCb;{X`L$blqF$P8Zw^$X*#v&%$FV^ zR`&#rrCJrwRA0Z;kdTy^Wo*!JNM_NmVg(Bw$)o2Kj?8**Y;fb?O%wui|m2PtC zY;4!6|NpNK0|wOmGY3ADANv1)zxdz(|H};nKFhMTF;_B(DyF_OJ$~^wFw84bBRtc5 zeHpZXYz_uiMm7d!Ad8WK2}npWu!7kP3|@@VaCQ)*20~4HCIbspO%#v@ffOKSgwsq5 znBXb|7BC~&Af--nJUf6CXMsm#F#`iXF!K2m8CTdZ&tza=;?E3;D2ed(u}aR*)k{pt zPfFFR$SnZrVz8;O0y1+`OA-|-a&z*EttxDlz$&bOY>=?Nk^)#sNw%$0gl~X?bAC~( zf|;Iyo`I4bmx6+VO;JjkRgjAtR6CGotCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWv zWTXpJp<7&;SCUwvn^&w1F$89gOKNd)QD#9&W`3Rm$lS!F{L&IzB_)tWZ~$>KW+6%?4_<0f|~g2Bj9~=ahoXw=*;_umP(@5k=Sp(Gh`F2eK%- zj)08(l6+vu6=f!S=9Q!t6%+wO5p1Io)IQuQ(9J?v9-5a~Vh1w-O&F@nMjsSpNI?jR zAFwDeg6z0#^x=_h$90LdbQ>`Kk9)c}hFAzDCoB*!cyxLG#;4coC;Zttbq$YtysPn_ z)A{Y%>7S+~C$KQ7?cm^J^Z2~?^uC0f_U-eG8Vor8-2TVmv$?@jhGkRQ1fgx8gAZuc z8$LNu`oNSS&Di7X#P#V9W=Zs^9JtA{cfpd~lItX-nb&jQi@9-V#gfwud?ya*XYok1 z$tI>8$Zm*yQ@n!2%j)D@I7G?=9pyK%yY<~LFB9i+k{n;M;VGt t4)ZF68{c5?GHK>va93Wqf=TlTD?>w6QiScP88<*_+SAp~Wt~$(69CVSw;%uj literal 0 HcmV?d00001 diff --git a/front/public/resources/icons/icon_body.png b/front/public/resources/icons/icon_body.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d4983bb07ec39ecce8e086c8d445eb99e27a86 GIT binary patch literal 1342 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU`)((b`HpPc6ZLt$u9~nNK8(3 z$t*6&NvsSoW?;~mn4FLhOO|GKyppK57poIDp2l!g{@FekYb)G!R z+Q-Of=b*}CASGI}``7`FMf$Is&RD6MZuQ(6ci?)`vJY3g*SpL%xVo5O-IObjQWXBO zcLY!7?(&k5kO*$bdHQJ*lfZ7~6JnZ7R$M3MF()l|z9H@KMup=yM})7(1mg*{%qPM; z&UvuRI{1V6#5GSRA%FKI=eMkkdDG84YMOJztCb;{X`L$blqF$P8Zw^$X*#v&%$FV^ zR`&#rrCJrwRA0Z;kdTy^Wo*!JNM_NmVg(Bw$)o2Kj?8**Y;fb?O%wui|m2PtC zY;4!6|NpNK0|wOmGY3ADANv1)zxdz(|H};nKFhMTF;_B(DyF_OJ$~^wFw84bBRtc5 zeHpZXYz_uiMm7d!Ad8WK2}npWu!7kP3|@@VaCQ)*20~4HCIbspO%#v@ffOKSgwsq5 znBXb|7BC~&Af--nJUf6CXMsm#F#`iXF!K2m8CTdZ&tza=;?E3;D2ed(u}aR*)k{pt zPfFFR$SnZrVz8;O0y1+`OA-|-a&z*EttxDlz$&bOY>=?Nk^)#sNw%$0gl~X?bAC~( zf|;Iyo`I4bmx6+VO;JjkRgjAtR6CGotCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWv zWTXpJp<7&;SCUwvn^&w1F$89gOKNd)QD#9&W`3Rm$lS!F{L&IzB_)tWZ~$>KW+6%?4_<0f|~g2Bj9~=ahoXw=*;_umP(@5k=Sp(Gh`F2eK%- zj)08(l6+vu6=f!S=9Q!t6%+wO5p1Io)IQuQ(9J?v9-5a~Vh1w-O&F@nMjsSpNI?jR zAFwDeg6z0#^x=_h$90LdbQ>`KpLn`BhFAzDryO83+V`#C7xW; z*E$zw7-r+Ou;*tKdxq0cjPsNzP*Imi&1PXHq6O?bv-$!oYGy)C3d1qiGxe z2Ri+~lH|4~Ya`boljb=*OkTTYA2cy@E>Kg6h-fG^`th$_lIfwcM}@?J7KeYvT>p6= zAIh8{vFZLpp%*+8lyX+`18ryE5ntdgyovS3lLw6)#OO|GKyppK57poIDp2l!g{@FekYb)G!R z+Q-Of=b*}CASGI}``7`FMf$Is&RD6MZuQ(6ci?)`vJY3g*SpL%xVo5O-IObjQWXBO zcLY!7?(&k5kO*$bdHQJ*lfZ7~6JnZ7R$M3MF()l|z9H@KMup=yM})7(1mg*{%qPM; z&UvuRI{1V6#5GSRA%FKI=eMkkdDG84YMOJztCb;{X`L$blqF$P8Zw^$X*#v&%$FV^ zR`&#rrCJrwRA0Z;kdTy^Wo*!JNM_NmVg(Bw$)o2Kj?8**Y;fb?O%wui|m2PtC zY;4!6|NpNK0|wOmGY3ADANv1)zxdz(|H};nKFhMTF;_B(DyF_OJ$~^wFw84bBRtc5 zeHpZXYz_uiMm7d!Ad8WK2}npWu!7kP3|@@VaCQ)*20~4HCIbspO%#v@ffOKSgwsq5 znBXb|7BC~&Af--nJUf6CXMsm#F#`iXF!K2m8CTdZ&tza=;?E3;D2ed(u}aR*)k{pt zPfFFR$SnZrVz8;O0y1+`OA-|-a&z*EttxDlz$&bOY>=?Nk^)#sNw%$0gl~X?bAC~( zf|;Iyo`I4bmx6+VO;JjkRgjAtR6CGotCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWv zWTXpJp<7&;SCUwvn^&w1F$89gOKNd)QD#9&W`3Rm$lS!F{L&IzB_)tWZ~$>KW+6%?4_<0f|~g2Bj9~=ahoXw=*;_umP(@5k=Sp(Gh`F2eK%- zj)08(l6+vu6=f!S=9Q!t6%+wO5p1Io)IQuQ(9J?v9-5a~Vh1w-O&F@nMjsSpNI?jR zAFwDeg6z0#^x=_h$90LdbQ>`KXL`CghFAzDCoB*!X!??#^Yg3x%{h{GISGX+hB2=q z61vWDGOjA5A6sM#>HYmKB@S(c%)@*P$KM~Plv zW*@$pX->~39A4JP_+9V!q#R2hejA~ZcGFiy@W1X&Qx3X6$D3yA;`njxgN@xNA`&6CN literal 0 HcmV?d00001 diff --git a/front/public/resources/icons/icon_eyes.png b/front/public/resources/icons/icon_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..8d37005e0c682ef0c4ac585a45af0c31bb62a9e0 GIT binary patch literal 1267 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU`)((b`HpPc6ZLt$u9~nNK8(3 z$t*6&NvsSoW?;~mn4FLhOO|GKyppK57poIDp2l!g{@FekYb)G!R z+Q-Of=b*}CASGI}``7`FMf$Is&RD6MZuQ(6ci?)`vJY3g*SpL%xVo5O-IObjQWXBO zcLY!7?(&k5kO*$bdHQJ*lfZ7~6JnZ7R$M3MF()l|z9H@KMup=yM})7(1mg*{%qPM; z&UvuRI{1V6#5GSRA%FKI=eMkkdDG84YMOJztCb;{X`L$blqF$P8Zw^$X*#v&%$FV^ zR`&#rrCJrwRA0Z;kdTy^Wo*!JNM_NmVg(Bw$)o2Kj?8**Y;fb?O%wui|m2PtC zY;4!6|NpNK0|wOmGY3ADANv1)zxdz(|H};nKFhMTF;_B(DyF_OJ$~^wFw84bBRtc5 zeHpZXYz_uiMm7d!Ad8WK2}npWu!7kP3|@@VaCQ)*20~4HCIbspO%#v@ffOKSgwsq5 znBXb|7BC~&Af--nJUf6CXMsm#F#`iXF!K2m8CTdZ&tza=;?E3;D2ed(u}aR*)k{pt zPfFFR$SnZrVz8;O0y1+`OA-|-a&z*EttxDlz$&bOY>=?Nk^)#sNw%$0gl~X?bAC~( zf|;Iyo`I4bmx6+VO;JjkRgjAtR6CGotCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWv zWTXpJp<7&;SCUwvn^&w1F$89gOKNd)QD#9&W`3Rm$lS!F{L&IzB_)tWZ~$>KW+6%?4_<0f|~g2Bj9~=ahoXw=*;_umP(@5k=Sp(Gh`F2eK%- zj)08(l6+vu6=f!S=9Q!t6%+wO5p1Io)IQuQ(9J?v9-5a~Vh1w-O&F@nMjsSpNI?jR zAFwDeg6z0#^x=_h$90LdbQ>`KXL`CghFAzDCrEs15PZTc$UOi53%knVMD7T_NSW?k z%svJOuRk|8ZrQTNIjsNCv4!UvIhT1|xgdV*i1*^v&C(2;{@(El+cV2?(u-FI*cNa< zGGd)=wSm!K_WRgl?|E}xJ%~Gys;yWkHi0Q+w&TV|xc~#_4J(OO|GKyppK57poIDp2l!g{@FekYb)G!R z+Q-Of=b*}CASGI}``7`FMf$Is&RD6MZuQ(6ci?)`vJY3g*SpL%xVo5O-IObjQWXBO zcLY!7?(&k5kO*$bdHQJ*lfZ7~6JnZ7R$M3MF()l|z9H@KMup=yM})7(1mg*{%qPM; z&UvuRI{1V6#5GSRA%FKI=eMkkdDG84YMOJztCb;{X`L$blqF$P8Zw^$X*#v&%$FV^ zR`&#rrCJrwRA0Z;kdTy^Wo*!JNM_NmVg(Bw$)o2Kj?8**Y;fb?O%wui|m2PtC zY;4!6|NpNK0|wOmGY3ADANv1)zxdz(|H};nKFhMTF;_B(DyF_OJ$~^wFw84bBRtc5 zeHpZXYz_uiMm7d!Ad8WK2}npWu!7kP3|@@VaCQ)*20~4HCIbspO%#v@ffOKSgwsq5 znBXb|7BC~&Af--nJUf6CXMsm#F#`iXF!K2m8CTdZ&tza=;?E3;D2ed(u}aR*)k{pt zPfFFR$SnZrVz8;O0y1+`OA-|-a&z*EttxDlz$&bOY>=?Nk^)#sNw%$0gl~X?bAC~( zf|;Iyo`I4bmx6+VO;JjkRgjAtR6CGotCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWv zWTXpJp<7&;SCUwvn^&w1F$89gOKNd)QD#9&W`3Rm$lS!F{L&IzB_)tWZ~$>KW+6%?4_<0f|~g2Bj9~=ahoXw=*;_umP(@5k=Sp(Gh`F2eK%- zj)08(l6+vu6=f!S=9Q!t6%+wO5p1Io)IQuQ(9J?v9-5a~Vh1w-O&F@nMjsSpNI?jR zAFwDeg6z0#^x=_h$90LdbQ>`KA9%VrhFAzL4LZ$x$biR`V@3n#4rRq>8Tv613!M!( zv=s`opF4Qmnc2YkAmCHX-Ul-}+?0fVe81QIHsr~9wd?% zf66^kTz9zXcFGbBhD+=n85TQbvNyyl-%zS4d-S%D6 zdH&9(J2SZgdXEMPJ}B*ce%P;|`sGIdRsK_VU(`RJ9C2MxVgs|hvA=bg`nnVQ7^m4L U<;k-6cY_kUr>mdKI;Vst0Gofz2mk;8 literal 0 HcmV?d00001 diff --git a/front/public/resources/icons/icon_hat.png b/front/public/resources/icons/icon_hat.png new file mode 100644 index 0000000000000000000000000000000000000000..36ed26ae373edccd55387cd50e68474d67bf0b01 GIT binary patch literal 1325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU`)((b`HpPc6ZLt$u9~nNK8(3 z$t*6&NvsSoW?;~mn4FLhOO|GKyppK57poIDp2l!g{@FekYb)G!R z+Q-Of=b*}CASGI}``7`FMf$Is&RD6MZuQ(6ci?)`vJY3g*SpL%xVo5O-IObjQWXBO zcLY!7?(&k5kO*$bdHQJ*lfZ7~6JnZ7R$M3MF()l|z9H@KMup=yM})7(1mg*{%qPM; z&UvuRI{1V6#5GSRA%FKI=eMkkdDG84YMOJztCb;{X`L$blqF$P8Zw^$X*#v&%$FV^ zR`&#rrCJrwRA0Z;kdTy^Wo*!JNM_NmVg(Bw$)o2Kj?8**Y;fb?O%wui|m2PtC zY;4!6|NpNK0|wOmGY3ADANv1)zxdz(|H};nKFhMTF;_B(DyF_OJ$~^wFw84bBRtc5 zeHpZXYz_uiMm7d!Ad8WK2}npWu!7kP3|@@VaCQ)*20~4HCIbspO%#v@ffOKSgwsq5 znBXb|7BC~&Af--nJUf6CXMsm#F#`iXF!K2m8CTdZ&tza=;?E3;D2ed(u}aR*)k{pt zPfFFR$SnZrVz8;O0y1+`OA-|-a&z*EttxDlz$&bOY>=?Nk^)#sNw%$0gl~X?bAC~( zf|;Iyo`I4bmx6+VO;JjkRgjAtR6CGotCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWv zWTXpJp<7&;SCUwvn^&w1F$89gOKNd)QD#9&W`3Rm$lS!F{L&IzB_)tWZ~$>KW+6%?4_<0f|~g2Bj9~=ahoXw=*;_umP(@5k=Sp(Gh`F2eK%- zj)08(l6+vu6=f!S=9Q!t6%+wO5p1Io)IQuQ(9J?v9-5a~Vh1w-O&F@nMjsSpNI?jR zAFwDeg6z0#^x=_h$90LdbQ>`KFMGN;hFAzDCoB*!IP@hw=jT`XV{XgOuT60kPqNrL zLvYRX){tI<$u2xK2gEid7|Sl}keJ2!Oe9HU@#@OrL~do_T?&SOs;AdJJavF=!eg!p zZxw=16fxeZD&lb{vpWGyIt+zplKwyT;Z~@ekv8MAW0FfX<1)!5oRSN6Jb2E%^ZzBe zsuQ0i-l$G|E@^XW{-LiM6|AfOJ4v=Jx7JI{a1y(*U*PKMo)c^bM9f~EI50z__Pzy!ikC literal 0 HcmV?d00001 diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 4370f1d8..93ceb1c4 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -36,8 +36,6 @@ export class WokaBodyPartSlot extends GridItem { this.config = config; - const offsetY = -3; - const offsetX = -2; this.selected = this.config.selected ?? false; this.background = this.scene.add.graphics(); @@ -48,16 +46,17 @@ export class WokaBodyPartSlot extends GridItem { this.categoryImage = this.scene.add .image(this.SIZE / 2 - 1, -this.SIZE / 2 + 1, this.config.categoryImageKey) .setDisplaySize(16, 16) + .setAlpha(0.4) .setOrigin(1, 0); this.add(this.categoryImage); } this.bodyImage = this.scene.add - .image(offsetX, offsetY, config.bodyImageKey ?? "") + .image(this.config.offsetX, this.config.offsetY, config.bodyImageKey ?? "") .setVisible(config.imageKey !== undefined); this.image = this.scene.add - .image(offsetX, offsetY, config.imageKey ?? "") + .image(this.config.offsetX, this.config.offsetY, config.imageKey ?? "") .setVisible(config.bodyImageKey !== undefined); this.setSize(this.SIZE, this.SIZE); diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index eeb81933..e98f849e 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -55,6 +55,14 @@ export class CustomizeScene extends AbstractCharacterScene { public preload(): void { this.input.dragDistanceThreshold = 10; + + this.load.image("iconClothes", "/resources/icons/icon_clothes.png"); + this.load.image("iconAccessory", "/resources/icons/icon_accessory.png"); + this.load.image("iconHat", "/resources/icons/icon_hat.png"); + this.load.image("iconHair", "/resources/icons/icon_hair.png"); + this.load.image("iconEyes", "/resources/icons/icon_eyes.png"); + this.load.image("iconBody", "/resources/icons/icon_body.png"); + const wokaMetadataKey = "woka-list"; this.cache.json.remove(wokaMetadataKey); // FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!) @@ -137,27 +145,27 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsSlots = { [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, { ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconTalk", + categoryImageKey: "iconHair", }), [CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 0, 0, { ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconTalk", + categoryImageKey: "iconBody", }), [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 0, 0, { ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconTalk", + categoryImageKey: "iconAccessory", }), [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 0, 0, { ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconTalk", + categoryImageKey: "iconHat", }), [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 0, 0, { ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconTalk", + categoryImageKey: "iconClothes", }), [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, { ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconTalk", + categoryImageKey: "iconEyes", }), }; @@ -417,7 +425,7 @@ export class CustomizeScene extends AbstractCharacterScene { borderColor: 0xadafbc, borderSelectedColor: 0x209cee, offsetX: -4, - offsetY: -3, + offsetY: 2, }; } @@ -492,10 +500,17 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGrid.clearAllItems(); for (let i = 0; i < bodyPartsLayer.length; i += 1) { - const slot = new WokaBodyPartSlot(this, 0, 0, this.getDefaultWokaBodyPartSlotConfig(), i).setDisplaySize( - slotDimension, - slotDimension - ); + const slot = new WokaBodyPartSlot( + this, + 0, + 0, + { + ...this.getDefaultWokaBodyPartSlotConfig(), + offsetX: 0, + offsetY: 0, + }, + i + ).setDisplaySize(slotDimension, slotDimension); if (this.selectedBodyPartType === CustomWokaBodyPart.Body) { slot.setBodyTexture(bodyPartsLayer[i].id); slot.setImageTexture(); From 81c5876a6696409301c4c3494825ed489f5cb51d Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Mon, 28 Mar 2022 12:29:31 +0200 Subject: [PATCH 54/61] grid shadow and animations --- .../CustomizeWoka/WokaBodyPartSlot.ts | 14 +++-- front/src/Phaser/Entity/Character.ts | 1 - front/src/Phaser/Helpers/TexturesHelper.ts | 12 ++++ front/src/Phaser/Login/CustomizeScene.ts | 61 ++++++++++++++----- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 93ceb1c4..54209225 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -29,7 +29,7 @@ export class WokaBodyPartSlot extends GridItem { private selected: boolean; - public readonly SIZE: number = 50; + public static readonly SIZE: number = 50; constructor(scene: Phaser.Scene, x: number, y: number, config: WokaBodyPartSlotConfig, id?: number) { super(scene, `${id}`, { x, y }); @@ -44,9 +44,9 @@ export class WokaBodyPartSlot extends GridItem { if (this.config.categoryImageKey) { this.categoryImage = this.scene.add - .image(this.SIZE / 2 - 1, -this.SIZE / 2 + 1, this.config.categoryImageKey) + .image(WokaBodyPartSlot.SIZE / 2 - 1, -WokaBodyPartSlot.SIZE / 2 + 1, this.config.categoryImageKey) .setDisplaySize(16, 16) - .setAlpha(0.4) + .setAlpha(0.75) .setOrigin(1, 0); this.add(this.categoryImage); } @@ -59,7 +59,7 @@ export class WokaBodyPartSlot extends GridItem { .image(this.config.offsetX, this.config.offsetY, config.imageKey ?? "") .setVisible(config.bodyImageKey !== undefined); - this.setSize(this.SIZE, this.SIZE); + this.setSize(WokaBodyPartSlot.SIZE, WokaBodyPartSlot.SIZE); this.add([this.bodyImage, this.image]); @@ -118,8 +118,10 @@ export class WokaBodyPartSlot extends GridItem { this.selected ? this.config.borderSelectedColor : this.config.borderColor ); - this.background.fillRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); - this.background.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); + const size = WokaBodyPartSlot.SIZE; + + this.background.fillRect(-size / 2, -size / 2, size, size); + this.background.strokeRect(-size / 2, -size / 2, size, size); } private updateSelected(): void { diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index c06ae443..fab18ce1 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -255,7 +255,6 @@ export abstract class Character extends Container implements OutlineableInterfac } const sprite = new Sprite(this.scene, 0, 0, texture, frame); this.add(sprite); - console.log(texture); getPlayerAnimations(texture).forEach((d) => { this.scene.anims.create({ key: d.key, diff --git a/front/src/Phaser/Helpers/TexturesHelper.ts b/front/src/Phaser/Helpers/TexturesHelper.ts index 348e957a..ee1b1082 100644 --- a/front/src/Phaser/Helpers/TexturesHelper.ts +++ b/front/src/Phaser/Helpers/TexturesHelper.ts @@ -31,4 +31,16 @@ export class TexturesHelper { throw new Error("Could not get the snapshot"); } } + + public static createRectangleTexture( + scene: Phaser.Scene, + textureKey: string, + width: number, + height: number, + color: number + ): void { + const rectangleTexture = scene.add.graphics().fillStyle(color, 1).fillRect(0, 0, width, height); + rectangleTexture.generateTexture(textureKey, width, height); + rectangleTexture.destroy(); + } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index e9354cb1..ae7999f0 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -26,6 +26,7 @@ import { import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/DraggableGrid"; import { Button } from "../Components/Ui/Button"; import { wokaList } from "../../Messages/JsonMessages/PlayerTextures"; +import { TexturesHelper } from "../Helpers/TexturesHelper"; export const CustomizeSceneName = "CustomizeScene"; @@ -33,6 +34,8 @@ export class CustomizeScene extends AbstractCharacterScene { private customWokaPreviewer!: CustomWokaPreviewer; private bodyPartsDraggableGridBackground!: Phaser.GameObjects.Graphics; private bodyPartsDraggableGridForeground!: Phaser.GameObjects.Graphics; + private bodyPartsDraggableGridLeftShadow!: Phaser.GameObjects.Image; + private bodyPartsDraggableGridRightShadow!: Phaser.GameObjects.Image; private bodyPartsDraggableGrid!: DraggableGrid; private bodyPartsSlots!: Record; @@ -47,6 +50,8 @@ export class CustomizeScene extends AbstractCharacterScene { private loader: Loader; + private readonly SLOT_DIMENSION = 100; + constructor() { super({ key: CustomizeSceneName, @@ -64,6 +69,8 @@ export class CustomizeScene extends AbstractCharacterScene { this.load.image("iconEyes", "/resources/icons/icon_eyes.png"); this.load.image("iconBody", "/resources/icons/icon_body.png"); + TexturesHelper.createRectangleTexture(this, "gridEdgeShadow", 200, 115, 0x000000); + const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href; this.cache.json.remove(wokaMetadataKey); this.superLoad @@ -91,6 +98,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public create(): void { + this.selectedBodyPartType = CustomWokaBodyPart.Body; this.customWokaPreviewer = new CustomWokaPreviewer( this, 0, @@ -118,8 +126,8 @@ export class CustomizeScene extends AbstractCharacterScene { repositionToCenter: true, itemsInRow: 1, margin: { - left: 5, - right: 5, + left: (innerWidth / waScaleManager.getActualZoom() - this.SLOT_DIMENSION) * 0.5, + right: (innerWidth / waScaleManager.getActualZoom() - this.SLOT_DIMENSION) * 0.5, }, spacing: 5, debug: { @@ -128,6 +136,21 @@ export class CustomizeScene extends AbstractCharacterScene { }); this.bodyPartsDraggableGridForeground = this.add.graphics(); + this.bodyPartsDraggableGridLeftShadow = this.add + .image(0, this.cameras.main.worldView.y + this.cameras.main.height, "gridEdgeShadow") + .setAlpha(1, 0, 1, 0) + .setOrigin(0, 1); + + this.bodyPartsDraggableGridRightShadow = this.add + .image( + this.cameras.main.worldView.x + this.cameras.main.width, + this.cameras.main.worldView.y + this.cameras.main.height, + "gridEdgeShadow" + ) + .setAlpha(1, 0, 1, 0) + .setFlipX(true) + .setOrigin(1, 1); + this.bodyPartsSlots = { [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, { ...this.getDefaultWokaBodyPartSlotConfig(), @@ -319,11 +342,11 @@ export class CustomizeScene extends AbstractCharacterScene { ); const bottom = Math.floor(top + slotSize + 10); - this.bodyPartsSlots.Hair.setPosition(left, top); - this.bodyPartsSlots.Hat.setPosition(middle, top); - this.bodyPartsSlots.Eyes.setPosition(right, top); - this.bodyPartsSlots.Body.setPosition(left, bottom); - this.bodyPartsSlots.Clothes.setPosition(middle, bottom); + this.bodyPartsSlots.Body.setPosition(left, top); + this.bodyPartsSlots.Eyes.setPosition(middle, top); + this.bodyPartsSlots.Hair.setPosition(right, top); + this.bodyPartsSlots.Clothes.setPosition(left, bottom); + this.bodyPartsSlots.Hat.setPosition(middle, bottom); this.bodyPartsSlots.Accessory.setPosition(right, bottom); return; @@ -341,12 +364,12 @@ export class CustomizeScene extends AbstractCharacterScene { const middle = Math.floor(top + slotSize + 10); const bottom = Math.floor(middle + slotSize + 10); - this.bodyPartsSlots.Hair.setPosition(left, top); - this.bodyPartsSlots.Body.setPosition(left, middle); - this.bodyPartsSlots.Accessory.setPosition(ratio < 0.6 ? leftEdge : left, ratio < 0.6 ? middle : bottom); - this.bodyPartsSlots.Hat.setPosition(right, top); - this.bodyPartsSlots.Clothes.setPosition(right, middle); - this.bodyPartsSlots.Eyes.setPosition(ratio < 0.6 ? rightEdge : right, ratio < 0.6 ? middle : bottom); + this.bodyPartsSlots.Body.setPosition(left, top); + this.bodyPartsSlots.Eyes.setPosition(left, middle); + this.bodyPartsSlots.Hair.setPosition(ratio < 0.6 ? leftEdge : left, ratio < 0.6 ? middle : bottom); + this.bodyPartsSlots.Clothes.setPosition(right, top); + this.bodyPartsSlots.Hat.setPosition(right, middle); + this.bodyPartsSlots.Accessory.setPosition(ratio < 0.6 ? rightEdge : right, ratio < 0.6 ? middle : bottom); } private handleBodyPartsDraggableGridOnResize(): void { @@ -357,6 +380,12 @@ export class CustomizeScene extends AbstractCharacterScene { y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5, }; + this.bodyPartsDraggableGridLeftShadow.setPosition(0, this.cameras.main.worldView.y + this.cameras.main.height); + this.bodyPartsDraggableGridRightShadow.setPosition( + this.cameras.main.worldView.x + this.cameras.main.width, + this.cameras.main.worldView.y + this.cameras.main.height + ); + this.drawGridBackground(gridPos); this.drawGridForeground(gridPos); try { @@ -371,6 +400,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.populateGrid(); this.bodyPartsDraggableGrid.moveContentToBeginning(); + void this.bodyPartsDraggableGrid.moveContentTo(0.5, 500); } private handleRandomizeButtonOnResize(): void { @@ -456,6 +486,7 @@ export class CustomizeScene extends AbstractCharacterScene { } this.bodyPartsDraggableGrid.on(DraggableGridEvent.ItemClicked, (item: WokaBodyPartSlot) => { + void this.bodyPartsDraggableGrid.centerOnItem(this.bodyPartsDraggableGrid.getAllItems().indexOf(item), 500); this.bodyPartsDraggableGrid.getAllItems().forEach((slot) => (slot as WokaBodyPartSlot).select(false)); this.changeOutfitPart(Number(item.getId())); this.refreshPlayerCurrentOutfit(); @@ -480,7 +511,6 @@ export class CustomizeScene extends AbstractCharacterScene { if (this.selectedBodyPartType === undefined) { return; } - const slotDimension = 100; const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[this.selectedBodyPartType]]; @@ -496,7 +526,7 @@ export class CustomizeScene extends AbstractCharacterScene { offsetY: 0, }, i - ).setDisplaySize(slotDimension, slotDimension); + ).setDisplaySize(this.SLOT_DIMENSION, this.SLOT_DIMENSION); if (this.selectedBodyPartType === CustomWokaBodyPart.Body) { slot.setBodyTexture(bodyPartsLayer[i].id); slot.setImageTexture(); @@ -509,6 +539,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGrid.addItem(slot); } this.bodyPartsDraggableGrid.moveContentToBeginning(); + void this.bodyPartsDraggableGrid.moveContentTo(0.5, 500); } private clearGrid(): void { From 7afa53840e2194333e58bc77079147afaa34dc06 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Mon, 28 Mar 2022 14:55:20 +0200 Subject: [PATCH 55/61] update dependencies --- front/package.json | 4 ++-- front/yarn.lock | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/front/package.json b/front/package.json index c2e7a7b9..43b8c5f9 100644 --- a/front/package.json +++ b/front/package.json @@ -5,7 +5,7 @@ "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { "@geprog/vite-plugin-env-config": "^4.0.0", - "@home-based-studio/phaser3-utils": "0.3.2", + "@home-based-studio/phaser3-utils": "^0.4.2", "@sveltejs/vite-plugin-svelte": "^1.0.0-next.36", "@tsconfig/svelte": "^1.0.10", "@types/google-protobuf": "^3.7.3", @@ -46,7 +46,7 @@ "easystarjs": "^0.4.4", "generic-type-guard": "^3.4.2", "google-protobuf": "^3.13.0", - "phaser": "^3.54.0", + "phaser": "3.55.1", "phaser-animated-tiles": "workadventure/phaser-animated-tiles#da68bbededd605925621dd4f03bd27e69284b254", "phaser3-rex-plugins": "^1.1.42", "posthog-js": "^1.14.1", diff --git a/front/yarn.lock b/front/yarn.lock index d44a8f2f..d8167874 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -77,10 +77,10 @@ resolved "https://registry.yarnpkg.com/@geprog/vite-plugin-env-config/-/vite-plugin-env-config-4.0.0.tgz#989d95f23fbab5eae7c4c96d04a18abdc289b81e" integrity sha512-25ZMNdpssqkyv1sxfa6gBhmL8yCxCqjRRc1c05GJfhPkqD6Cn9dnG6xnHHHfJaEqrDFCViD0Bcnr+tgs76OZ2Q== -"@home-based-studio/phaser3-utils@0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.3.2.tgz#2240819473fbdb66123cee4b37b3d18be03d1dbe" - integrity sha512-qbv2H2IOSCyG+8hzBsZHPx4HWvlH1G4AfXuc3DyorI4QQMlvcVq5sZIjVaz6RIzeC7czivXOWsW5bApVhGa55A== +"@home-based-studio/phaser3-utils@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@home-based-studio/phaser3-utils/-/phaser3-utils-0.4.2.tgz#b2c1815a6b51321ea8dab027b5badcf714d99fd6" + integrity sha512-S0VkAq3z0Kf0vEUUyCDes911icvc+nkUq7lGp23zD/5lk7LTGM51NswSAfel7Rm/DLY8IBxvDTBJADTf/De82w== dependencies: phaser "3.55.1" @@ -2176,14 +2176,6 @@ phaser@3.55.1: eventemitter3 "^4.0.7" path "^0.12.7" -phaser@^3.54.0: - version "3.54.0" - resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.54.0.tgz#46b191e46059aab2a9a57f78525c60b595767eee" - integrity sha512-/1XVI6J2siS0OGwJez7vLbRjars1zb//EvJdYMVyd3wNTUf5DHrvYUj1f6TsEISr4vjnbrNtS66QIuPbGU8x6A== - dependencies: - eventemitter3 "^4.0.7" - path "^0.12.7" - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" From 4c93060f852106af3f9d7e17c909ebe7bbaefe90 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 30 Mar 2022 11:16:58 +0200 Subject: [PATCH 56/61] select and center on currently selected item --- .../CustomizeWoka/WokaBodyPartSlot.ts | 15 ++++--- front/src/Phaser/Login/CustomizeScene.ts | 45 ++++++++++++++++--- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 54209225..3db2962e 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -1,6 +1,5 @@ import { GridItem } from "@home-based-studio/phaser3-utils"; import { GridItemEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/GridItem"; -import { MathUtils } from "../../../Utils/MathUtils"; export interface WokaBodyPartSlotConfig { color: number; @@ -52,12 +51,12 @@ export class WokaBodyPartSlot extends GridItem { } this.bodyImage = this.scene.add - .image(this.config.offsetX, this.config.offsetY, config.bodyImageKey ?? "") - .setVisible(config.imageKey !== undefined); + .image(this.config.offsetX, this.config.offsetY, this.config.bodyImageKey ?? "") + .setVisible(this.config.imageKey !== undefined); this.image = this.scene.add - .image(this.config.offsetX, this.config.offsetY, config.imageKey ?? "") - .setVisible(config.bodyImageKey !== undefined); + .image(this.config.offsetX, this.config.offsetY, this.config.imageKey ?? "") + .setVisible(this.config.bodyImageKey !== undefined); this.setSize(WokaBodyPartSlot.SIZE, WokaBodyPartSlot.SIZE); @@ -71,6 +70,10 @@ export class WokaBodyPartSlot extends GridItem { this.scene.add.existing(this); } + public getContentData(): { bodyImageKey?: string; key?: string } { + return { bodyImageKey: this.config.bodyImageKey, key: this.config.imageKey }; + } + public setTextures(bodyTextureKey?: string, imageTextureKey?: string): void { this.setBodyTexture(bodyTextureKey); this.setImageTexture(imageTextureKey); @@ -80,6 +83,7 @@ export class WokaBodyPartSlot extends GridItem { this.bodyImage.setVisible(textureKey !== undefined && textureKey !== ""); if (textureKey) { this.bodyImage.setTexture(textureKey, frame); + this.config.bodyImageKey = textureKey; } } @@ -87,6 +91,7 @@ export class WokaBodyPartSlot extends GridItem { this.image.setVisible(textureKey !== undefined && textureKey !== ""); if (textureKey) { this.image.setTexture(textureKey, frame); + this.config.imageKey = textureKey; } } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index ae7999f0..e48ce89c 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -45,6 +45,7 @@ export class CustomizeScene extends AbstractCharacterScene { private selectedLayers: number[] = [0, 0, 0, 0, 0, 0]; private layers: BodyResourceDescriptionInterface[][] = []; private selectedBodyPartType?: CustomWokaBodyPart; + private selectedItemTextureKey?: string; protected lazyloadingAttempt = true; //permit to update texture loaded after renderer @@ -98,7 +99,6 @@ export class CustomizeScene extends AbstractCharacterScene { } public create(): void { - this.selectedBodyPartType = CustomWokaBodyPart.Body; this.customWokaPreviewer = new CustomWokaPreviewer( this, 0, @@ -178,6 +178,10 @@ export class CustomizeScene extends AbstractCharacterScene { }), }; + this.selectedBodyPartType = CustomWokaBodyPart.Body; + this.selectedItemTextureKey = this.layers[CustomWokaBodyPartOrder.Body][0].id; + this.bodyPartsSlots.Body.select(); + this.initializeRandomizeButton(); this.initializeFinishButton(); @@ -399,8 +403,10 @@ export class CustomizeScene extends AbstractCharacterScene { } this.populateGrid(); - this.bodyPartsDraggableGrid.moveContentToBeginning(); - void this.bodyPartsDraggableGrid.moveContentTo(0.5, 500); + const selectedGridItem = this.selectGridItem(); + if (selectedGridItem) { + this.centerGridOnItem(selectedGridItem); + } } private handleRandomizeButtonOnResize(): void { @@ -474,9 +480,19 @@ export class CustomizeScene extends AbstractCharacterScene { slot.on(WokaBodyPartSlotEvent.Clicked, (selected: boolean) => { if (!selected) { this.selectedBodyPartType = bodyPart as CustomWokaBodyPart; + this.selectedItemTextureKey = slot.getContentData().key ?? slot.getContentData().bodyImageKey; this.deselectAllSlots(); slot.select(true); this.populateGrid(); + if (!this.selectedItemTextureKey) { + return; + } + const selectedGridItem = this.selectGridItem(); + if (!selectedGridItem) { + return; + } + this.bodyPartsDraggableGrid.moveContentToBeginning(); + this.centerGridOnItem(selectedGridItem); } else { this.selectedBodyPartType = undefined; slot.select(false); @@ -494,6 +510,25 @@ export class CustomizeScene extends AbstractCharacterScene { }); } + private selectGridItem(): WokaBodyPartSlot | undefined { + const items = this.bodyPartsDraggableGrid.getAllItems() as WokaBodyPartSlot[]; + let item: WokaBodyPartSlot | undefined; + if (this.selectedBodyPartType === CustomWokaBodyPart.Body) { + item = items.find((item) => item.getContentData().bodyImageKey === this.selectedItemTextureKey); + } else { + item = items.find((item) => item.getContentData().key === this.selectedItemTextureKey); + } + item?.select(); + return item; + } + + private centerGridOnItem(item: WokaBodyPartSlot, duration: number = 500): void { + void this.bodyPartsDraggableGrid.centerOnItem( + this.bodyPartsDraggableGrid.getAllItems().indexOf(item), + duration + ); + } + private randomizeOutfit(): void { for (let i = 0; i < 6; i += 1) { this.selectedLayers[i] = Math.floor(Math.random() * this.layers[i].length); @@ -514,7 +549,7 @@ export class CustomizeScene extends AbstractCharacterScene { const bodyPartsLayer = this.layers[CustomWokaBodyPartOrder[this.selectedBodyPartType]]; - this.bodyPartsDraggableGrid.clearAllItems(); + this.clearGrid(); for (let i = 0; i < bodyPartsLayer.length; i += 1) { const slot = new WokaBodyPartSlot( this, @@ -538,8 +573,6 @@ export class CustomizeScene extends AbstractCharacterScene { } this.bodyPartsDraggableGrid.addItem(slot); } - this.bodyPartsDraggableGrid.moveContentToBeginning(); - void this.bodyPartsDraggableGrid.moveContentTo(0.5, 500); } private clearGrid(): void { From d0ad5f8299a07af289ee92bccf4ad6a825491c77 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 30 Mar 2022 13:02:02 +0200 Subject: [PATCH 57/61] custom background for slots --- .../public/resources/tilesets/floor_tiles.png | Bin 0 -> 19880 bytes .../CustomizeWoka/CustomWokaPreviewer.ts | 20 +++---- .../CustomizeWoka/WokaBodyPartSlot.ts | 22 ++++---- front/src/Phaser/Helpers/TexturesHelper.ts | 24 +++++++++ front/src/Phaser/Login/CustomizeScene.ts | 49 ++---------------- front/src/Phaser/Login/EntryScene.ts | 4 +- 6 files changed, 52 insertions(+), 67 deletions(-) create mode 100644 front/public/resources/tilesets/floor_tiles.png diff --git a/front/public/resources/tilesets/floor_tiles.png b/front/public/resources/tilesets/floor_tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..0cdc7fde031f1dd3dd6f31913a9dee2999956432 GIT binary patch literal 19880 zcmeFYWmH_5EDZQ$!S>{F`Xw zf+lQTH@3Dbq)fcTH@GsrpEfpzP3L+Cb+qBM#n2xCPA)ELt_hegX>W8x5e@lHhdVll zR|Q4OwA1b14?(OtR%z&`Hnd^WKOzQolZ28njPYO{h9L{Y*7`u-dE;*D+^rvc`^f_Z ziFv`q$^70k$J6^8JO3G0bk#@8f&Bpx4srZl!g0l)S~0+6A(etD?M z>~KJRKvj{K&gjcSmDAc*m3Bpymy&>5zXC1;Oq3n&RGC##|=g^$e_F>M`hN-6cr z8SW*1UpY~MS{wfZ(dA+w>fmL_=iW2>AALKad2bSeYH$~x@F4m+M2q&?*N1Dq^Fahp z_M}YzbeIjwP@b`2X7+}@a)82JU{+j(V(|#vi0RPDP5SEV*S~@zpYkqTW^+(s!ecS@+4(TC;v*VCU9q4sd5FBN7TmS&nf!{BP z6h>4+0Dy1ITLb8-X5#T5b42Fq+T0G`X$IaaAe{LY~-+izT;Pez^Ux=RTU z?jDf8Q0ScOHRWzOscZ@>Fxp$_4$e9-%b`a>H7PH)~_#2{#xfa&zm+XSebmjApiU# zh@<5|?T^>F-U~CjE^_AqCHBIP_3$Jea7k#Gd@pidTa)H{8VYmY;nwhZCFR~v`-Iee z@YzIN-2W}_(4xfzSHv}xRm%YqlSO7nFx2)d6l^3Y~hfm z1@Gx5taeSANB(VP!QGxkUASM=!*(odK^Qhe2lvuikMdKTkJD}Y4@3L*k+8Jwl(h>J zr4r+-g@CO|(zgdh8J#Z8&p^i0QJda2lpop8dz!Wpf=2Gir;q_p_fG-K3q7bM9b+kQ zC+?kbcwhZ&72MyPO2ZkZ-#x7KrCi?wQX_0=?T298d(3eYM<;O3qFt&$-~a(q4ZjHg zIv5t}&#eli5?^eUuG@Le=uf-nXrCEKYuT2S#+%C1mnK?U zm+!}el=$}(Vf?#7F%=lvlGPL#yTS=)IF3&Fh}&H(h?s@$fL5 z`gKnEfp6wZ(FcL$1FXvor;-edjJYwMUq2a{98GsMwG8a8fAEwvZ7T_$H5}V_kiPp7 zilr=YpRBGd_%vL@f7yJ^8t{DEvDfWBzMeCKG#Y2HU0v+!7mAfl^#fmUuLv|8?G6aJ zS?1I;_^@Vq++@-0)k)!X%30jp8YL(=(EP3HL>A?8fY^=|>_&>V z@oi*N*edHtybOAX=m>Q@mGg43Emyze7=&Y_geu3n*F9=2n2W--(~1bs-%k=-Dm<*8 zZeag|i$c#(FW04!AQx$$8$r>nF}B;4WGQtb3ck>dve>aDx-@SW`P=9+sH7oBZ*KCN z#c7S>3(H)$ITK~8ru>(U{jbq(I>jwoKYteBXiKAo2v&t_OP_W&vS^~!A;diBs#{%X z%&*l7x4oeOY3Bv&v6$L?Agi(aig9%-zh^vM=~c0L+=4)Hv`xQE=%rbj)OequbUtUeCouXl$>V+^U_8mvuky`R z?TpgpaCW9Cf2y&%#om%K1TmTdp%4p)a&wUhcKV|(TfEv*>3sA$AFS4}_)sROiiY?* ztrPuEe&3rFj#V5+LF^%9C#^>SFMiBZ{qkc!R^twy_6CbkL2XqS7_>&KbxPcCKmCb? z6N{BW_^i9xfbwI(q{aJK2V&0+0Ve%RACveR)0N@#!8n6SOa$G6?HeEExXGLDg`QBj z?k|d-?w|9d9mB#*bd#j0T2uWI?SUx%yDm3&v-+)`+vKCbFwI)}0d9TD(^QgSATvrf zkBOBN;5J9Gth98XpSm;8!3D{7pbzoF;neG+J|J&40;hR+y99i}@NN(aO425P0;$&m z(pD&pq;E0fNPzO^)Z!@(ur>(mqG-8-3p&YTe<-8(*f$&Q{}N&f&iP`m%t= z^I+DZZNFIl7KmySRZsO{X1oh1aDx+Fb%*Der1ukm;|ggyAYYHym(f}qJ$nEcb2}GY zx)BMak2fOz1QL9rN;EJK5B2DqZt*Sb@*OY7;TZ=fhjJFXfeH? z0_pnz(*ELb1KH+rciids)_{&|^!#-kq=}bL`s*SKzD9=;8JIRijxFYtKy%m6)scX&go709WkT3r-J(( z_PNJd1AbZWgb4uk9^EO7&ujY{&)XJw7y`!Oaa@oL>QM_(;NvI7Wn=_86&<3)bLarK zF2Ef;_7vz4VFrRZGK9E-+HPGWq*wL#%S#ky$#Q}C(PW1mksEzMbGNDU`aLdklVncc zZ7@4^Mp-43`?1o|YFgz{``!sI!sB!jk|Q&)!fE>%A==tQEOrjZHDI~d5&aB$A9wd7 za*As7+IkrkYolE2=LS4P!!9&0epU}Ue850vqmYLe$~CO?JuPu1bCfN`$jbQgO`yX|BLp8ARt#Oquje`VMe?+oqI0ePfI5)nISF`+Pce8w$RB3u_y{dzI2PZU9f z6p;9kfhIcf4EltaMQ?%lJw}%!y}_wlU~0n7E*wvKsXE3+#ID1u4v8zC!?HuTtj1>otg;Iq%#8+H>^ ziyFq@4ZJ86zk1E@?jK5RK5adr9q&h;5w^lunl_7~_`el-8#B1FfL?xbjgLkTj+WkC zaIx$30nM}y$F2#l)5aR)0$N2EtWyb)^&mLfJ5dW9&zmRU0@YI@)NZWq_*Dk`E^v{Q zg`hc2Qk(M?CQh)GCk*UQW73Q;(h6+pxH{jm&J&6_!*ddbBd(c}bSCVw6sO^w6C2au zkTh%=bmB>}SCQ96l?{qgd|5RZC>#2MHID|3ZS*q@!od{t#KL?ThLh6HGHm65O ze>o|#%fg0T4V@8X#6-m~M#0aM(B5_@Z;U?~PH#JfU*M|Sgl?IwK=_Q92HC@GAHbvx zmF(_OE`uIIfpdjyD>~g&D=Le>@Ep6eA1tB=&qX1#xrvVhgWFwCIxhAh5W6m~`m6f) z8_xBfsmp=>`h#%gENI+_P{b*uj6Tfk1Y?9d)>;;!P1rKm7H_pLRywk}Cp%k_)J%~vuvYcv&+B+_9Jh#m{FT9Q72WB&OBzg1U!Eg2KvYlgLC<`Dl z@ov=^+2!|%N=%StD6_(2ECIc?8q&cm;dY(*t`Vnv5Nlujqx2Z72i;GKd#2+*L-!`4 z3xzBOzt{ZS2+uU*!{F@0X=^@CEY$3m4s#!pUe%XYut#tn2zq(*g2=f0&yGK$JmTXium0j!7^T| zs-!=EDMNo_0p>UwVS6ck8Yg-aiKeds_ac(!z#4Zh(b@a!xgrfF2&^*UZ9Tk!4grK$ z7J=mJwP==mG>fAt#Frh?u?2jOcf=UVLL9O}9|OQGMhR|zxtBxC_c|7nx+5E5P7j35E^v`I!(AU$jgq zaHld-duN+x+UVDTVCygTsARk+(s-+n zG*t9gIIeKAzU1z6E%^&*$Z=MpmArXd8u@!L0ln-t}@)07zsW?$(2bqk}l*pCj zf(S(9&N4Wi(z_q4Fmq9PEtpo3O~Q7iXG1^%gho9)j%^v)`w%n3N(UjihMDL?{O5wwUKAv48Z?@ z)*qxCAIRQrDzXDXbQP>6I~eN!^D=fyV#zZXXEjcak4q*w7%bCwu_!%Yw}0*!6HjY~ z(KBw#1pmTO4$xE2k1VZt)Q? zwV!P&wggmHMRmt>w-S$SW-Tuqf)6`yFXt*x=inOFIp6B>ioJa#jY2PC3=SK~ME8gc z50gcz57hCxLr(mX(I*J(fQ}Ci*@FZibmb&73QG;+V2fk-&5I2tv8udXJir?A0 z1*NFG-NB}jEeplO7@N-H2f+Jj1mb90tmeByNki6Q_#{U*P^95i&0=mW>!g-(Z9_yC-E0Mt$m#LiHBP^{lqzOif%P zxa6YmOv{Nd97Xx#;RSn}Vc(GI8~Zp?eL{Tp6?O+%klDsipl`Myg z(88P{=7zRKP997G10spOrp`A&5$%+? zyHFC+?|y$vL@hI+NRo}Y)C=*=;vTUz811rLMy~qjHT@YU%UAX@^^t`}-&|B&{|`5=l@|^RnD{(Rk}(fx?tP zn3F*AiKmCfy|jI4kDtN*5fxe0J>LkB+$E(G_{!d`7lbb=W{3{?QRSblz0}ZbSsp0E zxRfuHY0Ltlw8Ljc&O)rmeUJ!GOIwsXXeb~kbBQZ4MEKVDH z#u1;wZ>y2Jk)Uxz;GpcI?D(tV-DKULlYpWy8K24GWswlN|x$fW(iY>C&h z)awuA09M2KvQg(kJo-A=j;;s3`IHt#&j70DE56~xBH{gv{ z)aqTs54`4IN<7|m!4^NqM!liULZK^|iSF`0fg~BUkl2;UCbY}kY@0@Xtkj7|oK>5t zw4ff@^SYO#W-^Sz&OayVPaFoNK@+1P493E@#TxS^d`Yp>%Dkot{pJ6YaRjm6>l7Zb zvfVt*>B0$s9+!gV<6p(VM)}TN{>u9h;$D)37)#y24>ztbzBmv3k#)zcwQy|u!F6um zG6rQVVus6kNWzJwju0#jplW3K3*z_RN;c9Q28nMkNm2Vs?Y2+ zp(&Oh2lBNcY6q#>;6g~c575FbHM&i>`>;Asog>qd#W`pxZ0#6mS>}e|LGcebD}i%P zqf>}ZhIhKkdA`jY*{nVvY9#qrtJtSY>4v+Ovfv?fDu~=~B6AK7gvlVa*5Ax3bJmI8 zO*&O!+~(-WEyBQt28S*7+OEbt`x@XstInCA_Ymw$eTA%}H3Y!|E07z}FJ&QXUR--I`pl+1Lbg5c{7yCpD)K+Vz zlbPq{Sp-S_*hY%iZ#|=nKN#1|pC(53ti#BV6=ZT!w9N#fPI#&=ohRGzv(_q3^?#uW z$EzXb0h(nONllKs=B9Os6v1-ZoMG(u_LIMtJeXZ@8MBaf9<`$Q(Skl3X?zCp9M9afEnr6h5(-^hH#P+^ShT2jm^Eb&xI#VxxTBO#J+=5iBZ zM@Y;2Ay+yNQ?JGPU21E#Mxx25{DC?ncSC%Asjiujok!W>{Jsd)N#+*o!pJO|k05Tz zpsK#7Y#0H>ond>7YWxyIQ!GtsO8P2t>rOH4zJ@uuo*ypiOwiA7=$`W%E_I2UFPdSxwh0zs`!bO~ad-VE1oFY>oQD zAqN!`t}+fjCo-Z`XkC0Ph>;nHA8&#Z4SxWlR4T7@F( zS`v{ZyHlqq|y=)Ay-^ENx=FuvlyW7zz%-j3~G_O!L0XDMO99ym1 z^NOevlDw6$1{Ui_e1WqRE=i6Y#sQB>Xi^jCJBSHlca{l!BM{`-wEmp$+bR^yEM;HB zEPRI`FYNK~5|KCgcig79(fIK+b!s$HQYmQ4^8j5EW8soyR~<&Fg5N_MlQI850)MXNpgc^dAYw+l4oYoOYpw0b9_y;bsO@rM)WWZ^ z(XA-5imLliJ(TN%Xz(&42&VL@8|!V-fH&yWY`4{JW40(PrCce?qI*5=PBg)kyw$6$ z{$TMRY}8l=z`e2l>tr4-WPuQQB0mg!+yosAxIXUW+2p0lsBTRg+1DYT@> z0luB#dRZ@A!@h6%>*S{Q5)^@FNprC<`+SSOH@RV&{zN=FZm z+IB01f><;PtpgDg>csB57Hd*O=iM)P6BMex*U!LR5=-s59)GsWOx>wq+H~sC_{p0g z^XMW^%7knfR;BF6SAOMlrqAVB!4AXLa}oiu$MxmP8>UvxzY`fvM=mt@fGIKj>y^0! zfsoyX_!8!r6Q(4D`SbvI{Zc)Sk?S2qp3MPdV%e z(0yBaocd{8`){?9HN~G@Myq}%eW@UbBU|OvWTp=Mu$=Y4xIvZ3?_b2j2GRU^1aBJJ zlaAd0MprB52PNbHH^mvGe@e|e-Z=ItacB{7GqVoQ%tO&C+fGEj{Tt>!hYJuqLMt14 zHd96Jn_x-Y6qR$-&Pwg3ZJQ&+NbaJd?>`7PVN9=lgzP#^p$1qAvn4R8^Yc zq58-7XqGX7h1V5_jXaKmlQr~;{*S>LP_z)YLJ42Sd}y>shh5|tUzA4#p;}Pg7v5|+ zSzF4e3%ezcTDNSWEgpbr@14n}p%*gYSdX(M=|xKrHaBrnl5sz;2iHOeTfL#(l69uFzPqI$ICC`B7nS3_@(Uq*3MZqEC zHdjP-{A`vsl~?Q9`YzzwxbmGHu!7Um6JF9F%9~3ZN-um&4R@6Mz~Mp#hdiGMqtsB6 zORU8aV{D9}E2P@^>q^}hykAqvnP-MSk`^U4mTi_f^}}P4VnVsU8tzdTrH| zPZVAsZ0KN;(S2f#D%> zGLj_Fh}@-o*3NCN?1hayDS{X`&;s$x6T7nTs* z(5!9z6;Y#(jPy@R*S_epku)iGC#8^;F5*Zbl z_VEGLoq^xw#9cl1$^Z+8&E#6eTGr-05^8LFYTM9&=xlVBEg%5Q~IW^!WA-Lba@ z=r24hn}U@zg|Ye6K6bT@6P9Y|0anDg53re7A9rUiYDu{P$YyKo{GdunS&EsP_!#zq zm-eG1M}a9AlSp^X(O5&oY-u{$Edyd%g+hM11+hRg6cx4ehF|P`_#`SQKwziY}iPEplXXnvv4D~H$#g*5b2u%ZT9^H#8PeB zYF!@-gX<&I6n|J$p25ZJ5C%O$*NUJ#Lce$X)w%b+uu_J#uPZsL?f#yJicF_kx-7^7 z)Kw@pYN~ZatFer3kP#1SoA!XzbihfE;eKvl+SnlIFl-j>xsSRBid@{pxK%Gx-IMk6 zz=5*YvGQ-@?}jp8iZTscu{qrojf_W;SJ^?VLrKx!c&_M^{AIJWLT{C(IVwr6d|<35 z{)w*q*%d*5w7ed+lFc%JJJN7xFJVvO^a`pCJ0&#{uG>``DRT#r3Lf7-2+K}Y=}kUD zkxU;;yT04wk)0GBzZNE@s3=Yl*+{-@av})f$J(M`tpIUG1TrK$q1n7G70ztKyo|V} z_S#vEZ^xm&$TyQ117cHa4J-$>v^UV)^v6>_5C@df-ztQ<)#sMUbSlg-V^=vklTt-_ zv{dj4Xja45QiC9SvWWAkK z(WZt)bJtb?^>529w&>JxBO-iv4N#gi*NHmbZYqj2S8fUkps=a6LtSa!3Oj`Rn&{(j z9L@fWg{sDZf;OwPVOWPAxl%)Z(tNAC#GA#UcqD@2LSG5*HL526VcqnL zVl773fun#Z9ZjWj<|1hf9BK^2_-HblE{a6-35GUO*fj?QPMZ=2wrQ_2Fskkr8z;q~ zE-diOTF`kactZF}$gA3Tiro7~%^nLTFufA6W~+I>yJe#zs?`TYo_k`#X{vc}9XL&G z=pN}j?OJ@3*=g0CC1cktlo^3Qc`vYg!QmQ=F3_PW7&F>Q{Sf57VvVA<3s+E8 zhnKf;5z6$5-Am3ahO5j2&It-fLY5mDT!QB(L&1c?w?U#cjxt41UshlLLEz{JdnXro zm25;06~M^$jWTmdfTBGT&sy#5WG5=~5C|ha>9D|Lt!_1P!MKmWSR=&TAXr9i9&#pm zdR|C8W<=JS>?V48*<}4yWOZ}cS=MbyZ!MYIY!IcQ-=Eaop)8@b`=nWUWdv!-Z}J>j zVMwfJ{lQpuTZ=T#T5CLM8Cnu17|u2<)SEH79ff!!p+pYe)6ma=jOZ$cE@o#%G3TA%r$4;D}vI6S+BTY%`XNEWew{*RE`KbhS4iT5rj$VlqtT@EqMOoj{sS>MH(Vv;nL?lvNZQ1!JAcx)9up83wIBO zLuYu8kLJ2L#3^7UomzOe@;(?0Uh;H{EV`S!3H zI#ZY`SS(P@+5@P4tfTN9LRBGRUtE1FqTflfBOw(mKo@1AAS(ZQ*FsTahivdopp%%D ztp|Dw_pKF+=Nzqy{tb?hg!bS>S~&*ZAyb(D;cOsOX(q+S0oEBE-+iA|kg!&x*YmFZ z9#aIC68D(9N8TOR0ne=LQ{fl+=j(rG9z;xB^O*Y%98vdS<7IdaT2Fk-2#|L zp1m=qC$g&opF;zK+bfNyW2DCOa^?eCP+AJy{4AfiC)Jj@rUN-)44K&({IIGxL~Gwv z70FwILeKUNy-x(0Kk!49AdNMWSZ!zh5;mU>q4;JJdt~2SRfO+GU&EbiJLyRN(j8Z^ zUWzK?n@e|E$C;Ij&W1I0sydIXw<4z-`llmRnM8bzv_S8|aZIP#d)aE?{sP2u-Hu-CPt zi^~ylD&^kED3xA(u#HT#(j}7&m`qU^sTMK7LrDRI_8Tfe)m$zKD}8G>Gl2`lDM&Z{=kw!WGEH%WAeoSPpX-1N zcY32PermVg3wgYCRo(<$Tn*oLtGLCTh6%*p~^Z9sdSQc?tS|E*(uNT(JlR8RdcCe>A)$|tAlbFHMgPmuDh z-f~yRS3fud^r$~kT~t<2%Q<;mOVXPa`pj#nyEB;gFkde7G82oCs^>H684Azpl*>oa~Zq`(H4=Ar&~cr(wM=Upq?z1 zyXIChtul=y4nPTtjcO_8e4&aJcqAu5_gvF+=49zV_2}~>c!Xl4Pqb0L`a$J3Jt@c$ zI{V%Wj+i}A1RZ44lEV!x_V(v+S<7&k?RKUkgAmfsRZk^Pyv}@nZwub)RHT0u6QCK zfSHDaw-lkesYrX3R4Q{H{ecWIq!O2C(H%`1E(K=7B;&yvK#G#A&6?JH^u#dRqaiZS zyEWpg8>(Fhag)+$ zc;bo!RcJN!R;S~pyu?z(+ySbdzDm_wY8T1mE5hPr!>(WAhtxs$2|Z}>nP`dKM#5KE ziMpjreKO|+1hJpAx%(zr4nyOX7E@TKJ4@3vxUGjZ`n&v}8=}52xm{Odtgj3rDSx{C zalfIL`{PSPSTG3#OO)`H)yE)02(b{eiVqZiQ+SWa5iNr@TW@;#P6tvosDwm|xl^X& zbTz9p)%;vx!I4@?M_tVLAeVrn6ZH9jjy4~g;HSyz;&N#Gd_Qu&EJJ(T9nU^Jts9&0 zWWshpi51m2QEFB&f#xXf=La<3Df!IEke`4@lrnZCMt4h43{3tF$0@=g3W2ByevxW) zn?R#s2N(u;=Lm0_jI+%sq%uRFC6=s`)e*-V9d#ouQce>E(a@>XxLSmgUeWLg;0RvRor)V8S)WmA zz<30~N4P5rxOr-nX-G@@3qWg<`Jo^$$|upa9QoS{7M#!R4gA*YR~6$xIFjC;kD}Y% z>S01$SCo6$IwlSHHm8T9;b*eYZovu8 zB4A8{cUx^x~7xcm;Qa}y%CmH9F(SRuGgeH-5H#rtvd#R1nZZAVk!L%#et zqgW3Q?|9sGx*MNNN_E*mkWOY9mNURbi~9*f3*jn+Sac#kT;sOtxc*VrmD64Yz!{019BZtAWH5+eTkx=SIrH#7~Mnj(1*T}1iMvh76BH&FwADI zo7@2!7d!1~+KYl6n8$5kLVF~iZHmR}tdfH(YjjUS-M3|GGH`%bx~X1Lsf{M8n+&UH zwS6s#hmv5~5UH%`vM2f?=6Q6I`$ZK((owz9noeBr-OTw2GYi_>hfd5$#r(rF_)TL% zS0RjdL-pbW(#<+a281FCv+JhzaB7g%^m0Y?+k2<2RuP42>wuREEA$bbC_3f(a$BeA zc9QRE{n%#+6{upBzo@;jCVmD zXp%)gbnlvi!W}0KW-es)p+>cc$?c2pHcIsPRch@`Q@Jj$CX{~E$42x{>{pvLtVyi; zF$IRkKOm+e(Ug56tdg7+#fmFi65bc}ljs`tQ=pv1%^{mjfCm@cwOSO$nZM%}(M?(i zt!>s+f5)R(OOT@sJ;rPkX)QUR;;aBE`6;1ZuiIK2T`&ly;G)raBvd72CxpdB^`s^< zT2H&wCh&s?bopCWMLaGU|ABIL^!Z2so72R|UUPAQp74XvE~yyPUxFVHdogGy8lDhexFojsgAzeHhX5HO;&%ryLu796dQEhus1UPerO0u)L!7P!ltiOtcUt+_gem?sG(2$xNp-+quLt};@9?*P=9 zAdLv3VXJ2oWO6y_C6`vA(7_>emdF-8^mDhuNFwZTk5ds+!-|PHCirj#e%{Uot(*w7 z1d7`WjN9Oh8G%G3(MzRP`;^9edgbPPMP_TtyGKm#a#zmtis zGh}43DasBHkXpxxv*{+)>WbP>MiIv-U1cZM!6gB z&VI`WU53$8FCR=<2dBDSGtCmwj94g_L@!oc`rQ?KmbAzPexrabF@o$G+c>1$3mYkO z$oSLd58SY;0nsXhO|lzT0RR(1s>fr)AsbNopOTW?z0%pZe-}%jsTZ$RNd`CY>Xbt&%rUU*FBY`v(^f3JZY$9h?$Hr z0?o31NOb#Ukx48`A?cWG+|!@Plvny7wY^wkqrDkaGvEBx54k6i{<7I zxtCTw965uEZ!zq|mWbfHN<+0mfliYw$zwn}zEMq(fN!1kEl8^a3=TK{aE=+8t1p>g zLjNX6Tz&R*!oqELdp5|jm9+0E1{s*K9zEA9KD;j!oyx4sO%AgOz$$A1xY~+6+0Y-H z;4dOpG^HQRZLVSO#x29M8(fQ-6Jrl_!A~s`P}d~Uq?ss3AC!6dSTdAxSM3QMb18C` z3izPuwh$6kA!T|X;i6IqN+Nfkq`=62g(cE@pUY5VtH_H|YR}D$+5@@HmuxPgSvZd) z&qwF5q~U3)j`ilA_?bO~pn2#ACxbjY;5(p2Y^k&Fmtu8lhjH^dMO7i5ZrzcZB@&SW3Se@e(YVfFC zC2_M=AF|3Wb`^l#6jc#sw`_>fE$9GGgiJXJDHB$b%qo_pKL^2C#ar{7i+O&o%e%F~ zUA$Jy82xxuM<>xsg1b|#x&m0ScazMMcclI9T-*Kouzkq1lYti037OMO@{8IpcT6l| z49JPO3*#IvT(0GX0n=S*1lKYq=*OONGm~V2gx6cJqqgxb;(jR!6&?0=Rzdbj4A%r3 zbDPHJ=Ge&Ojp<}rJF|t_1>Dp&h*Mj|ViI-)!1hu-V}>QGA&pY8)DMj^D9Zqt0o`mI zoNDeK|2BW_PzgRsB`G3y1M|WfqZ~ho^(i1z3slUkj>e7q2zVhi{F}zeY-?frL9^2P z9};2`{>XL;qh7>K#a{&-+}cYfWYIgU2t?KBrb93%*f2~>7qi84d4WjZgHINAR$Xsp zA#UWyRA3ayQnK=%aRXAEpNo?wyn@m5_rkpFp7shc(lmw_-aOQ%jKmc7p5%P&g3yAoNmWdlBJ2 zm_y9kDW3D7Dq^kOc#pP~6o`u&MaCrDbMW)s_%C7tnJI{6|^2>&1a{?sY z7X!LVr38owHfP2DTzN7D>kQ&MlmP@eM;W4a8Qh83zA2{71(>l~?LZfe=H<#y%ZF@1 zY`02+hwtX9Hbebnx+B(K?|tp%4mC`%%r!o3R#qVvLz^xgGd$yBdl#txO2NJvrIjMv zxq^6(i%{TVrHnu!ho~!GcM6?EN{#0d%}-p`1vnB*@p4^Pu3=n-$kM0kb2pXFkp8L; zk&k@3AYs}i64QhuL4SltmN&Hs1}L_Wq`P*e#;W&0am~h0aDPf&8_084gd(*@Ouu~kQt@Rm8B*(I zRO6Bt(+*0~!;wH*7+OPSTga!pKXRc@<3sG4=8v-lW!#DK?9?z^`Nal~uX}>`X55yD zO#p3vwAx7BQC8GV_zYC_g^OabYL9g13q=cSYXWjif0RgKY(H z<^?HWT)lQ3bClqw#}^20f5v#Iay<6v&~H1CYKhMHfAEf4Hy5eb$r&BRt|UV-Rv9|0`H^z~-byAUN}r+3 zBA{F>?H22l97+J`e%^kr5ILCJE#+{LJv;m}F2n@tcu*Ab!Kxe_zIhHsbOV>buBhDC zrBsK$NJssg3j~ma8ejT2^9_j{TMDpYc^;dt@$hJ5HsQe9U$G*1teTu^ ze4AFY)YY;exKFaD!nJ>ue2^vxfpTk_jGyP$95yqyRx0?QpiN z2(>UAkzT69l}JMEyb^Pp&&6nu1WjUwLB7N&XJ7!>1EQg!*w~)O0M&3sgps~qpg7IU|diCMdNoII|Q;>Xp!AvZJD&H*5C3`9@ik?;sy&_QP$#xFr% zZLW_PT;2L^C)|ReQX%lSD{vae!mzrZ>R>@o=n+_&9+J_* zZ`&}!zi?-jFTXWq2cqmAr#lv1VsVFnjt8r=@P{a2YzBC9(xqp>I zT(DgQb-=Bd|A;5vXSf(u4&&G~ni}E8V6fza@{hs~7Z`I9V5Fou3-_O z%gk!oZm%4qK!@Cai{n-gmD}1k_j0L8zu^V*H(t5n4 zYOWa8)?RExKupxjH|?Cz;lhj;2(_n$;H~U|wAam!p#OAVqm`rU>rP0|W#K4x)Ic_5 zsN2q9TB>@OU4G4xo}r$XutOESRHnQi{%Z|(QCwU!%BZEC$t1gx5}(L7<#$9G(mzVF zK)bt&c76}QGEDMII>a%J-*Sur0MN3nUbj&K72gSf9PC(3%p6R?ES`3buW?R#p!W4;Bwj76)ewR(5`VepWUPRt^s4R|K<* zm%XcrC$qf^-{tQ_pgesh|bI=HzCQ&7D2ll_DIx3p58uZ{n3@cZ}= zy^E_EtK91W*Xwv*0jwNsY<$dY9Lya2tbezE?NwC#r?tJyKU93xlhxD2k(Hf=jn&TX z-z;2QrQHAJ??1J0(Rkg>%Blu-ad2}6fu-ER_O6tFcj{>C=JI!+ZZ6>8O@HFHH8W>@ z4eF2QzuU;jDXRR_=C_O%R(6hmEPkW^jx+=P6X)pWZ2Jde24V%F8Gjo`mgV@0QATZd({BKlp_Aag__8{gQ*8vGH*J3#0{hc6m+n z->mFxEF2tvddxrq(yyE*ujyuGXJP?nb+otm)A4&^1YU>pDy_-y>Ud@T)BZXd0SRZY ziK~ONhJ%BxFvaiK$bM7)DJn9df0T>BI|tAo!#`lK+3$+`N6m?wSg`&%6k`3a!2b)A zx}}4M{r?`%zo7qQ5p#C+aB#L!c2+jE27_GxYo7lM{7)ve*VWF&)%o?u1^+e4{|hJd zSHjA^+B!IU{moPz?DW^sUrUm$)gP(I$o?z?0w$op#~?>H=As*js>K z*V}&-+kdoM{U?e1nsjCyrhMG5iOkEz%w@`J#>@}q;$b#5;o;*q<>KSz1OJuC|73S@ zFn9GZaR!T7yyn2`Jiiv#pLr&u`$zuK|9dnZmf+vX#m2$@dl6*g)L`ck;A9ivV59hJ zw#kH8f3L9rF8tR);Ay(pA~R{=anpp8)@2cxMFy+q*dYSE2tM@|P@sI}yFc{8!uS3GVfL z$@eEcK({x5oXrT!l$|1Ew0N3Q>o>%XPIe+&FS+4Vni{kIhOZ-M_O zyZ(>Kh4`;aZm|99|9u{>7t}Ny!=me~2@)3+_@^D?j&*h|d!8wZ}w3Cr%v|aOGsn5cVPC6TfAAUN$w5TveiN7mR zcbmnCGc1Qin?#KryxNNua4x!YD$(uxWf<&ylB+y$b5q^$W5icPoq5xbRgB;4zJ=1C z8RrFtH~S|Q7jts=(sY_qw_lB|#h;03=_ zM2R#ss&GHVeA4#=yMtlcALUVh_8we)>s=?vk31_i8vN1Wb3}(a7_wh6*gsn2QE?r| zvYTZcShEwNv=8wh$|9T>|CX}zeF7zy8U^pYsNrqdo*kKtv%qC_U7q$+dw;j`}}}X_g-R3KAgOZhQ-UVHT;5r}iJNkEP`u zzbu^X#>e@{Pgdw#ul8Mprb1S0|5{-_2IFAh&w<%cW>6YI(cD{BGkf|)&^DdH05 z{Q#zU02gBQ{QEL&UL}Tnu$c!4AeQ{lktddX+$(8L-Zf$pJ6{(JNj{L(6~3k+Jmv$@ z^j)bjH2HwN2I#|n0MM3vm;C^s4f&va1#rRn-98Ml=M!ZS7-Kj$L3wS_2Zk&kNPTVI zw7%~X)f{?mTcpg;*S0O(4-tp?~qKG@6y7|5aL6Dx~BZ3VEMPmP@9 zgO$agwgTABr$lb@LG&8H)U)+{1Uh&KQrHgwxa3m}z#$(P_X7}gPQRupeDJ-#EpqJn zMD2b6I;UR~5_LJa$yq+w><2hJDgSfGM-J=<037n?^O^VqT82}>f-M>900000NkvXX Hu0mjfj3HyU literal 0 HcmV?d00001 diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index 828e03ec..f35a7aea 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -27,7 +27,8 @@ export interface CustomWokaPreviewerConfig { } export class CustomWokaPreviewer extends Phaser.GameObjects.Container { - private background: Phaser.GameObjects.Graphics; + private background: Phaser.GameObjects.Image; + private frame: Phaser.GameObjects.Graphics; private sprites: Record; private animationDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; @@ -53,13 +54,15 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { [CustomWokaBodyPart.Hat]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), }; - this.background = this.scene.add.graphics(); - this.drawBackground(); + this.background = this.scene.add.image(0, 0, "floorTexture"); + this.frame = this.scene.add.graphics(); + this.drawFrame(); this.setSize(this.SIZE, this.SIZE); this.setInteractive({ cursor: "pointer" }); this.add([ this.background, + this.frame, this.sprites.Body, this.sprites.Eyes, this.sprites.Hair, @@ -118,13 +121,10 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { }); } - private drawBackground(): void { - this.background.clear(); - this.background.fillStyle(0xffffff); - this.background.lineStyle(this.config.borderThickness, 0xadafbc); - - this.background.fillRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); - this.background.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); + private drawFrame(): void { + this.frame.clear(); + this.frame.lineStyle(this.config.borderThickness, 0xadafbc); + this.frame.strokeRect(-this.SIZE / 2, -this.SIZE / 2, this.SIZE, this.SIZE); } private animate(): void { diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 3db2962e..9aee98f5 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -19,7 +19,8 @@ export enum WokaBodyPartSlotEvent { } export class WokaBodyPartSlot extends GridItem { - private background: Phaser.GameObjects.Graphics; + private background: Phaser.GameObjects.Image; + private frame: Phaser.GameObjects.Graphics; private categoryImage?: Phaser.GameObjects.Image; private bodyImage: Phaser.GameObjects.Image; private image: Phaser.GameObjects.Image; @@ -37,9 +38,10 @@ export class WokaBodyPartSlot extends GridItem { this.selected = this.config.selected ?? false; - this.background = this.scene.add.graphics(); - this.drawBackground(); - this.add(this.background); + this.background = this.background = this.scene.add.image(0, 0, "floorTexture"); + this.frame = this.scene.add.graphics(); + this.drawFrame(); + this.add([this.background, this.frame]); if (this.config.categoryImageKey) { this.categoryImage = this.scene.add @@ -115,21 +117,19 @@ export class WokaBodyPartSlot extends GridItem { }); } - private drawBackground(): void { - this.background.clear(); - this.background.fillStyle(0xffffff); - this.background.lineStyle( + private drawFrame(): void { + this.frame.clear(); + this.frame.lineStyle( this.config.borderThickness, this.selected ? this.config.borderSelectedColor : this.config.borderColor ); const size = WokaBodyPartSlot.SIZE; - this.background.fillRect(-size / 2, -size / 2, size, size); - this.background.strokeRect(-size / 2, -size / 2, size, size); + this.frame.strokeRect(-size / 2, -size / 2, size, size); } private updateSelected(): void { - this.drawBackground(); + this.drawFrame(); } } diff --git a/front/src/Phaser/Helpers/TexturesHelper.ts b/front/src/Phaser/Helpers/TexturesHelper.ts index ee1b1082..6c0f1aab 100644 --- a/front/src/Phaser/Helpers/TexturesHelper.ts +++ b/front/src/Phaser/Helpers/TexturesHelper.ts @@ -32,6 +32,30 @@ export class TexturesHelper { } } + public static createFloorRectangleTexture( + scene: Phaser.Scene, + newTextureKey: string, + width: number, + height: number, + sourceTextureKey: string, + sourceTextureFrame?: number | string, + sourceTextureWidth: number = 32, + sourceTextureHeight: number = 32 + ): void { + const rt = scene.make.renderTexture({ x: 0, y: 0, width, height }, false); + const widthTiles = Math.ceil(width / sourceTextureWidth); + const heightTiles = Math.ceil(height / sourceTextureHeight); + + for (let x = 0; x < widthTiles; x += 1) { + for (let y = 0; y < heightTiles; y += 1) { + rt.drawFrame(sourceTextureKey, sourceTextureFrame, x * 32, y * 32); + } + } + + rt.saveTexture(newTextureKey); + rt.destroy(); + } + public static createRectangleTexture( scene: Phaser.Scene, textureKey: string, diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index e48ce89c..c8b89e11 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -32,8 +32,6 @@ export const CustomizeSceneName = "CustomizeScene"; export class CustomizeScene extends AbstractCharacterScene { private customWokaPreviewer!: CustomWokaPreviewer; - private bodyPartsDraggableGridBackground!: Phaser.GameObjects.Graphics; - private bodyPartsDraggableGridForeground!: Phaser.GameObjects.Graphics; private bodyPartsDraggableGridLeftShadow!: Phaser.GameObjects.Image; private bodyPartsDraggableGridRightShadow!: Phaser.GameObjects.Image; private bodyPartsDraggableGrid!: DraggableGrid; @@ -69,8 +67,9 @@ export class CustomizeScene extends AbstractCharacterScene { this.load.image("iconHair", "/resources/icons/icon_hair.png"); this.load.image("iconEyes", "/resources/icons/icon_eyes.png"); this.load.image("iconBody", "/resources/icons/icon_body.png"); + this.load.spritesheet("floorTiles", "/resources/tilesets/floor_tiles.png", { frameWidth: 32, frameHeight: 32 }); - TexturesHelper.createRectangleTexture(this, "gridEdgeShadow", 200, 115, 0x000000); + TexturesHelper.createRectangleTexture(this, "gridEdgeShadow", this.cameras.main.width * 0.2, 115, 0x000000); const wokaMetadataKey = "woka-list" + gameManager.currentStartedRoom.href; this.cache.json.remove(wokaMetadataKey); @@ -99,6 +98,7 @@ export class CustomizeScene extends AbstractCharacterScene { } public create(): void { + TexturesHelper.createFloorRectangleTexture(this, "floorTexture", 50, 50, "floorTiles", 0); this.customWokaPreviewer = new CustomWokaPreviewer( this, 0, @@ -106,18 +106,6 @@ export class CustomizeScene extends AbstractCharacterScene { this.getCustomWokaPreviewerConfig() ).setDisplaySize(200, 200); - this.bodyPartsDraggableGridBackground = this.add.graphics(); - - const gridBackgroundWidth = 500; - const gridBackgroundHeight = 170; - this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9); - this.bodyPartsDraggableGridBackground.fillRect( - -gridBackgroundWidth / 2, - -gridBackgroundHeight / 2, - gridBackgroundWidth, - gridBackgroundHeight - ); - this.bodyPartsDraggableGrid = new DraggableGrid(this, { position: { x: 0, y: 0 }, maskPosition: { x: 0, y: 0 }, @@ -134,7 +122,6 @@ export class CustomizeScene extends AbstractCharacterScene { showDraggableSpace: false, }, }); - this.bodyPartsDraggableGridForeground = this.add.graphics(); this.bodyPartsDraggableGridLeftShadow = this.add .image(0, this.cameras.main.worldView.y + this.cameras.main.height, "gridEdgeShadow") @@ -231,32 +218,6 @@ export class CustomizeScene extends AbstractCharacterScene { this.scene.run(SelectCharacterSceneName); } - private drawGridBackground(gridPosition: { x: number; y: number }): void { - const gridBackgroundWidth = innerWidth / waScaleManager.getActualZoom(); - const gridBackgroundHeight = 115; - this.bodyPartsDraggableGridBackground.clear(); - this.bodyPartsDraggableGridBackground.fillStyle(0xf9f9f9); - this.bodyPartsDraggableGridBackground.fillRect( - gridPosition.x - gridBackgroundWidth / 2, - gridPosition.y - gridBackgroundHeight / 2, - gridBackgroundWidth, - gridBackgroundHeight - ); - } - - private drawGridForeground(gridPosition: { x: number; y: number }): void { - const gridBackgroundWidth = (innerWidth + 10) / waScaleManager.getActualZoom(); - const gridBackgroundHeight = 115; - this.bodyPartsDraggableGridForeground.clear(); - this.bodyPartsDraggableGridForeground.lineStyle(4, 0xadafbc); - this.bodyPartsDraggableGridForeground.strokeRect( - gridPosition.x - gridBackgroundWidth / 2, - gridPosition.y - gridBackgroundHeight / 2, - gridBackgroundWidth, - gridBackgroundHeight - ); - } - private initializeRandomizeButton(): void { this.randomizeButton = new Button(this, 50, 50, { width: 95, @@ -381,7 +342,7 @@ export class CustomizeScene extends AbstractCharacterScene { const gridWidth = innerWidth / waScaleManager.getActualZoom(); const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, - y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5, + y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5 - 5, }; this.bodyPartsDraggableGridLeftShadow.setPosition(0, this.cameras.main.worldView.y + this.cameras.main.height); @@ -390,8 +351,6 @@ export class CustomizeScene extends AbstractCharacterScene { this.cameras.main.worldView.y + this.cameras.main.height ); - this.drawGridBackground(gridPos); - this.drawGridForeground(gridPos); try { this.bodyPartsDraggableGrid.changeDraggableSpacePosAndSize( gridPos, diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index 75846bf7..2610023f 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -9,6 +9,7 @@ import { get } from "svelte/store"; import { localeDetector } from "../../i18n/locales"; import { PlayerTextures } from "../Entity/PlayerTextures"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; +import { CustomizeSceneName } from "./CustomizeScene"; export const EntrySceneName = "EntryScene"; @@ -46,7 +47,8 @@ export class EntryScene extends Scene { // Let's rescale before starting the game // We can do it at this stage. waScaleManager.applyNewSize(); - this.scene.start(nextSceneName); + // this.scene.start(nextSceneName); + this.scene.start(CustomizeSceneName); }) .catch((err) => { const $LL = get(LL); From 42e71411b5a227b60afb06e073b3574e88345b3a Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Wed, 30 Mar 2022 13:20:49 +0200 Subject: [PATCH 58/61] center grid if possible --- front/src/Phaser/Login/CustomizeScene.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index c8b89e11..62dbfb21 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -126,7 +126,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGridLeftShadow = this.add .image(0, this.cameras.main.worldView.y + this.cameras.main.height, "gridEdgeShadow") .setAlpha(1, 0, 1, 0) - .setOrigin(0, 1); + .setOrigin(0, 0.5); this.bodyPartsDraggableGridRightShadow = this.add .image( @@ -136,7 +136,7 @@ export class CustomizeScene extends AbstractCharacterScene { ) .setAlpha(1, 0, 1, 0) .setFlipX(true) - .setOrigin(1, 1); + .setOrigin(1, 0.5); this.bodyPartsSlots = { [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, { @@ -186,9 +186,9 @@ export class CustomizeScene extends AbstractCharacterScene { public onResize(): void { this.handleCustomWokaPreviewerOnResize(); this.handleBodyPartSlotsOnResize(); - this.handleBodyPartsDraggableGridOnResize(); this.handleRandomizeButtonOnResize(); this.handleFinishButtonOnResize(); + this.handleBodyPartsDraggableGridOnResize(); } public nextSceneToCamera() { @@ -340,15 +340,24 @@ export class CustomizeScene extends AbstractCharacterScene { private handleBodyPartsDraggableGridOnResize(): void { const gridHeight = 110; const gridWidth = innerWidth / waScaleManager.getActualZoom(); + + const gridTopMargin = Math.max( + this.finishButton.y + this.finishButton.displayHeight * 0.5, + this.bodyPartsSlots.Hair.y + this.bodyPartsSlots.Hair.displayHeight * 0.5 + ); + const gridBottomMargin = this.cameras.main.worldView.y + this.cameras.main.height; + + const yPos = gridTopMargin + (gridBottomMargin - gridTopMargin) * 0.5; + const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, - y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5 - 5, + y: yPos, }; - this.bodyPartsDraggableGridLeftShadow.setPosition(0, this.cameras.main.worldView.y + this.cameras.main.height); + this.bodyPartsDraggableGridLeftShadow.setPosition(0, yPos); this.bodyPartsDraggableGridRightShadow.setPosition( this.cameras.main.worldView.x + this.cameras.main.width, - this.cameras.main.worldView.y + this.cameras.main.height + yPos ); try { From 32b777b5cfd248eac2c658169a155c82203d9545 Mon Sep 17 00:00:00 2001 From: Piotr Hanusiak Date: Mon, 4 Apr 2022 13:01:21 +0200 Subject: [PATCH 59/61] New custom woka scene full clothes (#2023) * center grid if possible * whole outfit for every slot * switched bodyPart slots into buttons * change categories and body parts with WSAD / arrow keys Co-authored-by: Piotr 'pwh' Hanusiak --- .../CustomizeWoka/CustomWokaPreviewer.ts | 3 +- .../CustomizeWoka/WokaBodyPartSlot.ts | 84 +++-- front/src/Phaser/Components/Ui/IconButton.ts | 115 ++++++ front/src/Phaser/Login/CustomizeScene.ts | 356 ++++++++++++------ front/src/WebRtc/VideoPeer.ts | 2 - 5 files changed, 398 insertions(+), 162 deletions(-) create mode 100644 front/src/Phaser/Components/Ui/IconButton.ts diff --git a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts index f35a7aea..fef802a7 100644 --- a/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts +++ b/front/src/Phaser/Components/CustomizeWoka/CustomWokaPreviewer.ts @@ -1,4 +1,3 @@ -import { MathUtils } from "../../../Utils/MathUtils"; import { getPlayerAnimations, PlayerAnimationDirections, PlayerAnimationTypes } from "../../Player/Animation"; export enum CustomWokaBodyPart { @@ -54,7 +53,7 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { [CustomWokaBodyPart.Hat]: this.scene.add.sprite(this.config.bodyPartsOffsetX, 0, "").setVisible(false), }; - this.background = this.scene.add.image(0, 0, "floorTexture"); + this.background = this.scene.add.image(0, 0, "floorTexture1"); this.frame = this.scene.add.graphics(); this.drawFrame(); this.setSize(this.SIZE, this.SIZE); diff --git a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts index 9aee98f5..94e3bb33 100644 --- a/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts +++ b/front/src/Phaser/Components/CustomizeWoka/WokaBodyPartSlot.ts @@ -1,5 +1,6 @@ import { GridItem } from "@home-based-studio/phaser3-utils"; import { GridItemEvent } from "@home-based-studio/phaser3-utils/lib/utils/gui/containers/grids/GridItem"; +import { CustomWokaBodyPart } from "./CustomWokaPreviewer"; export interface WokaBodyPartSlotConfig { color: number; @@ -8,9 +9,8 @@ export interface WokaBodyPartSlotConfig { borderSelectedColor: number; offsetX: number; offsetY: number; - bodyImageKey?: string; + textureKeys: Record; categoryImageKey?: string; - imageKey?: string; selected?: boolean; } @@ -22,8 +22,7 @@ export class WokaBodyPartSlot extends GridItem { private background: Phaser.GameObjects.Image; private frame: Phaser.GameObjects.Graphics; private categoryImage?: Phaser.GameObjects.Image; - private bodyImage: Phaser.GameObjects.Image; - private image: Phaser.GameObjects.Image; + private sprites: Record; private config: WokaBodyPartSlotConfig; @@ -36,12 +35,43 @@ export class WokaBodyPartSlot extends GridItem { this.config = config; + const textures = this.config.textureKeys; + this.sprites = { + [CustomWokaBodyPart.Accessory]: this.scene.add + .sprite(this.config.offsetX, this.config.offsetY, textures.Accessory) + .setVisible(textures.Accessory !== ""), + [CustomWokaBodyPart.Body]: this.scene.add + .sprite(this.config.offsetX, this.config.offsetY, textures.Body) + .setVisible(textures.Body !== ""), + [CustomWokaBodyPart.Clothes]: this.scene.add + .sprite(this.config.offsetX, this.config.offsetY, textures.Clothes) + .setVisible(textures.Clothes !== ""), + [CustomWokaBodyPart.Eyes]: this.scene.add + .sprite(this.config.offsetX, this.config.offsetY, textures.Eyes) + .setVisible(textures.Eyes !== ""), + [CustomWokaBodyPart.Hair]: this.scene.add + .sprite(this.config.offsetX, this.config.offsetY, textures.Hair) + .setVisible(textures.Hair !== ""), + [CustomWokaBodyPart.Hat]: this.scene.add + .sprite(this.config.offsetX, this.config.offsetY, textures.Hat) + .setVisible(textures.Hat !== ""), + }; + this.selected = this.config.selected ?? false; - this.background = this.background = this.scene.add.image(0, 0, "floorTexture"); + this.background = this.background = this.scene.add.image(0, 0, `floorTexture1`); this.frame = this.scene.add.graphics(); this.drawFrame(); - this.add([this.background, this.frame]); + this.add([ + this.background, + this.frame, + this.sprites.Body, + this.sprites.Eyes, + this.sprites.Hair, + this.sprites.Clothes, + this.sprites.Hat, + this.sprites.Accessory, + ]); if (this.config.categoryImageKey) { this.categoryImage = this.scene.add @@ -52,18 +82,7 @@ export class WokaBodyPartSlot extends GridItem { this.add(this.categoryImage); } - this.bodyImage = this.scene.add - .image(this.config.offsetX, this.config.offsetY, this.config.bodyImageKey ?? "") - .setVisible(this.config.imageKey !== undefined); - - this.image = this.scene.add - .image(this.config.offsetX, this.config.offsetY, this.config.imageKey ?? "") - .setVisible(this.config.bodyImageKey !== undefined); - this.setSize(WokaBodyPartSlot.SIZE, WokaBodyPartSlot.SIZE); - - this.add([this.bodyImage, this.image]); - this.setInteractive({ cursor: "pointer" }); this.scene.input.setDraggable(this); @@ -72,29 +91,18 @@ export class WokaBodyPartSlot extends GridItem { this.scene.add.existing(this); } - public getContentData(): { bodyImageKey?: string; key?: string } { - return { bodyImageKey: this.config.bodyImageKey, key: this.config.imageKey }; + public getContentData(): Record { + return this.config.textureKeys; } - public setTextures(bodyTextureKey?: string, imageTextureKey?: string): void { - this.setBodyTexture(bodyTextureKey); - this.setImageTexture(imageTextureKey); - } - - public setBodyTexture(textureKey?: string, frame?: string | number): void { - this.bodyImage.setVisible(textureKey !== undefined && textureKey !== ""); - if (textureKey) { - this.bodyImage.setTexture(textureKey, frame); - this.config.bodyImageKey = textureKey; - } - } - - public setImageTexture(textureKey?: string, frame?: string | number): void { - this.image.setVisible(textureKey !== undefined && textureKey !== ""); - if (textureKey) { - this.image.setTexture(textureKey, frame); - this.config.imageKey = textureKey; - } + public setTextures(textureKeys: Record): void { + this.config.textureKeys = textureKeys; + this.sprites.Accessory.setTexture(textureKeys.Accessory).setVisible(textureKeys.Accessory !== ""); + this.sprites.Body.setTexture(textureKeys.Body).setVisible(textureKeys.Body !== ""); + this.sprites.Clothes.setTexture(textureKeys.Clothes).setVisible(textureKeys.Clothes !== ""); + this.sprites.Eyes.setTexture(textureKeys.Eyes).setVisible(textureKeys.Eyes !== ""); + this.sprites.Hair.setTexture(textureKeys.Hair).setVisible(textureKeys.Hair !== ""); + this.sprites.Hat.setTexture(textureKeys.Hat).setVisible(textureKeys.Hat !== ""); } public select(select: boolean = true): void { diff --git a/front/src/Phaser/Components/Ui/IconButton.ts b/front/src/Phaser/Components/Ui/IconButton.ts new file mode 100644 index 00000000..2fdc9cfd --- /dev/null +++ b/front/src/Phaser/Components/Ui/IconButton.ts @@ -0,0 +1,115 @@ +export interface IconButtonConfig { + width: number; + height: number; + iconTextureKey: string; + idle: IconButtonAppearanceConfig; + hover: IconButtonAppearanceConfig; + pressed: IconButtonAppearanceConfig; + selected: IconButtonAppearanceConfig; +} + +export interface IconButtonAppearanceConfig { + color: number; + borderThickness: number; + borderColor: number; +} + +export enum IconButtonEvent { + Clicked = "IconButton:Clicked", +} + +export class IconButton extends Phaser.GameObjects.Container { + private background: Phaser.GameObjects.Graphics; + private icon: Phaser.GameObjects.Image; + + private config: IconButtonConfig; + + private hovered: boolean = false; + private pressed: boolean = false; + private selected: boolean = false; + + constructor(scene: Phaser.Scene, x: number, y: number, config: IconButtonConfig) { + super(scene, x, y); + + this.config = config; + + this.background = this.scene.add.graphics(); + this.icon = this.scene.add.image(0, 0, this.config.iconTextureKey); + this.drawBackground(this.config.idle); + + this.add([this.background, this.icon]); + + this.setSize(this.config.width, this.config.height); + this.setInteractive({ cursor: "pointer" }); + + this.bindEventHandlers(); + + this.scene.add.existing(this); + } + + public select(select: boolean = true): void { + if (this.selected === select) { + return; + } + this.selected = select; + this.updateButtonAppearance(); + } + + private updateButtonAppearance(): void { + if (this.selected) { + this.drawBackground(this.config.selected); + return; + } + if (this.pressed) { + this.drawBackground(this.config.pressed); + return; + } + if (this.hovered) { + this.drawBackground(this.config.hover); + return; + } + this.drawBackground(this.config.idle); + } + + private drawBackground(appearance: IconButtonAppearanceConfig): void { + this.background.clear(); + this.background.fillStyle(appearance.color); + this.background.lineStyle(appearance.borderThickness, appearance.borderColor); + + const w = this.config.width; + const h = this.config.height; + + this.background.fillRect(-w / 2, -h / 2, w, h); + this.background.strokeRect(-w / 2, -h / 2, w, h); + } + + private bindEventHandlers(): void { + this.on(Phaser.Input.Events.POINTER_OVER, () => { + if (this.selected) { + return; + } + this.hovered = true; + this.updateButtonAppearance(); + }); + this.on(Phaser.Input.Events.POINTER_OUT, () => { + this.hovered = false; + this.pressed = false; + this.updateButtonAppearance(); + }); + this.on(Phaser.Input.Events.POINTER_DOWN, () => { + if (this.selected) { + return; + } + this.pressed = true; + this.updateButtonAppearance(); + }); + this.on(Phaser.Input.Events.POINTER_UP, () => { + if (this.selected) { + return; + } + this.pressed = false; + this.updateButtonAppearance(); + this.emit(IconButtonEvent.Clicked, this.selected); + }); + } +} diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index c8b89e11..4b4a97f9 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -27,6 +27,7 @@ import { DraggableGridEvent } from "@home-based-studio/phaser3-utils/lib/utils/g import { Button } from "../Components/Ui/Button"; import { wokaList } from "../../Messages/JsonMessages/PlayerTextures"; import { TexturesHelper } from "../Helpers/TexturesHelper"; +import { IconButton, IconButtonConfig, IconButtonEvent } from "../Components/Ui/IconButton"; export const CustomizeSceneName = "CustomizeScene"; @@ -35,7 +36,7 @@ export class CustomizeScene extends AbstractCharacterScene { private bodyPartsDraggableGridLeftShadow!: Phaser.GameObjects.Image; private bodyPartsDraggableGridRightShadow!: Phaser.GameObjects.Image; private bodyPartsDraggableGrid!: DraggableGrid; - private bodyPartsSlots!: Record; + private bodyPartsButtons!: Record; private randomizeButton!: Button; private finishButton!: Button; @@ -43,7 +44,6 @@ export class CustomizeScene extends AbstractCharacterScene { private selectedLayers: number[] = [0, 0, 0, 0, 0, 0]; private layers: BodyResourceDescriptionInterface[][] = []; private selectedBodyPartType?: CustomWokaBodyPart; - private selectedItemTextureKey?: string; protected lazyloadingAttempt = true; //permit to update texture loaded after renderer @@ -98,7 +98,10 @@ export class CustomizeScene extends AbstractCharacterScene { } public create(): void { - TexturesHelper.createFloorRectangleTexture(this, "floorTexture", 50, 50, "floorTiles", 0); + TexturesHelper.createFloorRectangleTexture(this, "floorTexture1", 50, 50, "floorTiles", 0); + TexturesHelper.createFloorRectangleTexture(this, "floorTexture2", 50, 50, "floorTiles", 1); + TexturesHelper.createFloorRectangleTexture(this, "floorTexture3", 50, 50, "floorTiles", 2); + TexturesHelper.createFloorRectangleTexture(this, "floorTexture4", 50, 50, "floorTiles", 3); this.customWokaPreviewer = new CustomWokaPreviewer( this, 0, @@ -126,7 +129,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGridLeftShadow = this.add .image(0, this.cameras.main.worldView.y + this.cameras.main.height, "gridEdgeShadow") .setAlpha(1, 0, 1, 0) - .setOrigin(0, 1); + .setOrigin(0, 0.5); this.bodyPartsDraggableGridRightShadow = this.add .image( @@ -136,38 +139,24 @@ export class CustomizeScene extends AbstractCharacterScene { ) .setAlpha(1, 0, 1, 0) .setFlipX(true) - .setOrigin(1, 1); + .setOrigin(1, 0.5); - this.bodyPartsSlots = { - [CustomWokaBodyPart.Hair]: new WokaBodyPartSlot(this, 0, 0, { - ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconHair", - }), - [CustomWokaBodyPart.Body]: new WokaBodyPartSlot(this, 0, 0, { - ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconBody", - }), - [CustomWokaBodyPart.Accessory]: new WokaBodyPartSlot(this, 0, 0, { - ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconAccessory", - }), - [CustomWokaBodyPart.Hat]: new WokaBodyPartSlot(this, 0, 0, { - ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconHat", - }), - [CustomWokaBodyPart.Clothes]: new WokaBodyPartSlot(this, 0, 0, { - ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconClothes", - }), - [CustomWokaBodyPart.Eyes]: new WokaBodyPartSlot(this, 0, 0, { - ...this.getDefaultWokaBodyPartSlotConfig(), - categoryImageKey: "iconEyes", - }), + this.bodyPartsButtons = { + [CustomWokaBodyPart.Accessory]: new IconButton( + this, + 0, + 0, + this.getDefaultIconButtonConfig("iconAccessory") + ), + [CustomWokaBodyPart.Body]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconBody")), + [CustomWokaBodyPart.Clothes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconClothes")), + [CustomWokaBodyPart.Eyes]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconEyes")), + [CustomWokaBodyPart.Hair]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHair")), + [CustomWokaBodyPart.Hat]: new IconButton(this, 0, 0, this.getDefaultIconButtonConfig("iconHat")), }; this.selectedBodyPartType = CustomWokaBodyPart.Body; - this.selectedItemTextureKey = this.layers[CustomWokaBodyPartOrder.Body][0].id; - this.bodyPartsSlots.Body.select(); + this.bodyPartsButtons.Body.select(); this.initializeRandomizeButton(); this.initializeFinishButton(); @@ -185,10 +174,10 @@ export class CustomizeScene extends AbstractCharacterScene { public onResize(): void { this.handleCustomWokaPreviewerOnResize(); - this.handleBodyPartSlotsOnResize(); - this.handleBodyPartsDraggableGridOnResize(); + this.handleBodyPartButtonsOnResize(); this.handleRandomizeButtonOnResize(); this.handleFinishButtonOnResize(); + this.handleBodyPartsDraggableGridOnResize(); } public nextSceneToCamera() { @@ -218,6 +207,34 @@ export class CustomizeScene extends AbstractCharacterScene { this.scene.run(SelectCharacterSceneName); } + private getDefaultIconButtonConfig(iconTextureKey: string): IconButtonConfig { + return { + iconTextureKey, + width: 25, + height: 25, + idle: { + color: 0xffffff, + borderThickness: 3, + borderColor: 0xe7e7e7, + }, + hover: { + color: 0xe7e7e7, + borderThickness: 3, + borderColor: 0xadafbc, + }, + pressed: { + color: 0xadafbc, + borderThickness: 3, + borderColor: 0xadafbc, + }, + selected: { + color: 0xadafbc, + borderThickness: 3, + borderColor: 0x209cee, + }, + }; + } + private initializeRandomizeButton(): void { this.randomizeButton = new Button(this, 50, 50, { width: 95, @@ -275,80 +292,101 @@ export class CustomizeScene extends AbstractCharacterScene { for (const layerItem of this.selectedLayers) { const bodyPart = CustomWokaBodyPart[CustomWokaBodyPartOrder[i] as CustomWokaBodyPart]; this.customWokaPreviewer.updateSprite(this.layers[i][layerItem].id, bodyPart); - this.bodyPartsSlots[bodyPart].setTextures( - this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id, - this.layers[i][layerItem].id - ); i += 1; } } + private getCurrentlySelectedWokaTexturesRecord(): Record { + return { + [CustomWokaBodyPart.Accessory]: + this.layers[CustomWokaBodyPartOrder.Accessory][this.selectedLayers[CustomWokaBodyPartOrder.Accessory]] + .id, + [CustomWokaBodyPart.Body]: + this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id, + [CustomWokaBodyPart.Clothes]: + this.layers[CustomWokaBodyPartOrder.Clothes][this.selectedLayers[CustomWokaBodyPartOrder.Clothes]].id, + [CustomWokaBodyPart.Eyes]: + this.layers[CustomWokaBodyPartOrder.Eyes][this.selectedLayers[CustomWokaBodyPartOrder.Eyes]].id, + [CustomWokaBodyPart.Hair]: + this.layers[CustomWokaBodyPartOrder.Hair][this.selectedLayers[CustomWokaBodyPartOrder.Hair]].id, + [CustomWokaBodyPart.Hat]: + this.layers[CustomWokaBodyPartOrder.Hat][this.selectedLayers[CustomWokaBodyPartOrder.Hat]].id, + }; + } + private handleCustomWokaPreviewerOnResize(): void { this.customWokaPreviewer.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; this.customWokaPreviewer.y = this.customWokaPreviewer.displayHeight * 0.5 + 10; } - private handleBodyPartSlotsOnResize(): void { + private handleBodyPartButtonsOnResize(): void { const ratio = innerHeight / innerWidth; - const slotDimension = 100; + const slotDimension = 50; - for (const part in this.bodyPartsSlots) { - this.bodyPartsSlots[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension); + for (const part in this.bodyPartsButtons) { + this.bodyPartsButtons[part as CustomWokaBodyPart].setDisplaySize(slotDimension, slotDimension); } - const slotSize = this.bodyPartsSlots.Accessory.displayHeight; + const slotSize = this.bodyPartsButtons.Accessory.displayHeight; if (ratio > 1.6) { const middle = Math.floor(this.customWokaPreviewer.x); - const left = Math.floor(middle - slotSize - 10); - const right = Math.floor(middle + slotSize + 10); + const left = Math.floor(middle - slotSize - 23); + const right = Math.floor(middle + slotSize + 23); const top = Math.floor( - this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 1.5 + 9 + this.customWokaPreviewer.y + this.customWokaPreviewer.displayHeight * 0.5 + slotSize * 1.5 + 30 ); - const bottom = Math.floor(top + slotSize + 10); + const bottom = Math.floor(top + slotSize + 23); - this.bodyPartsSlots.Body.setPosition(left, top); - this.bodyPartsSlots.Eyes.setPosition(middle, top); - this.bodyPartsSlots.Hair.setPosition(right, top); - this.bodyPartsSlots.Clothes.setPosition(left, bottom); - this.bodyPartsSlots.Hat.setPosition(middle, bottom); - this.bodyPartsSlots.Accessory.setPosition(right, bottom); + this.bodyPartsButtons.Body.setPosition(left, top); + this.bodyPartsButtons.Eyes.setPosition(middle, top); + this.bodyPartsButtons.Hair.setPosition(right, top); + this.bodyPartsButtons.Clothes.setPosition(left, bottom); + this.bodyPartsButtons.Hat.setPosition(middle, bottom); + this.bodyPartsButtons.Accessory.setPosition(right, bottom); return; } const left = Math.floor( - this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize * 0.5 - 10 + this.customWokaPreviewer.x - this.customWokaPreviewer.displayWidth * 0.5 - slotSize * 0.5 - 24 ); - const leftEdge = Math.floor(left - slotSize - 10); const right = Math.floor( - this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize * 0.5 + 10 + this.customWokaPreviewer.x + this.customWokaPreviewer.displayWidth * 0.5 + slotSize * 0.5 + 24 ); - const rightEdge = Math.floor(right + slotSize + 10); - const top = Math.floor(0 + slotSize * 0.5 + 9); - const middle = Math.floor(top + slotSize + 10); - const bottom = Math.floor(middle + slotSize + 10); + const top = Math.floor(0 + slotSize * 0.5 + 11); + const middle = Math.floor(top + slotSize + 24); + const bottom = Math.floor(middle + slotSize + 24); - this.bodyPartsSlots.Body.setPosition(left, top); - this.bodyPartsSlots.Eyes.setPosition(left, middle); - this.bodyPartsSlots.Hair.setPosition(ratio < 0.6 ? leftEdge : left, ratio < 0.6 ? middle : bottom); - this.bodyPartsSlots.Clothes.setPosition(right, top); - this.bodyPartsSlots.Hat.setPosition(right, middle); - this.bodyPartsSlots.Accessory.setPosition(ratio < 0.6 ? rightEdge : right, ratio < 0.6 ? middle : bottom); + this.bodyPartsButtons.Body.setPosition(left, top); + this.bodyPartsButtons.Eyes.setPosition(left, middle); + this.bodyPartsButtons.Hair.setPosition(left, bottom); + this.bodyPartsButtons.Clothes.setPosition(right, top); + this.bodyPartsButtons.Hat.setPosition(right, middle); + this.bodyPartsButtons.Accessory.setPosition(right, bottom); } private handleBodyPartsDraggableGridOnResize(): void { const gridHeight = 110; const gridWidth = innerWidth / waScaleManager.getActualZoom(); + + const gridTopMargin = Math.max( + this.finishButton.y + this.finishButton.displayHeight * 0.5, + this.bodyPartsButtons.Hair.y + this.bodyPartsButtons.Hair.displayHeight * 0.5 + ); + const gridBottomMargin = this.cameras.main.worldView.y + this.cameras.main.height; + + const yPos = gridTopMargin + (gridBottomMargin - gridTopMargin) * 0.5; + const gridPos = { x: this.cameras.main.worldView.x + this.cameras.main.width / 2, - y: this.cameras.main.worldView.y + this.cameras.main.height - gridHeight * 0.5 - 5, + y: yPos, }; - this.bodyPartsDraggableGridLeftShadow.setPosition(0, this.cameras.main.worldView.y + this.cameras.main.height); + this.bodyPartsDraggableGridLeftShadow.setPosition(0, yPos); this.bodyPartsDraggableGridRightShadow.setPosition( this.cameras.main.worldView.x + this.cameras.main.width, - this.cameras.main.worldView.y + this.cameras.main.height + yPos ); try { @@ -399,34 +437,33 @@ export class CustomizeScene extends AbstractCharacterScene { }; } - private getDefaultWokaBodyPartSlotConfig(): WokaBodyPartSlotConfig { + private getWokaBodyPartSlotConfig(bodyPart?: CustomWokaBodyPart, newTextureKey?: string): WokaBodyPartSlotConfig { + const textures = this.getCurrentlySelectedWokaTexturesRecord(); + if (bodyPart && newTextureKey) { + textures[bodyPart] = newTextureKey; + } return { color: 0xffffff, borderThickness: 1, borderColor: 0xadafbc, borderSelectedColor: 0x209cee, + textureKeys: textures, offsetX: -4, offsetY: 2, }; } private bindEventHandlers(): void { + this.bindKeyboardEventHandlers(); this.events.addListener("wake", () => { waScaleManager.saveZoom(); waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1; }); - this.input.keyboard.on("keyup-ENTER", () => { - this.nextSceneToCamera(); - }); - this.input.keyboard.on("keyup-BACKSPACE", () => { - this.backToPreviousScene(); - }); - this.randomizeButton.on(Phaser.Input.Events.POINTER_UP, () => { this.randomizeOutfit(); this.clearGrid(); - this.deselectAllSlots(); + this.deselectAllButtons(); this.refreshPlayerCurrentOutfit(); }); @@ -435,52 +472,136 @@ export class CustomizeScene extends AbstractCharacterScene { }); for (const bodyPart in CustomWokaBodyPart) { - const slot = this.bodyPartsSlots[bodyPart as CustomWokaBodyPart]; - slot.on(WokaBodyPartSlotEvent.Clicked, (selected: boolean) => { + const button = this.bodyPartsButtons[bodyPart as CustomWokaBodyPart]; + button.on(IconButtonEvent.Clicked, (selected: boolean) => { if (!selected) { - this.selectedBodyPartType = bodyPart as CustomWokaBodyPart; - this.selectedItemTextureKey = slot.getContentData().key ?? slot.getContentData().bodyImageKey; - this.deselectAllSlots(); - slot.select(true); - this.populateGrid(); - if (!this.selectedItemTextureKey) { - return; - } - const selectedGridItem = this.selectGridItem(); - if (!selectedGridItem) { - return; - } - this.bodyPartsDraggableGrid.moveContentToBeginning(); - this.centerGridOnItem(selectedGridItem); - } else { - this.selectedBodyPartType = undefined; - slot.select(false); - this.clearGrid(); + this.selectBodyPartType(bodyPart as CustomWokaBodyPart); } }); } this.bodyPartsDraggableGrid.on(DraggableGridEvent.ItemClicked, (item: WokaBodyPartSlot) => { void this.bodyPartsDraggableGrid.centerOnItem(this.bodyPartsDraggableGrid.getAllItems().indexOf(item), 500); - this.bodyPartsDraggableGrid.getAllItems().forEach((slot) => (slot as WokaBodyPartSlot).select(false)); - this.changeOutfitPart(Number(item.getId())); - this.refreshPlayerCurrentOutfit(); + this.deselectAllSlots(); item.select(true); + this.setNewBodyPart(Number(item.getId())); }); } - private selectGridItem(): WokaBodyPartSlot | undefined { - const items = this.bodyPartsDraggableGrid.getAllItems() as WokaBodyPartSlot[]; - let item: WokaBodyPartSlot | undefined; - if (this.selectedBodyPartType === CustomWokaBodyPart.Body) { - item = items.find((item) => item.getContentData().bodyImageKey === this.selectedItemTextureKey); - } else { - item = items.find((item) => item.getContentData().key === this.selectedItemTextureKey); + private selectBodyPartType(bodyPart: CustomWokaBodyPart): void { + this.selectedBodyPartType = bodyPart; + this.deselectAllButtons(); + const button = this.bodyPartsButtons[bodyPart]; + button.select(true); + this.populateGrid(); + const selectedGridItem = this.selectGridItem(); + if (!selectedGridItem) { + return; } + this.bodyPartsDraggableGrid.moveContentToBeginning(); + this.centerGridOnItem(selectedGridItem); + } + + private bindKeyboardEventHandlers(): void { + this.input.keyboard.on("keyup-ENTER", () => { + this.nextSceneToCamera(); + }); + this.input.keyboard.on("keyup-BACKSPACE", () => { + this.backToPreviousScene(); + }); + this.input.keyboard.on("keydown-LEFT", () => { + this.selectNextGridItem(true); + }); + this.input.keyboard.on("keydown-RIGHT", () => { + this.selectNextGridItem(); + }); + this.input.keyboard.on("keydown-UP", () => { + this.selectNextCategory(true); + }); + this.input.keyboard.on("keydown-DOWN", () => { + this.selectNextCategory(); + }); + this.input.keyboard.on("keydown-W", () => { + this.selectNextCategory(true); + }); + this.input.keyboard.on("keydown-S", () => { + this.selectNextCategory(); + }); + this.input.keyboard.on("keydown-A", () => { + this.selectNextGridItem(true); + }); + this.input.keyboard.on("keydown-D", () => { + this.selectNextGridItem(); + }); + } + + private setNewBodyPart(bodyPartIndex: number) { + this.changeOutfitPart(bodyPartIndex); + this.refreshPlayerCurrentOutfit(); + } + + private selectGridItem(): WokaBodyPartSlot | undefined { + const bodyPartType = this.selectedBodyPartType; + if (!bodyPartType) { + return; + } + const items = this.bodyPartsDraggableGrid.getAllItems() as WokaBodyPartSlot[]; + const item = items.find( + (item) => item.getContentData()[bodyPartType] === this.getBodyPartSelectedItemId(bodyPartType) + ); item?.select(); return item; } + private getBodyPartSelectedItemId(bodyPartType: CustomWokaBodyPart): string { + const categoryIndex = CustomWokaBodyPartOrder[bodyPartType]; + return this.layers[categoryIndex][this.selectedLayers[categoryIndex]].id; + } + + private selectNextGridItem(previous: boolean = false): void { + if (!this.selectedBodyPartType) { + return; + } + const currentIndex = this.getCurrentlySelectedItemIndex(); + if (previous ? currentIndex > 0 : currentIndex < this.bodyPartsDraggableGrid.getAllItems().length - 1) { + this.deselectAllSlots(); + const item = this.bodyPartsDraggableGrid.getAllItems()[ + currentIndex + (previous ? -1 : 1) + ] as WokaBodyPartSlot; + if (item) { + item.select(); + this.setNewBodyPart(Number(item.getId())); + this.centerGridOnItem(item); + } + } + } + + private selectNextCategory(previous: boolean = false): void { + if (!this.selectedBodyPartType) { + this.selectBodyPartType(CustomWokaBodyPart.Body); + return; + } + if (previous && this.selectedBodyPartType === CustomWokaBodyPart.Body) { + return; + } + if (!previous && this.selectedBodyPartType === CustomWokaBodyPart.Accessory) { + return; + } + const index = CustomWokaBodyPartOrder[this.selectedBodyPartType] + (previous ? -1 : 1); + this.selectBodyPartType(CustomWokaBodyPart[CustomWokaBodyPartOrder[index] as CustomWokaBodyPart]); + } + + private getCurrentlySelectedItemIndex(): number { + const bodyPartType = this.selectedBodyPartType; + if (!bodyPartType) { + return -1; + } + const items = this.bodyPartsDraggableGrid.getAllItems() as WokaBodyPartSlot[]; + return items.findIndex( + (item) => item.getContentData()[bodyPartType] === this.getBodyPartSelectedItemId(bodyPartType) + ); + } + private centerGridOnItem(item: WokaBodyPartSlot, duration: number = 500): void { void this.bodyPartsDraggableGrid.centerOnItem( this.bodyPartsDraggableGrid.getAllItems().indexOf(item), @@ -515,21 +636,12 @@ export class CustomizeScene extends AbstractCharacterScene { 0, 0, { - ...this.getDefaultWokaBodyPartSlotConfig(), + ...this.getWokaBodyPartSlotConfig(this.selectedBodyPartType, bodyPartsLayer[i].id), offsetX: 0, offsetY: 0, }, i ).setDisplaySize(this.SLOT_DIMENSION, this.SLOT_DIMENSION); - if (this.selectedBodyPartType === CustomWokaBodyPart.Body) { - slot.setBodyTexture(bodyPartsLayer[i].id); - slot.setImageTexture(); - } else { - slot.setBodyTexture( - this.layers[CustomWokaBodyPartOrder.Body][this.selectedLayers[CustomWokaBodyPartOrder.Body]].id - ); - slot.setImageTexture(bodyPartsLayer[i].id); - } this.bodyPartsDraggableGrid.addItem(slot); } } @@ -538,9 +650,13 @@ export class CustomizeScene extends AbstractCharacterScene { this.bodyPartsDraggableGrid.clearAllItems(); } - private deselectAllSlots(): void { + private deselectAllButtons(): void { for (const bodyPart in CustomWokaBodyPart) { - this.bodyPartsSlots[bodyPart as CustomWokaBodyPart].select(false); + this.bodyPartsButtons[bodyPart as CustomWokaBodyPart].select(false); } } + + private deselectAllSlots(): void { + this.bodyPartsDraggableGrid.getAllItems().forEach((slot) => (slot as WokaBodyPartSlot).select(false)); + } } diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index 50c3e19f..97f855b7 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -10,8 +10,6 @@ import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore"; import { getIceServersConfig } from "../Components/Video/utils"; import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils"; import { SoundMeter } from "../Phaser/Components/SoundMeter"; -import { AudioContext } from "standardized-audio-context"; -import { Console } from "console"; import Peer from "simple-peer/simplepeer.min.js"; import { Buffer } from "buffer"; From 47b26d9a6c8e1276de2d4cef0bfb74067c55dfc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 4 Apr 2022 14:07:13 +0200 Subject: [PATCH 60/61] Removing Customize as defaut scene --- front/src/Phaser/Login/EntryScene.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index 2610023f..75846bf7 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -9,7 +9,6 @@ import { get } from "svelte/store"; import { localeDetector } from "../../i18n/locales"; import { PlayerTextures } from "../Entity/PlayerTextures"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; -import { CustomizeSceneName } from "./CustomizeScene"; export const EntrySceneName = "EntryScene"; @@ -47,8 +46,7 @@ export class EntryScene extends Scene { // Let's rescale before starting the game // We can do it at this stage. waScaleManager.applyNewSize(); - // this.scene.start(nextSceneName); - this.scene.start(CustomizeSceneName); + this.scene.start(nextSceneName); }) .catch((err) => { const $LL = get(LL); From 7acebfa70c6e2ea697a66d3f17cf51523fc9a636 Mon Sep 17 00:00:00 2001 From: Piotr 'pwh' Hanusiak Date: Mon, 4 Apr 2022 14:10:50 +0200 Subject: [PATCH 61/61] turn icon with animation --- front/public/resources/icons/icon_turn.png | Bin 0 -> 7137 bytes .../CustomizeWoka/CustomWokaPreviewer.ts | 21 ++++++++++++++++++ front/src/Phaser/Login/CustomizeScene.ts | 1 + 3 files changed, 22 insertions(+) create mode 100644 front/public/resources/icons/icon_turn.png diff --git a/front/public/resources/icons/icon_turn.png b/front/public/resources/icons/icon_turn.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1b367eeee62cad16af25ad7ca581c584dea2cf GIT binary patch literal 7137 zcmV<78y@6|P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=WHmMklJh2I&4h7fod4t6cgK*QI!iKr}p?rF6i z`&3tD-ZT;Ln{9wL>wo@xoB!fp$~l=3b4fL&i+`bp>KaeVb^oj9uf4+gd;Yocyk|eZ zZeDLVUh+Kp`#*DgKHqpfzJKBE9=3me-IV7YSDp*m*M-kNuRH7c{5VKnclYZyJQu3# zev)d*ABWnX3ytT4XDH8acn#}*(0`jxFJz)LBRBHSEpPpG{+4?LUcb&a9zB1(w|vg} z$n)Tf;US)n+B+bhruX)ET^|GVDadcn%-86@o_z_vYtQTcODxk9BcA;DhK*ld_pjTy zk2m(O5xLKA1ZV5F={&!C&$9QrXV*d^qUouq6Kwl-89vy#ls{if{A7M3&-3}I{FJL* zgOtx}T%Dg`t94{$8yoDj&2GESDsvBN;3i`3zWncaDg-i~& z3~3DBy~bkCee1d3u!TEM%SWB%V)mr}mml+|H~*C%v$vYf+N-=`#kid6nqeq&`pc(C zh}}=#;tBBE&y)V^L;X;yWH6pE7be)QeqW+T`(9iA@Aw=pYhe8dt=wpa6rkG~qL51AMvUnp^QDlvq;9rIcD) zb=8A`v8I}9skOG|Oyfq4Yc)RKc%{4Ud+4#Jo_p!Fw?X-gFycrfk230L(@j5tiJ4}e zW!Bl2H4CIzVa1hJUS-wQHnq0>4m<9&^DevY_R89m)o(xlLe}h)HFq+l&*fLvc-7?m zb_pvuiOLxnbHS4FtPD`lPC2`Z4Z$gM%Gp&NO%WY~jLMCfRv9COVObZqedX>wbAOdL z6U$%a&HYp6j8gaikvT)^zUS?StWELUI2pUUP%*tc-N*Z@G~-GWskMK6WQZY5mv>KT z$2IrZKvEhhZvIX)>k8M%(XJj_bcC;U4=TFelOqVka@N{m_T;y^zi_P?0(If8tXEPD zc@IOQ?M@-VB%b;-)?cP9(GQ#DMZ?n0oz<~E~Sn@ejJQDndX7>&`BGoUjZD85Gfhbd4ezVGU{ zje8}#V{38hT`;>ajNa|9W4dQ>UC$gYJwKCJDuztNm+RY%;R$o-of~93L!=&iZs)7X z$JJt2esfxdPp(;ewxe3^o+ss&`q-_9mQ84BfrTlRxjdK_bFd-uzm)vdPX4doA1iGg zRI#Lgr`$7=ZT2$fiZtHHGsD=EgfGpimO{9?S4LZJ`|>5lQb!1>`keY5BbP{M4}4~5 z$YT=FklKk8@nFikZ#Rilw1Ze97fH)nZl+vdz=5&?92&u1m2Z<(TDlabTX)RUFHfJ* zvg)RItuW|WTY&sol&hUxU$8}qP;kpG(QA`&K&{-&h1%w|eV~tbViqy%79#!GCrz8i zVleRUU<{Vu&d@5L(rHsePgMQ~NX%H9@72Eo9(!#xEw}Fx0xD}QThpA0kE6}j7}rMj zt>I^DW|c|sxVJPZo}7K?X-zwFp*|CYRe_jn z*G5F-xZycM6q;rFq>j7?$ghkU7aFph>Lh8Z&OWD)efB<5TYPC$=|rj%JnENl#1fU! z8W`=aw!m2pI44vkNt|;udy>ff4^=CPh|H}dy3?KJ$M!t^Tyj?9g+k*65nMjP1Ei&& z0+2K=J1OqaXw_|?Mg|sA4l`&(lq4j}%`Bx`d3TlVd|jj+DopU318lenq%44m844tr z#tXpVi-W+TJCb}Obb~JXP~7P-D8M@IGuKQ|0rm*QFT8M+TRe^ATU(_mk z(Ua>U@RyrpKCuQ76(WMPp*6n&WTH}(4~is#t~iIoC-721cN-LQ?wiCFw6R+SzG*(s zn%5v1kU7nq(XGJ}Dm%SaO=wc&hWEmDsnLy~Go;X`InabO1L9|4QIMkkI{CxTocz_K z{4ovk=gA+H1k_zUqu#h!@6|YDdL@ArXavqL0A`ikB5|O7L+LG{kby$AdterU3jp20 zuEF3U{X^u!Hoz8~3_Bn?(Z~pJga{s*l8)+@GythumPIr-2n}>s>DCj72GdQsMW)68 zq`%Qv$OhrS>GKIyfduId32rv^N3DSc2NybMZfTjTSBMmM)EO8lMik6Ej=H^|3&wW@ z;pL=c0}61~TlCF1C%(cLiStq1jOW^WdMK8Me9O`OM*Ct zIjDkuikEOBo?A7T=P1$NkabV!5F^?CR(xohTyBLVY8EGpnv=PinO?9;?l^S4CeTPp zoL(5PgBQ2@4M>Hu)m_v?+;Hpl;$-ukl1iVQoyVQFDtq@Zb59SU56od%a28wVY4e?y#!lGb`n)oikbJXG) zwX>4e=4V*oF~HD}q*@7Vp7ZV`iahz#Nh$FQp-%^N*=~Ajq~-^eM7aaN7ygFH(>OR| z3@SM$Uvyn+9;K5`JEdA_vYj2rL2$;DPF>(2&z@K8e5xUsowWXd$tY zmp_RY5I->dM=_`xueVdGTg(uUq9rmSO#kYwykHo}e$7$q0H3trDLUPjvZLrMrWP$6 z{4(@6WEk^4OB*oKNN3EOwj1R)f9>5S8xE~2HN*Fu!KFYt3Devr~+KRQN)I(^d#LhKa2O zS7;X814OTa)bPtPxUApC?*>2#IHFzzBuK~_>3*AF4mXZHN4_BpVD5?X#}&>h$tqEB z$@!&}iYy1t92E4RCKt&Nh?Yh=;ib;`yNc_w;2BN|W)~Ss^is#xf^8EhtfjR9cyaj$ zq84{gzgf#e_vYW>`;T;Q{vE#mNcZNi_@0`H79}TL8z*Si+ql+Ii&yb7+0L|-k*K93 zRW0%=-}?9X+qQ2P8{Fe!HxUK}e|*R2ts6Ku%qbBJidA%$2>D)BaQGlna~2+)Rp@93 zU^|r$ubaUZLa;TI?by* zs~c7$fyaUKB`2l@s~Ra=*Um^{sK2JbAjUv)4O7}TRWA;~*|Y>n_}bx-9bpJ$NtU~a zr|RS{2;0=E$3DKp{PNcN5O}1_EAo_Ay_XhGh~Fb@-a?G(rXb^Ki-LSxQ!W8N**#B% zgOhe!UN(P&o48#hevE#h{CDdHI>+%3hY+-Y)Xlrd&RDIDXD15)1@kJ>1a+jvWxAt& z#s0(nKegq9|6eu7C;b0}{>11T`v3hZz&rM5^AY>0l<(+|=tB|!p`baOY zWp;}KnU|~JWOwI4MM+v}PPnFeaoZzX17z#`NR^+NKuCh{TIRyqW!Ytduybw;bIr&= z%Cr8GJ$Cq6e<2<Y`fSbu%!Y|Bg$Zb87CUek&qxq>+P={aXysEVZMY}M`=Zvp zmN@I^sq`Kt*fDCez|D2okl>p5$T>Uk|Npq@^Y&=IzJQ~I-!$zUG2U7lx>ts_+h=hW z|G#mXN%(%23TZ32&fIH-HU;f0ks+ z_S#cw8IFZ!@|xX_i2^KDOIb{q2o^2HHitB&S+KVJIj4e73JrRSUJR!fVLz=pVzjYb z*SczewY9;qIvU-N=qM-Z_JV%3JNZ!@nlG&Z7GRt3c0E?C3CZTGQHyP=4FqV_W+s<{ zD91%>xB*iR*R)Nqed>n%kR-SjU|mWq_d6EHU9lfwpYK(Y0FNSKoFUdT zsO38%T(z0BSJ+peWHAiX`NTkIM=Yt42aa};6cXNh_ZVn9k58Z6*Pajll}2=3f^w49`#Pu<;{)N_)db~C}WDE4C_jDmI2}T z13Hl`IEg)bks*XE2-h`snS5$5=T5IN_^yR2UF#es7zmiV+rou~ZQ8eg%hx*js{YQG zKl$pEVo2uBmsb4V^Ho0NtG?ySm!h6HQCeqN|3Vl7d_13Tw4D{E^7P9&%Rv)!5 zuTv7z)Zy$2b_R=cOO#~9>5dRKej-5DE}VqvR-gO8l-B-7KYJ}uH72uLW!8QhV;z;H1I3a1?o!1$JoSoFjqkG+1CCc zd&geB>7vw*EJFP}y;hX+(Lk+{CWJ|8 zjI&tml6JsPlu=ffv<{8I9dyn}JA)~$*U6GWshpD~W`#BDbssGOg{+R2cZg-C0JaBe zKc9!W4;j6m&rt@mM5rXMdqn-$BR%JYk$D|4`o|MSe?DMj{{Do~pAQ(BzdvE*J;bV= z5}|Rksp{m|zV}~JMQBZJz+71fX~oMeQ^7QJ)jmMc%3%?uCkc_O(6;mr20m0BKEwEn!q7MU>ts zvE=2`hpAYb<8?7uLlVJUA!me$o^E5^&Ht3OMky(1#Dby7w? zTPIX;+G@9OmVKCIHwdx@L55|~%sK=}#XJ8%0%%8-|E`Ir_8(P;Wt&J1YyX)Rnz%mb zzD=(qi`gOMC@EWjY8|X=5(Z)Mhz1_W(x3w!kqFUWO&fKYQ7`A=W5!04GD0%YBjyyw@F;%LNl?VPHpt)2EqY1*ug zpI*Dv06B3=N!v0Y#dWswY^?cE9-Wt(&e9l)Cy+!qiPzKXR9*aQ`#u|nZ8?M1ld{Ui z$(6VJOqd+4>z5x6-Vyg_P+q42D8NUl$`V#ajuHd3@-hx-@Y5>Z+aeyqfV$K^0}UWT zqirA_W(!U5eE4vdbWKwwbIY!sozX?h6g$X}N2RcU=*L75c zV9FxwMf+P?c=XsL&{n7D9pe8Se4t!leK_Sp#*K*@DY&pMAzBbQp6?k{L(r~S2s#IL zLhV1dCt^)k5oyg4xXQCLQsjZNj*5jz)&O~iAku2}iB;`)tiltku2>`7Kx$G@~M%Sb32YAsu! zcBrVFR;?9rS~)%kOSFjlEa3vwXAxKDN5FhEJr=1RX|w|JsCB^Kx`6-GSwwGZMQHbc zw4%IB?mqrLi%TlMn@ zX*=%|3FR!BJBQ+BXwuZT%&2z)fZP{a;J677^|! zH6T{IaIpEayoaPse_r}AA2ukrR_DzB0%uKl1*MI*{Qv*~glR)VP)S2WAaHVTW@&6? z004NLeUUv#!$2IxUt6UjRR=qW2xe%VEQpG9)G8FALZ}s5buhW~3z{?}DK3tJYr(;f z#j1mgv#t)Vf*|+-;^^e0=prTlFDbN$@!+^0@9sVB-U0qbg{fxO1fXh`kxnLrY;IKu zz9N88^dpM6#7uoo6jSgVU-$6w^)AMyF z6ykH@af2>M{K$3L+gyEO|_lWtNd0d&9E_D2*5?gGuaZGRuzcJl-XJOfu++h1(}@lVq0Z7p&H3~U1z z*KJMS11@)fp(kB3Bu5I+^cM=i`x$*x4j8-zde_|ETKhPC05a57>IOJC1jdS#z3%bu zp3dI>J=5y%2hUn^%$IieW&i*H24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV z0GgZ_00007bV*G`2j&AG4lx2@-XyXB00HeuL_t(o3GJ6{Zo?o9hLdR*+JSbu9cV}D z;wmP#!NxXFbyD(&@7E76hN|0ke`_7sN9Di1ACHK}@6SFIGrc$pDmRq{FTe*t(LBUT zomNI6sTQqk@H8II`JfB2PT@)4wIl4DR8laMZ1urd6Bq_4w6+-L&H88zT9^9e>?xp_ zw9Hu%$82Ise44Yi%?rD0c@O%h8D1?D$pZESFhMc$xtZr_TE~s^tx^N-!r~EG;;k`U zmj!QKx@E9*0|-w0JoOfEZ(X^ly@@NvqrjMl!z{B>!b%6`?0NH3;8|D*7Il~bW}ru4 zC9vCFM*ZT(ki#!#eTLKC;lh%Aeu{voSbClmdn<>J&4}&Q&9?%s@#>x%xb|dj&+YJB zKWnwF!=t}Ubx6JE-#u&kR<05D@L6tJ5B?>WuUYQW_qm%Ad4x49-N2UnqiB}9Qkwy< zK6V-0)28VF^BHhYV1>&OcY%*xIt1>4%{mDZr^zARRe@oEh~Yk}G7GL~Gw2p90EALP z5-Wk_G}boOSpRs62HREXJu8N|oClk+dfsW>`Cx|OGKxo$2OS7IUPmZ9|Y4#p0 z2)lrhU=_Fr6d6LmuxK`P1Qz0lfngDR2RIboiv)Ao7X`(|X; + private turnIcon: Phaser.GameObjects.Image; private animationDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; private moving: boolean = true; + private turnIconTween?: Phaser.Tweens.Tween; + private config: CustomWokaPreviewerConfig; public readonly SIZE: number = 50; @@ -55,6 +59,12 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.background = this.scene.add.image(0, 0, "floorTexture1"); this.frame = this.scene.add.graphics(); + this.turnIcon = this.scene.add + .image(this.background.displayWidth * 0.35, this.background.displayHeight * 0.35, "iconTurn") + .setScale(0.25) + .setTintFill(0xffffff) + .setAlpha(0.5); + this.drawFrame(); this.setSize(this.SIZE, this.SIZE); this.setInteractive({ cursor: "pointer" }); @@ -68,6 +78,7 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { this.sprites.Clothes, this.sprites.Hat, this.sprites.Accessory, + this.turnIcon, ]); this.bindEventHandlers(); @@ -117,6 +128,16 @@ export class CustomWokaPreviewer extends Phaser.GameObjects.Container { const direction = this.getNextAnimationDirection(); const moving = direction === PlayerAnimationDirections.Down ? !this.moving : this.moving; this.changeAnimation(direction, moving); + + this.turnIconTween?.stop(); + this.turnIcon.setScale(0.25); + this.turnIconTween = this.scene.tweens.add({ + targets: [this.turnIcon], + duration: 100, + scale: 0.2, + yoyo: true, + ease: Easing.SineEaseIn, + }); }); } diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 4b4a97f9..1aeb0684 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -67,6 +67,7 @@ export class CustomizeScene extends AbstractCharacterScene { this.load.image("iconHair", "/resources/icons/icon_hair.png"); this.load.image("iconEyes", "/resources/icons/icon_eyes.png"); this.load.image("iconBody", "/resources/icons/icon_body.png"); + this.load.image("iconTurn", "/resources/icons/icon_turn.png"); this.load.spritesheet("floorTiles", "/resources/tilesets/floor_tiles.png", { frameWidth: 32, frameHeight: 32 }); TexturesHelper.createRectangleTexture(this, "gridEdgeShadow", this.cameras.main.width * 0.2, 115, 0x000000);