Refactoring Woka management (#1810)

* Wrap websockets with HyperExpress

* Add endpoints on pusher to resolve wokas

* getting textures urls from pusher

* Adding OpenAPI documentation for the pusher.

The pusher now exposes a "/openapi" endpoint and a "/swagger-ui/" endpoint.

* revert FRONT_URL

* playerTextures metadata is being loaded via Phaser.Loader

* fetch textures every time character or customize scene is open

* Heavy changes: refactoring the pusher to always send the textures (and the front to accept them)

* Sending character layer details to admin

* Cleaning commented code

* Fixing regex

* Fix woka endpoints on pusher

* Change error wording on pusher

* Working on integration of the woka-list with the new admin endpoint.

* Switching from "name" to "id" in texture object + using zod for woka/list validation

* Add position on default woka data

* Remove async on pusher option method

* Fix woka list url

* add options for /register

* Fxiing loading the Woka list

* Actually returning something in logout-callback

* Copying messages to back too

* 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>

* Add returns on pusher endpoints

Co-authored-by: Alexis Faizeau <a.faizeau@workadventu.re>
Co-authored-by: Hanusiak Piotr <piotr@ltmp.co>
Co-authored-by: Piotr Hanusiak <wacneg@gmail.com>
This commit is contained in:
David Négrier
2022-03-11 17:02:58 +01:00
committed by GitHub
parent d3862a3afd
commit 6540f15c5b
71 changed files with 3979 additions and 1810 deletions
@@ -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()}</button
>
<button
type="submit"
class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button
>
{#if $customizeAvailableStore}
<button
type="submit"
class="selectCharacterSceneFormCustomYourOwnSubmit nes-btn"
on:click|preventDefault={customizeScene}>{$LL.woka.selectWoka.customize()}</button
>
{/if}
</section>
</form>
+7 -26
View File
@@ -138,7 +138,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);
@@ -218,22 +218,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 {
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"));
@@ -259,7 +243,7 @@ class ConnectionManager {
public async anonymousLogin(isBenchmark: boolean = false): Promise<void> {
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.
@@ -269,7 +253,7 @@ class ConnectionManager {
}
public initBenchmark(): void {
this.localUser = new LocalUser("", []);
this.localUser = new LocalUser("");
}
public connectToRoomSocket(
@@ -346,16 +330,13 @@ class ConnectionManager {
throw new Error("No Auth code provided");
}
}
const { authToken, userUuid, textures, email, username, locale } = await Axios.get(
`${PUSHER_URL}/login-callback`,
{
params: { code, nonce, token, playUri: this.currentRoom?.key },
}
).then((res) => {
const { authToken, userUuid, email, username, locale } = 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;
+1 -1
View File
@@ -1,7 +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";
export interface PointInterface {
x: number;
@@ -83,6 +82,7 @@ export interface RoomJoinedMessageInterface {
//groups: GroupCreatedUpdatedMessageInterface[],
items: { [itemId: number]: unknown };
variables: Map<string, unknown>;
characterLayers: BodyResourceDescriptionInterface[];
}
export interface PlayGlobalMessageInterface {
+10 -11
View File
@@ -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;
@@ -14,9 +15,11 @@ 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) {
if (!value || !value.length) {
return false;
}
for (const layerName of value) {
if (layerName.length === 0 || layerName === " ") {
return false;
}
}
@@ -24,9 +27,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) {}
}
+2 -8
View File
@@ -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");
+45 -7
View File
@@ -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;
@@ -337,11 +341,28 @@ export class RoomConnection implements RoomConnection {
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.mapCharacterLayerToBodyResourceDescription.bind(this)
);
this._roomJoinedMessageStream.next({
connection: this,
room: {
items,
variables,
characterLayers,
} as RoomJoinedMessageInterface,
});
break;
@@ -351,6 +372,12 @@ export class RoomConnection implements RoomConnection {
this.closed = true;
break;
}
case "invalidTextureMessage": {
this.goToSelectYourWokaScene();
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 +618,15 @@ export class RoomConnection implements RoomConnection {
});
}*/
private mapCharacterLayerToBodyResourceDescription(
characterLayer: CharacterLayerMessage
): BodyResourceDescriptionInterface {
return {
id: characterLayer.name,
img: characterLayer.url,
};
}
// TODO: move this to protobuf utils
private toMessageUserJoined(message: UserJoinedMessageTsProto): MessageUserJoined {
const position = message.position;
@@ -598,12 +634,7 @@ 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.mapCharacterLayerToBodyResourceDescription.bind(this));
const companion = message.companion;
@@ -863,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());
}
}
+11 -2
View File
@@ -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";
@@ -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, [
{
id: "color_22",
img: "resources/customisation/character_color/character_color21.png",
},
{
id: "eyes_23",
img: "resources/customisation/character_eyes/character_eyes23.png",
},
]).then((textures) => {
this.addTextures(textures, frame);
this.invisible = false;
this.playAnimation(direction, moving);
+104 -428
View File
@@ -5,446 +5,122 @@ export interface BodyResourceDescriptionListInterface {
}
export interface BodyResourceDescriptionInterface {
name: string;
id: string;
img: string;
level?: number;
}
export const PLAYER_RESOURCES: BodyResourceDescriptionListInterface = {
male1: { name: "male1", img: "resources/characters/pipoya/Male 01-1.png" },
male2: { name: "male2", img: "resources/characters/pipoya/Male 02-2.png" },
male3: { name: "male3", img: "resources/characters/pipoya/Male 03-4.png" },
male4: { name: "male4", img: "resources/characters/pipoya/Male 09-1.png" },
male5: { name: "male5", img: "resources/characters/pipoya/Male 10-3.png" },
male6: { name: "male6", img: "resources/characters/pipoya/Male 17-2.png" },
male7: { name: "male7", img: "resources/characters/pipoya/Male 18-1.png" },
male8: { name: "male8", img: "resources/characters/pipoya/Male 16-4.png" },
male9: { name: "male9", img: "resources/characters/pipoya/Male 07-2.png" },
male10: { name: "male10", img: "resources/characters/pipoya/Male 05-3.png" },
male11: { name: "male11", img: "resources/characters/pipoya/Teacher male 02.png" },
male12: { name: "male12", img: "resources/characters/pipoya/su4 Student male 12.png" },
Female1: { name: "Female1", img: "resources/characters/pipoya/Female 01-1.png" },
Female2: { name: "Female2", img: "resources/characters/pipoya/Female 02-2.png" },
Female3: { name: "Female3", img: "resources/characters/pipoya/Female 03-4.png" },
Female4: { name: "Female4", img: "resources/characters/pipoya/Female 09-1.png" },
Female5: { name: "Female5", img: "resources/characters/pipoya/Female 10-3.png" },
Female6: { name: "Female6", img: "resources/characters/pipoya/Female 17-2.png" },
Female7: { name: "Female7", img: "resources/characters/pipoya/Female 18-1.png" },
Female8: { name: "Female8", img: "resources/characters/pipoya/Female 16-4.png" },
Female9: { name: "Female9", img: "resources/characters/pipoya/Female 07-2.png" },
Female10: { name: "Female10", img: "resources/characters/pipoya/Female 05-3.png" },
Female11: { name: "Female11", img: "resources/characters/pipoya/Teacher fmale 02.png" },
Female12: { name: "Female12", img: "resources/characters/pipoya/su4 Student fmale 12.png" },
/**
* 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,
};
export const COLOR_RESOURCES: BodyResourceDescriptionListInterface = {
color_1: { name: "color_1", img: "resources/customisation/character_color/character_color0.png" },
color_2: { name: "color_2", img: "resources/customisation/character_color/character_color1.png" },
color_3: { name: "color_3", img: "resources/customisation/character_color/character_color2.png" },
color_4: { name: "color_4", img: "resources/customisation/character_color/character_color3.png" },
color_5: { name: "color_5", img: "resources/customisation/character_color/character_color4.png" },
color_6: { name: "color_6", img: "resources/customisation/character_color/character_color5.png" },
color_7: { name: "color_7", img: "resources/customisation/character_color/character_color6.png" },
color_8: { name: "color_8", img: "resources/customisation/character_color/character_color7.png" },
color_9: { name: "color_9", img: "resources/customisation/character_color/character_color8.png" },
color_10: { name: "color_10", img: "resources/customisation/character_color/character_color9.png" },
color_11: { name: "color_11", img: "resources/customisation/character_color/character_color10.png" },
color_12: { name: "color_12", img: "resources/customisation/character_color/character_color11.png" },
color_13: { name: "color_13", img: "resources/customisation/character_color/character_color12.png" },
color_14: { name: "color_14", img: "resources/customisation/character_color/character_color13.png" },
color_15: { name: "color_15", img: "resources/customisation/character_color/character_color14.png" },
color_16: { name: "color_16", img: "resources/customisation/character_color/character_color15.png" },
color_17: { name: "color_17", img: "resources/customisation/character_color/character_color16.png" },
color_18: { name: "color_18", img: "resources/customisation/character_color/character_color17.png" },
color_19: { name: "color_19", img: "resources/customisation/character_color/character_color18.png" },
color_20: { name: "color_20", img: "resources/customisation/character_color/character_color19.png" },
color_21: { name: "color_21", img: "resources/customisation/character_color/character_color20.png" },
color_22: { name: "color_22", img: "resources/customisation/character_color/character_color21.png" },
color_23: { name: "color_23", img: "resources/customisation/character_color/character_color22.png" },
color_24: { name: "color_24", img: "resources/customisation/character_color/character_color23.png" },
color_25: { name: "color_25", img: "resources/customisation/character_color/character_color24.png" },
color_26: { name: "color_26", img: "resources/customisation/character_color/character_color25.png" },
color_27: { name: "color_27", img: "resources/customisation/character_color/character_color26.png" },
color_28: { name: "color_28", img: "resources/customisation/character_color/character_color27.png" },
color_29: { name: "color_29", img: "resources/customisation/character_color/character_color28.png" },
color_30: { name: "color_30", img: "resources/customisation/character_color/character_color29.png" },
color_31: { name: "color_31", img: "resources/customisation/character_color/character_color30.png" },
color_32: { name: "color_32", img: "resources/customisation/character_color/character_color31.png" },
color_33: { name: "color_33", img: "resources/customisation/character_color/character_color32.png" },
};
export enum PlayerTexturesKey {
Accessory = "accessory",
Body = "body",
Clothes = "clothes",
Eyes = "eyes",
Hair = "hair",
Hat = "hat",
Woka = "woka",
}
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" },
};
type PlayerTexturesMetadata = Record<PlayerTexturesKey, PlayerTexturesCategory>;
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 PlayerTexturesCategory {
collections: PlayerTexturesCollection[];
required?: boolean;
}
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" },
};
interface PlayerTexturesCollection {
name: string;
textures: PlayerTexturesRecord[];
}
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" },
};
interface PlayerTexturesRecord {
id: string;
name: string;
url: string;
}
export const ACCESSORIES_RESOURCES: BodyResourceDescriptionListInterface = {
accessory_1: {
name: "accessory_1",
img: "resources/customisation/character_accessories/character_accessories1.png",
},
accessory_2: {
name: "accessory_2",
img: "resources/customisation/character_accessories/character_accessories2.png",
},
accessory_3: {
name: "accessory_3",
img: "resources/customisation/character_accessories/character_accessories3.png",
},
accessory_4: {
name: "accessory_4",
img: "resources/customisation/character_accessories/character_accessories4.png",
},
accessory_5: {
name: "accessory_5",
img: "resources/customisation/character_accessories/character_accessories5.png",
},
accessory_6: {
name: "accessory_6",
img: "resources/customisation/character_accessories/character_accessories6.png",
},
accessory_7: {
name: "accessory_7",
img: "resources/customisation/character_accessories/character_accessories7.png",
},
accessory_8: {
name: "accessory_8",
img: "resources/customisation/character_accessories/character_accessories8.png",
},
accessory_9: {
name: "accessory_9",
img: "resources/customisation/character_accessories/character_accessories9.png",
},
accessory_10: {
name: "accessory_10",
img: "resources/customisation/character_accessories/character_accessories10.png",
},
accessory_11: {
name: "accessory_11",
img: "resources/customisation/character_accessories/character_accessories11.png",
},
accessory_12: {
name: "accessory_12",
img: "resources/customisation/character_accessories/character_accessories12.png",
},
accessory_13: {
name: "accessory_13",
img: "resources/customisation/character_accessories/character_accessories13.png",
},
accessory_14: {
name: "accessory_14",
img: "resources/customisation/character_accessories/character_accessories14.png",
},
accessory_15: {
name: "accessory_15",
img: "resources/customisation/character_accessories/character_accessories15.png",
},
accessory_16: {
name: "accessory_16",
img: "resources/customisation/character_accessories/character_accessories16.png",
},
accessory_17: {
name: "accessory_17",
img: "resources/customisation/character_accessories/character_accessories17.png",
},
accessory_18: {
name: "accessory_18",
img: "resources/customisation/character_accessories/character_accessories18.png",
},
accessory_19: {
name: "accessory_19",
img: "resources/customisation/character_accessories/character_accessories19.png",
},
accessory_20: {
name: "accessory_20",
img: "resources/customisation/character_accessories/character_accessories20.png",
},
accessory_21: {
name: "accessory_21",
img: "resources/customisation/character_accessories/character_accessories21.png",
},
accessory_22: {
name: "accessory_22",
img: "resources/customisation/character_accessories/character_accessories22.png",
},
accessory_23: {
name: "accessory_23",
img: "resources/customisation/character_accessories/character_accessories23.png",
},
accessory_24: {
name: "accessory_24",
img: "resources/customisation/character_accessories/character_accessories24.png",
},
accessory_25: {
name: "accessory_25",
img: "resources/customisation/character_accessories/character_accessories25.png",
},
accessory_26: {
name: "accessory_26",
img: "resources/customisation/character_accessories/character_accessories26.png",
},
accessory_27: {
name: "accessory_27",
img: "resources/customisation/character_accessories/character_accessories27.png",
},
accessory_28: {
name: "accessory_28",
img: "resources/customisation/character_accessories/character_accessories28.png",
},
accessory_29: {
name: "accessory_29",
img: "resources/customisation/character_accessories/character_accessories29.png",
},
accessory_30: {
name: "accessory_30",
img: "resources/customisation/character_accessories/character_accessories30.png",
},
accessory_31: {
name: "accessory_31",
img: "resources/customisation/character_accessories/character_accessories31.png",
},
accessory_32: {
name: "accessory_32",
img: "resources/customisation/character_accessories/character_accessories32.png",
},
accessory_mate_bottle: {
name: "accessory_mate_bottle",
img: "resources/customisation/character_accessories/mate_bottle1.png",
},
accessory_mask: { name: "accessory_mask", img: "resources/customisation/character_accessories/mask.png" },
};
export class PlayerTextures {
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[] = [];
export const LAYERS: BodyResourceDescriptionListInterface[] = [
COLOR_RESOURCES,
EYES_RESOURCES,
HAIR_RESOURCES,
CLOTHES_RESOURCES,
HATS_RESOURCES,
ACCESSORIES_RESOURCES,
];
public loadPlayerTexturesMetadata(metadata: PlayerTexturesMetadata): void {
this.mapTexturesMetadataIntoResources(metadata);
}
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;
}
}
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, img: texture.url };
}
}
return resources;
}
}
export const OBJECTS: BodyResourceDescriptionInterface[] = [
{ name: "teleportation", img: "resources/objects/teleportation.png" },
{ id: "teleportation", img: "resources/objects/teleportation.png" },
];
@@ -1,6 +1,6 @@
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
import type { CharacterTexture } from "../../Connexion/LocalUser";
import { BodyResourceDescriptionInterface, LAYERS, PLAYER_RESOURCES } from "./PlayerTextures";
import { BodyResourceDescriptionInterface, mapLayerToLevel, PlayerTextures, PlayerTexturesKey } from "./PlayerTextures";
import CancelablePromise from "cancelable-promise";
export interface FrameConfig {
@@ -8,33 +8,37 @@ export interface FrameConfig {
frameHeight: number;
}
export const loadAllLayers = (load: LoaderPlugin): BodyResourceDescriptionInterface[][] => {
export const loadAllLayers = (
load: LoaderPlugin,
playerTextures: PlayerTextures
): BodyResourceDescriptionInterface[][] => {
const returnArray: BodyResourceDescriptionInterface[][] = [];
LAYERS.forEach((layer) => {
playerTextures.getLayers().forEach((layer) => {
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);
});
return returnArray;
};
export const loadAllDefaultModels = (load: LoaderPlugin): BodyResourceDescriptionInterface[] => {
const returnArray = Object.values(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.name, playerResource.img, { frameWidth: 32, frameHeight: 32 });
load.spritesheet(playerResource.id, playerResource.img, { frameWidth: 32, frameHeight: 32 });
});
return returnArray;
};
export const loadCustomTexture = (
export const loadWokaTexture = (
loaderPlugin: LoaderPlugin,
texture: CharacterTexture
texture: BodyResourceDescriptionInterface
): CancelablePromise<BodyResourceDescriptionInterface> => {
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 +46,15 @@ export const loadCustomTexture = (
export const lazyLoadPlayerCharacterTextures = (
loadPlugin: LoaderPlugin,
texturekeys: Array<string | BodyResourceDescriptionInterface>
textures: BodyResourceDescriptionInterface[]
): CancelablePromise<string[]> => {
const promisesList: CancelablePromise<unknown>[] = [];
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.id)) {
promisesList.push(
createLoadingPromise(loadPlugin, playerResourceDescriptor, {
createLoadingPromise(loadPlugin, texture, {
frameWidth: 32,
frameHeight: 32,
})
@@ -64,58 +67,41 @@ export const lazyLoadPlayerCharacterTextures = (
let returnPromise: CancelablePromise<Array<string | BodyResourceDescriptionInterface>>;
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.
return returnPromise.then((keys) =>
keys.map((key) => {
return typeof key !== "string" ? key.name : key;
return typeof key !== "string" ? key.id : key;
})
);
};
export const getRessourceDescriptor = (
textureKey: string | BodyResourceDescriptionInterface
): BodyResourceDescriptionInterface => {
if (typeof textureKey !== "string" && textureKey.img) {
return textureKey;
}
const textureName: string = typeof textureKey === "string" ? textureKey : textureKey.name;
const playerResource = PLAYER_RESOURCES[textureName];
if (playerResource !== undefined) return playerResource;
for (let i = 0; i < LAYERS.length; i++) {
const playerResource = LAYERS[i][textureName];
if (playerResource !== undefined) return playerResource;
}
throw new Error("Could not find a data for texture " + textureName);
};
export const createLoadingPromise = (
loadPlugin: LoaderPlugin,
playerResourceDescriptor: BodyResourceDescriptionInterface,
frameConfig: FrameConfig
) => {
return new CancelablePromise<BodyResourceDescriptionInterface>((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 = () => {
@@ -123,7 +109,7 @@ export const createLoadingPromise = (
res(playerResourceDescriptor);
};
loadPlugin.once("filecomplete-spritesheet-" + playerResourceDescriptor.name, successCallback);
loadPlugin.once("filecomplete-spritesheet-" + playerResourceDescriptor.id, successCallback);
loadPlugin.on("loaderror", errorCallback);
});
};
+21 -11
View File
@@ -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) {
@@ -744,6 +739,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"));
@@ -1531,7 +1534,7 @@ ${escapedMessage}
this.messageSubscription?.unsubscribe();
this.userInputManager.destroy();
this.pinchManager?.destroy();
this.emoteManager.destroy();
this.emoteManager?.destroy();
this.cameraManager.destroy();
this.peerStoreUnsubscribe();
this.emoteUnsubscribe();
@@ -1690,16 +1693,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<string[]> = 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,
@@ -1,41 +1,41 @@
import { ResizableScene } from "./ResizableScene";
import { localUserStore } from "../../Connexion/LocalUserStore";
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
import { loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager";
import type { CharacterTexture } from "../../Connexion/LocalUser";
import { BodyResourceDescriptionInterface, PlayerTexturesKey } from "../Entity/PlayerTextures";
import { loadWokaTexture } from "../Entity/PlayerTexturesLoadingManager";
import type CancelablePromise from "cancelable-promise";
import { PlayerTextures } from "../Entity/PlayerTextures";
export abstract class AbstractCharacterScene extends ResizableScene {
protected playerTextures: PlayerTextures;
constructor(params: { key: string }) {
super(params);
this.playerTextures = new PlayerTextures();
}
loadCustomSceneSelectCharacters(): Promise<BodyResourceDescriptionInterface[]> {
const textures = this.getTextures();
const textures = this.playerTextures.getTexturesResources(PlayerTexturesKey.Woka);
const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = [];
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<BodyResourceDescriptionInterface[]> {
const textures = this.getTextures();
const promises: CancelablePromise<BodyResourceDescriptionInterface>[] = [];
if (textures) {
for (const texture of textures) {
for (const textures of this.playerTextures.getLayers()) {
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;
}
}
+43 -24
View File
@@ -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";
@@ -40,27 +41,45 @@ export class CustomizeScene extends AbstractCharacterScene {
}
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);
// 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()
.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.playerTextures);
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() {
@@ -192,13 +211,13 @@ export class CustomizeScene extends AbstractCharacterScene {
const children: Array<string> = new Array<string>();
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;
@@ -276,7 +295,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++;
}
@@ -287,14 +306,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);
+8
View File
@@ -7,6 +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";
export const EntrySceneName = "EntryScene";
@@ -15,6 +17,8 @@ export const EntrySceneName = "EntryScene";
* and to route to the next correct scene.
*/
export class EntryScene extends Scene {
private localeLoaded: boolean = false;
constructor() {
super({
key: EntrySceneName,
@@ -30,6 +34,10 @@ export class EntryScene extends Scene {
}
create() {
this.loadLocale();
}
private loadLocale(): void {
localeDetector()
.then(() => {
gameManager
+52 -20
View File
@@ -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,8 @@ 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";
import { customizeAvailableStore } from "../../Stores/SelectCharacterSceneStore";
//todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene";
@@ -38,25 +40,46 @@ export class SelectCharacterScene extends AbstractCharacterScene {
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;
const wokaMetadataKey = "woka-list";
this.cache.json.remove(wokaMetadataKey);
//this function must stay at the end of preload function
this.loader.addLoader();
// 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.loadSelectSceneCharacters()
.then((bodyResourceDescriptions) => {
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
this.playerModels.push(bodyResourceDescription);
});
this.lazyloadingAttempt = true;
})
.catch((e) => console.error(e));
this.playerModels = loadAllDefaultModels(this.load, this.playerTextures);
this.lazyloadingAttempt = false;
//this function must stay at the end of preload function
this.loader.addLoader();
});
}
create() {
customizeAvailableStore.set(this.isCustomizationAvailable());
selectCharacterSceneVisibleStore.set(true);
this.events.addListener("wake", () => {
waScaleManager.saveZoom();
@@ -130,16 +153,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,
});
@@ -164,7 +187,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() {
@@ -247,9 +270,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].id);
this.selectedPlayer = player;
localUserStore.setPlayerCharacterIndex(this.currentSelectUser);
}
@@ -274,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;
}
}
@@ -0,0 +1,3 @@
import { writable } from "svelte/store";
export const customizeAvailableStore = writable(false);
@@ -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");
});
});