remove customize button if no body parts are available (#1952)

* 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 <piotr@ltmp.co>
Co-authored-by: David Négrier <d.negrier@thecodingmachine.com>
This commit is contained in:
Piotr Hanusiak 2022-03-11 14:30:00 +01:00 committed by GitHub
parent 46f16f1422
commit 2a73400f7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 107 additions and 57 deletions

View File

@ -81,7 +81,7 @@ jobs:
working-directory: messages working-directory: messages
- name: Build proto 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 working-directory: messages
# docker # docker

View File

@ -28,6 +28,8 @@ services:
dockerfile: pusher/Dockerfile dockerfile: pusher/Dockerfile
command: yarn run runprod command: yarn run runprod
volumes: [] volumes: []
environment:
ENABLE_OPENAPI_ENDPOINT: "false"
back: back:
image: 'wa-back-e2e' image: 'wa-back-e2e'

View File

@ -2,6 +2,7 @@
import type { Game } from "../../Phaser/Game/Game"; import type { Game } from "../../Phaser/Game/Game";
import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene"; import { SelectCharacterScene, SelectCharacterSceneName } from "../../Phaser/Login/SelectCharacterScene";
import LL from "../../i18n/i18n-svelte"; import LL from "../../i18n/i18n-svelte";
import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore";
export let game: Game; export let game: Game;
@ -40,11 +41,13 @@
class="selectCharacterSceneFormSubmit nes-btn is-primary" class="selectCharacterSceneFormSubmit nes-btn is-primary"
on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()}</button on:click|preventDefault={cameraScene}>{$LL.woka.selectWoka.continue()}</button
> >
{#if $customizeAvailableStore}
<button <button
type="submit" type="submit"
class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn" class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button
> >
{/if}
</section> </section>
</form> </form>

View File

@ -1,8 +1,6 @@
import type { SignalData } from "simple-peer"; import type { SignalData } from "simple-peer";
import type { RoomConnection } from "./RoomConnection"; import type { RoomConnection } from "./RoomConnection";
import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; 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 { export interface PointInterface {
x: number; x: number;

View File

@ -15,7 +15,14 @@ export function isUserNameValid(value: unknown): boolean {
} }
export function areCharacterLayersValid(value: string[] | null): 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; return true;
} }

View File

