Merge pull request #878 from thecodingmachine/localStorageGhost

FIX: improved the validation from localstorage for username and characterLayers
This commit is contained in:
Kharhamel 2021-04-12 14:56:51 +02:00 committed by GitHub
commit 29d43d7776
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 84 additions and 9 deletions

View File

@ -1,3 +1,5 @@
import {MAX_USERNAME_LENGTH} from "../Enum/EnvironmentVariable";
export interface CharacterTexture { export interface CharacterTexture {
id: number, id: number,
level: number, level: number,
@ -5,6 +7,23 @@ export interface CharacterTexture {
rights: string rights: string
} }
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
export function isUserNameValid(value: string): boolean {
const regexp = new RegExp('^[A-Za-z]{1,'+maxUserNameLength+'}$');
return regexp.test(value);
}
export function areCharacterLayersValid(value: string[]): boolean {
if (!value.length) return false;
for (let i = 0; i < value.length; i++) {
if (/^\w+$/.exec(value[i]) === null) {
return false;
}
}
return true;
}
export class LocalUser { export class LocalUser {
constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) { constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) {
} }

View File

@ -1,4 +1,4 @@
import {LocalUser} from "./LocalUser"; import {areCharacterLayersValid, isUserNameValid, LocalUser} from "./LocalUser";
const playerNameKey = 'playerName'; const playerNameKey = 'playerName';
const selectedPlayerKey = 'selectedPlayer'; const selectedPlayerKey = 'selectedPlayer';
@ -22,8 +22,9 @@ class LocalUserStore {
setName(name:string): void { setName(name:string): void {
localStorage.setItem(playerNameKey, name); localStorage.setItem(playerNameKey, name);
} }
getName(): string { getName(): string|null {
return localStorage.getItem(playerNameKey) || ''; const value = localStorage.getItem(playerNameKey) || '';
return isUserNameValid(value) ? value : null;
} }
setPlayerCharacterIndex(playerCharacterIndex: number): void { setPlayerCharacterIndex(playerCharacterIndex: number): void {
@ -44,7 +45,8 @@ class LocalUserStore {
localStorage.setItem(characterLayersKey, JSON.stringify(layers)); localStorage.setItem(characterLayersKey, JSON.stringify(layers));
} }
getCharacterLayers(): string[]|null { getCharacterLayers(): string[]|null {
return JSON.parse(localStorage.getItem(characterLayersKey) || "null"); const value = JSON.parse(localStorage.getItem(characterLayersKey) || "null");
return areCharacterLayersValid(value) ? value : null;
} }
setGameQualityValue(value: number): void { setGameQualityValue(value: number): void {

View File

@ -14,6 +14,7 @@ const RESOLUTION = 2;
const ZOOM_LEVEL = 1/*3/4*/; const ZOOM_LEVEL = 1/*3/4*/;
const POSITION_DELAY = 200; // Wait 200ms between sending position events const POSITION_DELAY = 200; // Wait 200ms between sending position events
const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player
export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8;
export { export {
DEBUG_MODE, DEBUG_MODE,

View File

@ -35,7 +35,7 @@ export class GameManager {
if (!this.playerName) { if (!this.playerName) {
return LoginSceneName; return LoginSceneName;
} else if (!this.characterLayers) { } else if (!this.characterLayers || !this.characterLayers.length) {
return SelectCharacterSceneName; return SelectCharacterSceneName;
} else { } else {
return EnableCameraSceneName; return EnableCameraSceneName;

View File

@ -11,6 +11,7 @@ import {localUserStore} from "../../Connexion/LocalUserStore";
import {addLoader} from "../Components/Loader"; import {addLoader} from "../Components/Loader";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
export const CustomizeSceneName = "CustomizeScene"; export const CustomizeSceneName = "CustomizeScene";
@ -111,6 +112,9 @@ export class CustomizeScene extends AbstractCharacterScene {
} }
i++; i++;
} }
if (!areCharacterLayersValid(layers)) {
return;
}
gameManager.setCharacterLayers(layers); gameManager.setCharacterLayers(layers);

View File

@ -4,6 +4,7 @@ import {TextInput} from "../Components/TextInput";
import Image = Phaser.GameObjects.Image; import Image = Phaser.GameObjects.Image;
import {SelectCharacterSceneName} from "./SelectCharacterScene"; import {SelectCharacterSceneName} from "./SelectCharacterScene";
import {ResizableScene} from "./ResizableScene"; import {ResizableScene} from "./ResizableScene";
import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser";
//todo: put this constants in a dedicated file //todo: put this constants in a dedicated file
export const LoginSceneName = "LoginScene"; export const LoginSceneName = "LoginScene";
@ -37,7 +38,7 @@ export class LoginScene extends ResizableScene {
create() { create() {
this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:'); this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:');
this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => { this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => {
this.name = text; this.name = text;
}); });
@ -50,10 +51,9 @@ export class LoginScene extends ResizableScene {
this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false); this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false);
this.input.keyboard.on('keyup-ENTER', () => { this.input.keyboard.on('keyup-ENTER', () => {
if (this.name === '') { if (isUserNameValid(this.name)) {
return this.login(this.name);
} }
this.login(this.name);
}); });
} }

View File

@ -10,6 +10,7 @@ import {loadAllDefaultModels, loadCustomTexture} from "../Entity/PlayerTexturesL
import {addLoader} from "../Components/Loader"; import {addLoader} from "../Components/Loader";
import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
//todo: put this constants in a dedicated file //todo: put this constants in a dedicated file
@ -142,6 +143,9 @@ export class SelectCharacterScene extends AbstractCharacterScene {
} }
private nextScene(): void { private nextScene(): void {
if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) {
return;
}
this.scene.stop(SelectCharacterSceneName); this.scene.stop(SelectCharacterSceneName);
if (this.selectedPlayer !== null) { if (this.selectedPlayer !== null) {
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]); gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);

View File

@ -0,0 +1,45 @@
import "jasmine";
import {areCharacterLayersValid, isUserNameValid, maxUserNameLength} from "../../../src/Connexion/LocalUser";
describe("isUserNameValid()", () => {
it("should validate name with letters", () => {
expect(isUserNameValid('toto')).toBe(true);
});
it("should not validate empty name", () => {
expect(isUserNameValid('')).toBe(false);
});
it("should not validate string with too many letters", () => {
let testString = '';
for (let i = 0; i < maxUserNameLength + 2; i++) {
testString += 'a';
}
expect(isUserNameValid(testString)).toBe(false);
});
it("should not validate spaces", () => {
expect(isUserNameValid(' ')).toBe(false);
});
it("should not validate numbers", () => {
expect(isUserNameValid('a12')).toBe(false);
});
it("should not validate special characters", () => {
expect(isUserNameValid('a&-')).toBe(false);
});
});
describe("areCharacterLayersValid()", () => {
it("should validate default textures array", () => {
expect(areCharacterLayersValid(['male1', 'male2'])).toBe(true);
});
it("should not validate an empty array", () => {
expect(areCharacterLayersValid([])).toBe(false);
});
it("should not validate space only strings", () => {
expect(areCharacterLayersValid([' ', 'male1'])).toBe(false);
});
it("should not validate empty strings", () => {
expect(areCharacterLayersValid(['', 'male1'])).toBe(false);
});
});