Merge 7c57ccf42e
into 10e0f9f0de
This commit is contained in:
commit
ba0a3f8395
|
@ -14,7 +14,7 @@ import { GameModes, getGameMode } from "#app/game-mode";
|
|||
import { BattleType } from "#app/battle";
|
||||
import TrainerData from "#app/system/trainer-data";
|
||||
import { trainerConfigs } from "#app/data/trainer-config";
|
||||
import { resetSettings, setSetting, SettingKeys } from "#app/system/settings/settings";
|
||||
import { resetSettings, setSetting, Setting, settingIndex, SettingKeys } from "#app/system/settings/settings";
|
||||
import { achvs } from "#app/system/achv";
|
||||
import EggData from "#app/system/egg-data";
|
||||
import { Egg } from "#app/data/egg";
|
||||
|
@ -65,16 +65,12 @@ export const defaultStarterSpecies: Species[] = [
|
|||
|
||||
const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
||||
|
||||
export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): string {
|
||||
export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0, username?: string): string {
|
||||
switch (dataType) {
|
||||
case GameDataType.SYSTEM:
|
||||
return "data";
|
||||
return `data_${username}`;
|
||||
case GameDataType.SESSION:
|
||||
let ret = "sessionData";
|
||||
if (slotId) {
|
||||
ret += slotId;
|
||||
}
|
||||
return ret;
|
||||
return `sessionData${slotId || ""}_${username}`;
|
||||
case GameDataType.SETTINGS:
|
||||
return "settings";
|
||||
case GameDataType.TUTORIALS:
|
||||
|
@ -82,7 +78,7 @@ export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): str
|
|||
case GameDataType.SEEN_DIALOGUES:
|
||||
return "seenDialogues";
|
||||
case GameDataType.RUN_HISTORY:
|
||||
return "runHistoryData";
|
||||
return `runHistoryData_${username}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1364,9 +1360,35 @@ export class GameData {
|
|||
});
|
||||
}
|
||||
|
||||
public getDataToExport(dataType: GameDataType, slotId: integer = 0): Promise<string | null> {
|
||||
return new Promise<string | null>(resolve => {
|
||||
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
|
||||
let promise: Promise<string | null> = Promise.resolve(null);
|
||||
|
||||
if (dataType === GameDataType.SYSTEM) {
|
||||
promise = pokerogueApi.savedata.system.get({ clientSessionId });
|
||||
} else if (dataType === GameDataType.SESSION) {
|
||||
promise = pokerogueApi.savedata.session.get({ slot: slotId, clientSessionId });
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
if (!response?.length || response[0] !== "{") {
|
||||
console.error(response);
|
||||
resolve(null);
|
||||
}
|
||||
resolve(response);
|
||||
});
|
||||
} else {
|
||||
const dataKey: string = getDataTypeKey(dataType, slotId, loggedInUser?.username);
|
||||
const data = localStorage.getItem(dataKey);
|
||||
resolve((!data || dataType === GameDataType.SETTINGS) ? data : decrypt(data, bypassLogin));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public tryExportData(dataType: GameDataType, slotId: integer = 0): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
const dataKey: string = `${getDataTypeKey(dataType, slotId)}_${loggedInUser?.username}`;
|
||||
const dataKey: string = getDataTypeKey(dataType, slotId, loggedInUser?.username);
|
||||
const handleData = (dataStr: string) => {
|
||||
switch (dataType) {
|
||||
case GameDataType.SYSTEM:
|
||||
|
@ -1381,38 +1403,56 @@ export class GameData {
|
|||
link.click();
|
||||
link.remove();
|
||||
};
|
||||
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
|
||||
let promise: Promise<string | null> = Promise.resolve(null);
|
||||
|
||||
if (dataType === GameDataType.SYSTEM) {
|
||||
promise = pokerogueApi.savedata.system.get({ clientSessionId });
|
||||
} else if (dataType === GameDataType.SESSION) {
|
||||
promise = pokerogueApi.savedata.session.get({ slot: slotId, clientSessionId });
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
if (!response?.length || response[0] !== "{") {
|
||||
console.error(response);
|
||||
resolve(false);
|
||||
return;
|
||||
this.getDataToExport(dataType, slotId)
|
||||
.then(data => {
|
||||
if (data) {
|
||||
handleData(data);
|
||||
}
|
||||
|
||||
handleData(response);
|
||||
resolve(true);
|
||||
resolve(!!data);
|
||||
});
|
||||
} else {
|
||||
const data = localStorage.getItem(dataKey);
|
||||
if (data) {
|
||||
handleData(decrypt(data, bypassLogin));
|
||||
}
|
||||
resolve(!!data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public importData(dataType: GameDataType, slotId: integer = 0): void {
|
||||
const dataKey = `${getDataTypeKey(dataType, slotId)}_${loggedInUser?.username}`;
|
||||
public validateDataToImport(dataStr: string, dataType: GameDataType): boolean {
|
||||
try {
|
||||
switch (dataType) {
|
||||
case GameDataType.SYSTEM:
|
||||
dataStr = this.convertSystemDataStr(dataStr);
|
||||
const systemData = this.parseSystemData(dataStr);
|
||||
return !!systemData.dexData && !!systemData.timestamp;
|
||||
case GameDataType.SESSION:
|
||||
const sessionData = this.parseSessionData(dataStr);
|
||||
return !!sessionData.party && !!sessionData.enemyParty && !!sessionData.timestamp;
|
||||
case GameDataType.RUN_HISTORY:
|
||||
const data = JSON.parse(dataStr);
|
||||
const keys = Object.keys(data);
|
||||
return keys.every((key) => {
|
||||
const entryKeys = Object.keys(data[key]);
|
||||
return [ "isFavorite", "isVictory", "entry" ].every(v => entryKeys.includes(v)) && entryKeys.length === 3;
|
||||
});
|
||||
case GameDataType.SETTINGS:
|
||||
return Object.entries(JSON.parse(dataStr))
|
||||
.every(([ k, v ]: [string, number]) => {
|
||||
const index: number = settingIndex(k);
|
||||
return index === -1 || Setting[index].options.length > v;
|
||||
});
|
||||
case GameDataType.TUTORIALS:
|
||||
case GameDataType.SEEN_DIALOGUES:
|
||||
return true;
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public setImportedData(dataStr: string, dataType: GameDataType, slotId: integer = 0) {
|
||||
const dataKey = getDataTypeKey(dataType, slotId, loggedInUser?.username);
|
||||
const encryptedData = (dataType === GameDataType.SETTINGS) ? dataStr : encrypt(dataStr, bypassLogin);
|
||||
localStorage.setItem(dataKey, encryptedData);
|
||||
}
|
||||
|
||||
public importData(dataType: GameDataType, slotId: integer = 0): void {
|
||||
let saveFile: any = document.getElementById("saveFile");
|
||||
if (saveFile) {
|
||||
saveFile.remove();
|
||||
|
@ -1429,41 +1469,13 @@ export class GameData {
|
|||
|
||||
reader.onload = (_ => {
|
||||
return e => {
|
||||
let dataName: string;
|
||||
let dataStr = AES.decrypt(e.target?.result?.toString()!, saveKey).toString(enc.Utf8); // TODO: is this bang correct?
|
||||
let valid = false;
|
||||
try {
|
||||
dataName = GameDataType[dataType].toLowerCase();
|
||||
switch (dataType) {
|
||||
case GameDataType.SYSTEM:
|
||||
dataStr = this.convertSystemDataStr(dataStr);
|
||||
const systemData = this.parseSystemData(dataStr);
|
||||
valid = !!systemData.dexData && !!systemData.timestamp;
|
||||
break;
|
||||
case GameDataType.SESSION:
|
||||
const sessionData = this.parseSessionData(dataStr);
|
||||
valid = !!sessionData.party && !!sessionData.enemyParty && !!sessionData.timestamp;
|
||||
break;
|
||||
case GameDataType.RUN_HISTORY:
|
||||
const data = JSON.parse(dataStr);
|
||||
const keys = Object.keys(data);
|
||||
dataName = i18next.t("menuUiHandler:RUN_HISTORY").toLowerCase();
|
||||
keys.forEach((key) => {
|
||||
const entryKeys = Object.keys(data[key]);
|
||||
valid = [ "isFavorite", "isVictory", "entry" ].every(v => entryKeys.includes(v)) && entryKeys.length === 3;
|
||||
});
|
||||
break;
|
||||
case GameDataType.SETTINGS:
|
||||
case GameDataType.TUTORIALS:
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
const dataName = (dataType === GameDataType.RUN_HISTORY)
|
||||
? i18next.t("menuUiHandler:RUN_HISTORY").toLowerCase()
|
||||
: GameDataType[dataType].toLowerCase();
|
||||
const dataStr = AES.decrypt(e.target?.result?.toString()!, saveKey).toString(enc.Utf8); // TODO: is this bang correct?
|
||||
const valid = this.validateDataToImport(dataStr, dataType);
|
||||
|
||||
const displayError = (error: string) => this.scene.ui.showText(error, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500));
|
||||
dataName = dataName!; // tell TS compiler that dataName is defined!
|
||||
|
||||
if (!valid) {
|
||||
return this.scene.ui.showText(`Your ${dataName} data could not be loaded. It may be corrupted.`, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500));
|
||||
|
@ -1471,7 +1483,7 @@ export class GameData {
|
|||
|
||||
this.scene.ui.showText(`Your ${dataName} data will be overridden and the page will reload. Proceed?`, null, () => {
|
||||
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
|
||||
localStorage.setItem(dataKey, encrypt(dataStr, bypassLogin));
|
||||
this.setImportedData(dataStr, dataType, slotId);
|
||||
|
||||
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
|
||||
updateUserInfo().then(success => {
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as BattleScene from "#app/battle-scene";
|
|||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||
import { SessionSaveData } from "#app/system/game-data";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { GameDataType } from "#enums/game-data-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
|
@ -73,4 +74,63 @@ describe("System - Game Data", () => {
|
|||
expect(account.updateUserInfo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getDataToExport", () => {
|
||||
it("should get default settings", async () => {
|
||||
const defaultSettings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
|
||||
localStorage.setItem("settings", defaultSettings);
|
||||
|
||||
const result = await game.scene.gameData.getDataToExport(GameDataType.SETTINGS);
|
||||
|
||||
expect(result).toEqual(defaultSettings);
|
||||
});
|
||||
|
||||
it("should get undefined when there is no settings", async () => {
|
||||
const result = await game.scene.gameData.getDataToExport(GameDataType.SETTINGS);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("setImportedData", () => {
|
||||
it("should set settings in local storage", () => {
|
||||
const settings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
|
||||
game.scene.gameData.setImportedData(settings, GameDataType.SETTINGS);
|
||||
|
||||
expect(localStorage.getItem("settings")).toEqual(settings);
|
||||
});
|
||||
|
||||
it("should override default settings", () => {
|
||||
const defaultSettings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
|
||||
localStorage.setItem("settings", defaultSettings);
|
||||
|
||||
const newSettings = "{\"PLAYER_GENDER\":1,\"gameVersion\":\"1.0.7\",\"GAME_SPEED\":7}";
|
||||
game.scene.gameData.setImportedData(newSettings, GameDataType.SETTINGS);
|
||||
|
||||
expect(localStorage.getItem("settings")).toEqual(newSettings);
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateDataToImport", () => {
|
||||
it("should be true when the setting data is valid", async () => {
|
||||
const settings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\"}";
|
||||
const result = await game.scene.gameData.validateDataToImport(settings, GameDataType.SETTINGS);
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should be false when the setting data is an invalid JSON", async () => {
|
||||
const settings = "";
|
||||
const result = await game.scene.gameData.validateDataToImport(settings, GameDataType.SETTINGS);
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should be false when the setting data contains an unknow value", async () => {
|
||||
const settings = "{\"PLAYER_GENDER\":0,\"gameVersion\":\"1.0.4\",\"GAME_SPEED\":999}";
|
||||
const result = await game.scene.gameData.validateDataToImport(settings, GameDataType.SETTINGS);
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -246,6 +246,22 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
},
|
||||
keepOpen: true
|
||||
});
|
||||
manageDataOptions.push({
|
||||
label: i18next.t("menuUiHandler:importSettings"),
|
||||
handler: () => {
|
||||
this.scene.gameData.importData(GameDataType.SETTINGS);
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
});
|
||||
manageDataOptions.push({
|
||||
label: i18next.t("menuUiHandler:exportSettings"),
|
||||
handler: () => {
|
||||
this.scene.gameData.tryExportData(GameDataType.SETTINGS);
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
});
|
||||
if (Utils.isLocal || Utils.isBeta) {
|
||||
manageDataOptions.push({
|
||||
label: i18next.t("menuUiHandler:importData"),
|
||||
|
|
Loading…
Reference in New Issue