@ -6,7 +6,6 @@ export interface BodyResourceDescriptionListInterface {
export interface BodyResourceDescriptionInterface { export interface BodyResourceDescriptionInterface {
id: string; id: string;
label: string;
img: string; img: string;
level?: number; level?: number;
} }
@ -24,7 +23,7 @@ export const mapLayerToLevel = {
accessory: 5, accessory: 5,
}; };
enum PlayerTexturesKey { export enum PlayerTexturesKey {
Accessory = "accessory", Accessory = "accessory",
Body = "body", Body = "body",
Clothes = "clothes", Clothes = "clothes",
@ -43,7 +42,6 @@ interface PlayerTexturesCategory {
interface PlayerTexturesCollection { interface PlayerTexturesCollection {
name: string; name: string;
position: number;
textures: PlayerTexturesRecord[]; textures: PlayerTexturesRecord[];
} }
@ -54,43 +52,69 @@ interface PlayerTexturesRecord {
} }
export class PlayerTextures { export class PlayerTextures {
public static PLAYER_RESOURCES: BodyResourceDescriptionListInterface; private PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {};
public static COLOR_RESOURCES: BodyResourceDescriptionListInterface; private COLOR_RESOURCES: BodyResourceDescriptionListInterface = {};
public static EYES_RESOURCES: BodyResourceDescriptionListInterface; private EYES_RESOURCES: BodyResourceDescriptionListInterface = {};
public static HAIR_RESOURCES: BodyResourceDescriptionListInterface; private HAIR_RESOURCES: BodyResourceDescriptionListInterface = {};
public static CLOTHES_RESOURCES: BodyResourceDescriptionListInterface; private CLOTHES_RESOURCES: BodyResourceDescriptionListInterface = {};
public static HATS_RESOURCES: BodyResourceDescriptionListInterface; private HATS_RESOURCES: BodyResourceDescriptionListInterface = {};
public static ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface; private ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {};
public static LAYERS: BodyResourceDescriptionListInterface[]; private LAYERS: BodyResourceDescriptionListInterface[] = [];
public loadPlayerTexturesMetadata(metadata: PlayerTexturesMetadata): void { public loadPlayerTexturesMetadata(metadata: PlayerTexturesMetadata): void {
this.mapTexturesMetadataIntoResources(metadata); this.mapTexturesMetadataIntoResources(metadata);
} }
private mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void { public getTexturesResources(key: PlayerTexturesKey): BodyResourceDescriptionListInterface {
PlayerTextures.PLAYER_RESOURCES = this.getMappedResources(metadata.woka); switch (key) {
PlayerTextures.COLOR_RESOURCES = this.getMappedResources(metadata.body); case PlayerTexturesKey.Accessory:
PlayerTextures.EYES_RESOURCES = this.getMappedResources(metadata.eyes); return this.ACCESSORIES_RESOURCES;
PlayerTextures.HAIR_RESOURCES = this.getMappedResources(metadata.hair); case PlayerTexturesKey.Body:
PlayerTextures.CLOTHES_RESOURCES = this.getMappedResources(metadata.clothes); return this.COLOR_RESOURCES;
PlayerTextures.HATS_RESOURCES = this.getMappedResources(metadata.hat); case PlayerTexturesKey.Clothes:
PlayerTextures.ACCESSORIES_RESOURCES = this.getMappedResources(metadata.accessory); 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 = [ public getLayers(): BodyResourceDescriptionListInterface[] {
PlayerTextures.COLOR_RESOURCES, return this.LAYERS;
PlayerTextures.EYES_RESOURCES, }
PlayerTextures.HAIR_RESOURCES,
PlayerTextures.CLOTHES_RESOURCES, private mapTexturesMetadataIntoResources(metadata: PlayerTexturesMetadata): void {
PlayerTextures.HATS_RESOURCES, this.PLAYER_RESOURCES = this.getMappedResources(metadata.woka);
PlayerTextures.ACCESSORIES_RESOURCES, 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 { private getMappedResources(category: PlayerTexturesCategory): BodyResourceDescriptionListInterface {
const resources: BodyResourceDescriptionListInterface = {}; const resources: BodyResourceDescriptionListInterface = {};
if (!category) {
return {};
}
for (const collection of category.collections) { for (const collection of category.collections) {
for (const texture of collection.textures) { 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; return resources;
@ -98,5 +122,5 @@ export class PlayerTextures {
} }
export const OBJECTS: BodyResourceDescriptionInterface[] = [ export const OBJECTS: BodyResourceDescriptionInterface[] = [
{ id: "teleportation", label: "Teleport", img: "resources/objects/teleportation.png" }, { id: "teleportation", img: "resources/objects/teleportation.png" },
]; ];

View File

@ -1,6 +1,6 @@
import LoaderPlugin = Phaser.Loader.LoaderPlugin; import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import type { CharacterTexture } from "../../Connexion/LocalUser"; import type { CharacterTexture } from "../../Connexion/LocalUser";
import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures } from "./PlayerTextures"; import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures";
import CancelablePromise from "cancelable-promise"; import CancelablePromise from "cancelable-promise";
export interface FrameConfig { export interface FrameConfig {
@ -8,9 +8,12 @@ export interface FrameConfig {
frameHeight: number; frameHeight: number;
} }
export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => { export const loadAllLayers = (
load: LoaderPlugin,
playerTextures: PlayerTextures
): BodyResourceDescriptionInterface[][] => {
const returnArray: BodyResourceDescriptionInterface[][] = []; const returnArray: BodyResourceDescriptionInterface[][] = [];
PlayerTextures.LAYERS.forEach((layer) => { playerTextures.getLayers().forEach((layer) => {
const layerArray: BodyResourceDescriptionInterface[] = []; const layerArray: BodyResourceDescriptionInterface[] = [];
Object.values(layer).forEach((textureDescriptor) => { Object.values(layer).forEach((textureDescriptor) => {
layerArray.push(textureDescriptor); layerArray.push(textureDescriptor);
@ -20,8 +23,11 @@ export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterf
}); });
return returnArray; return returnArray;
}; };
export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => { export const loadAllDefaultModels = (
const returnArray = Object.values(PlayerTextures.PLAYER_RESOURCES); load: LoaderPlugin,
playerTextures: PlayerTextures
): BodyResourceDescriptionInterface[] => {
const returnArray = Object.values(playerTextures.getTexturesResources(PlayerTexturesKey.Woka));
returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => { returnArray.forEach((playerResource: BodyResourceDescriptionInterface) => {
load.spritesheet(playerResource.id, playerResource.img, { frameWidth: 32, frameHeight: 32 }); load.spritesheet(playerResource.id, playerResource.img, { frameWidth: 32, frameHeight: 32 });
}); });

View File

@ -1,12 +1,8 @@
import { ResizableScene } from "./ResizableScene"; import { ResizableScene } from "./ResizableScene";
import { localUserStore } from "../../Connexion/LocalUserStore"; import { BodyResourceDescriptionInterface, PlayerTexturesKey } from "../Entity/PlayerTextures";
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager"; import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager";
import type { CharacterTexture } from "../../Connexion/LocalUser";
import type CancelablePromise from "cancelable-promise"; import type CancelablePromise from "cancelable-promise";
import { PlayerTextures } from "../Entity/PlayerTextures"; import { PlayerTextures } from "../Entity/PlayerTextures";
import { Loader } from "../Components/Loader";
import { CustomizeSceneName } from "./CustomizeScene";
export abstract class AbstractCharacterScene extends ResizableScene { export abstract class AbstractCharacterScene extends ResizableScene {
protected playerTextures: PlayerTextures; protected playerTextures: PlayerTextures;
@ -17,7 +13,7 @@ export abstract class AbstractCharacterScene extends ResizableScene {
} }
loadCustomSceneSelectCharacters(): Promise<BodyResourceDescriptionInterface[]> { loadCustomSceneSelectCharacters(): Promise<BodyResourceDescriptionInterface[]> {
const textures = PlayerTextures.PLAYER_RESOURCES; const textures = this.playerTextures.getTexturesResources(PlayerTexturesKey.Woka);
const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = []; const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = [];
if (textures) { if (textures) {
for (const texture of Object.values(textures)) { for (const texture of Object.values(textures)) {
@ -32,7 +28,7 @@ export abstract class AbstractCharacterScene extends ResizableScene {
loadSelectSceneCharacters(): Promise<BodyResourceDescriptionInterface[]> { loadSelectSceneCharacters(): Promise<BodyResourceDescriptionInterface[]> {
const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = []; const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = [];
for (const textures of PlayerTextures.LAYERS) { for (const textures of this.playerTextures.getLayers()) {
for (const texture of Object.values(textures)) { for (const texture of Object.values(textures)) {
if (texture.level !== -1) { if (texture.level !== -1) {
continue; continue;

View File

@ -74,7 +74,7 @@ export class CustomizeScene extends AbstractCharacterScene {
}) })
.catch((e) => console.error(e)); .catch((e) => console.error(e));
this.layers = loadAllLayers(this.load); this.layers = loadAllLayers(this.load, this.playerTextures);
this.lazyloadingAttempt = false; this.lazyloadingAttempt = false;
//this function must stay at the end of preload function //this function must stay at the end of preload function

View File

@ -15,6 +15,7 @@ import { waScaleManager } from "../Services/WaScaleManager";
import { analyticsClient } from "../../Administration/AnalyticsClient"; import { analyticsClient } from "../../Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils"; import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
import { PUSHER_URL } from "../../Enum/EnvironmentVariable"; import { PUSHER_URL } from "../../Enum/EnvironmentVariable";
import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore";
//todo: put this constants in a dedicated file //todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene"; export const SelectCharacterSceneName = "SelectCharacterScene";
@ -69,7 +70,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
this.lazyloadingAttempt = true; this.lazyloadingAttempt = true;
}) })
.catch((e) => console.error(e)); .catch((e) => console.error(e));
this.playerModels = loadAllDefaultModels(this.load); this.playerModels = loadAllDefaultModels(this.load, this.playerTextures);
this.lazyloadingAttempt = false; this.lazyloadingAttempt = false;
//this function must stay at the end of preload function //this function must stay at the end of preload function
@ -78,6 +79,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
} }
create() { create() {
customizeAvailableStore.set(this.isCustomizationAvailable());
selectCharacterSceneVisibleStore.set(true); selectCharacterSceneVisibleStore.set(true);
this.events.addListener("wake", () => { this.events.addListener("wake", () => {
waScaleManager.saveZoom(); waScaleManager.saveZoom();
@ -295,4 +297,13 @@ export class SelectCharacterScene extends AbstractCharacterScene {
//move position of user //move position of user
this.moveUser(); this.moveUser();
} }
private isCustomizationAvailable(): boolean {
for (const layer of this.playerTextures.getLayers()) {
if (Object.keys(layer).length > 0) {
return true;
}
}
return false;
}
} }

View File

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

View File

@ -13,7 +13,6 @@ export const isRegisterData = new tg.IsInterface()
organizationMemberToken: tg.isNullable(tg.isString), organizationMemberToken: tg.isNullable(tg.isString),
mapUrlStart: tg.isString, mapUrlStart: tg.isString,
userUuid: tg.isString, userUuid: tg.isString,
// textures: tg.isArray(isCharacterTexture),
authToken: tg.isString, authToken: tg.isString,
}) })
.withOptionalProperties({ .withOptionalProperties({

View File

@ -4,9 +4,9 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"tsc": "tsc", "tsc": "tsc && cp -rf ./data ./dist/",
"dev": "ts-node-dev --respawn ./server.ts", "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", "runprod": "node --max-old-space-size=4096 ./dist/server.js",
"profile": "tsc && node --prof ./dist/server.js", "profile": "tsc && node --prof ./dist/server.js",
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json",

View File

@ -1,12 +1,11 @@
import swaggerJsdoc from "swagger-jsdoc";
import { BaseHttpController } from "./BaseHttpController"; import { BaseHttpController } from "./BaseHttpController";
// @ts-ignore
import LiveDirectory from "live-directory";
import * as fs from "fs"; import * as fs from "fs";
export class SwaggerController extends BaseHttpController { export class SwaggerController extends BaseHttpController {
routes() { routes() {
this.app.get("/openapi", (req, res) => { 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 = { const options = {
swaggerDefinition: { swaggerDefinition: {
openapi: "3.0.0", openapi: "3.0.0",
@ -22,6 +21,8 @@ export class SwaggerController extends BaseHttpController {
}); });
// Create a LiveDirectory instance to virtualize directory with our assets // Create a LiveDirectory instance to virtualize directory with our assets
// @ts-ignore
const LiveDirectory = require("live-directory");
const LiveAssets = new LiveDirectory({ 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. path: __dirname + "/../../node_modules/swagger-ui-dist", // We want to provide the system path to the folder. Avoid using relative paths.
keep: { keep: {