forked from profectus/Profectus
Added support for reading/loading from different encodings
This commit is contained in:
parent
36c96a4e44
commit
cfe378020a
6 changed files with 163 additions and 43 deletions
27
package-lock.json
generated
27
package-lock.json
generated
|
@ -11,6 +11,7 @@
|
|||
"@pixi/particle-emitter": "^5.0.4",
|
||||
"core-js": "^3.6.5",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"nanoevents": "^6.0.2",
|
||||
"pixi.js": "^6.3.0",
|
||||
"vue": "^3.2.26",
|
||||
|
@ -26,6 +27,7 @@
|
|||
"@jetblack/operator-overloading": "^0.2.0",
|
||||
"@rushstack/eslint-patch": "^1.1.0",
|
||||
"@types/lodash.clonedeep": "^4.5.6",
|
||||
"@types/lz-string": "^1.3.34",
|
||||
"@vue/babel-plugin-jsx": "^1.1.1",
|
||||
"@vue/cli-plugin-babel": "^5.0.3",
|
||||
"@vue/cli-plugin-eslint": "^5.0.3",
|
||||
|
@ -2461,6 +2463,12 @@
|
|||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/lz-string": {
|
||||
"version": "1.3.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/lz-string/-/lz-string-1.3.34.tgz",
|
||||
"integrity": "sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||
|
@ -8592,6 +8600,14 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/lz-string": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
|
||||
"integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=",
|
||||
"bin": {
|
||||
"lz-string": "bin/bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.25.7",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||
|
@ -15010,6 +15026,12 @@
|
|||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/lz-string": {
|
||||
"version": "1.3.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/lz-string/-/lz-string-1.3.34.tgz",
|
||||
"integrity": "sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||
|
@ -19594,6 +19616,11 @@
|
|||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"lz-string": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
|
||||
"integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY="
|
||||
},
|
||||
"magic-string": {
|
||||
"version": "0.25.7",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"@pixi/particle-emitter": "^5.0.4",
|
||||
"core-js": "^3.6.5",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"nanoevents": "^6.0.2",
|
||||
"pixi.js": "^6.3.0",
|
||||
"vue": "^3.2.26",
|
||||
|
@ -27,6 +28,7 @@
|
|||
"@jetblack/operator-overloading": "^0.2.0",
|
||||
"@rushstack/eslint-patch": "^1.1.0",
|
||||
"@types/lodash.clonedeep": "^4.5.6",
|
||||
"@types/lz-string": "^1.3.34",
|
||||
"@vue/babel-plugin-jsx": "^1.1.1",
|
||||
"@vue/cli-plugin-babel": "^5.0.3",
|
||||
"@vue/cli-plugin-eslint": "^5.0.3",
|
||||
|
|
|
@ -57,23 +57,17 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import projInfo from "data/projInfo.json";
|
||||
import Modal from "components/Modal.vue";
|
||||
import player, { PlayerData } from "game/player";
|
||||
import settings from "game/settings";
|
||||
import { getUniqueID, loadSave, save, newSave } from "util/save";
|
||||
import {
|
||||
ComponentPublicInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
ref,
|
||||
shallowReactive,
|
||||
unref,
|
||||
watch
|
||||
} from "vue";
|
||||
import { ComponentPublicInstance, computed, nextTick, ref, shallowReactive, watch } from "vue";
|
||||
import Select from "./fields/Select.vue";
|
||||
import Text from "./fields/Text.vue";
|
||||
import Save from "./Save.vue";
|
||||
import Draggable from "vuedraggable";
|
||||
import LZString from "lz-string";
|
||||
|
||||
export type LoadablePlayerData = Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown };
|
||||
|
||||
|
@ -89,21 +83,32 @@ defineExpose({
|
|||
const importingFailed = ref(false);
|
||||
const saveToImport = ref("");
|
||||
|
||||
watch(saveToImport, save => {
|
||||
if (save) {
|
||||
watch(saveToImport, importedSave => {
|
||||
if (importedSave) {
|
||||
nextTick(() => {
|
||||
try {
|
||||
const playerData = JSON.parse(decodeURIComponent(escape(atob(save))));
|
||||
if (importedSave[0] === "{") {
|
||||
// plaintext. No processing needed
|
||||
} else if (importedSave[0] === "e") {
|
||||
// Assumed to be base64, which starts with e
|
||||
importedSave = decodeURIComponent(escape(atob(importedSave)));
|
||||
} else if (importedSave[0] === "ᯡ") {
|
||||
// Assumed to be lz, which starts with ᯡ
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
importedSave = LZString.decompressFromUTF16(importedSave)!;
|
||||
} else {
|
||||
console.warn("Unable to determine preset encoding", importedSave);
|
||||
importingFailed.value = true;
|
||||
return;
|
||||
}
|
||||
const playerData = JSON.parse(importedSave);
|
||||
if (typeof playerData !== "object") {
|
||||
importingFailed.value = true;
|
||||
return;
|
||||
}
|
||||
const id = getUniqueID();
|
||||
playerData.id = id;
|
||||
localStorage.setItem(
|
||||
id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||
);
|
||||
save(playerData);
|
||||
saveToImport.value = "";
|
||||
importingFailed.value = false;
|
||||
|
||||
|
@ -132,14 +137,30 @@ let bank = ref(
|
|||
const cachedSaves = shallowReactive<Record<string, LoadablePlayerData | undefined>>({});
|
||||
function getCachedSave(id: string) {
|
||||
if (cachedSaves[id] == null) {
|
||||
const save = localStorage.getItem(id);
|
||||
let save = localStorage.getItem(id);
|
||||
if (save == null) {
|
||||
cachedSaves[id] = { error: `Save doesn't exist in localStorage`, id };
|
||||
} else if (save === "dW5kZWZpbmVk") {
|
||||
cachedSaves[id] = { error: `Save is undefined`, id };
|
||||
} else {
|
||||
try {
|
||||
cachedSaves[id] = { ...JSON.parse(decodeURIComponent(escape(atob(save)))), id };
|
||||
if (save[0] === "{") {
|
||||
// plaintext. No processing needed
|
||||
} else if (save[0] === "e") {
|
||||
// Assumed to be base64, which starts with e
|
||||
save = decodeURIComponent(escape(atob(save)));
|
||||
} else if (save[0] === "ᯡ") {
|
||||
// Assumed to be lz, which starts with ᯡ
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
save = LZString.decompressFromUTF16(save)!;
|
||||
} else {
|
||||
console.warn("Unable to determine preset encoding", save);
|
||||
importingFailed.value = true;
|
||||
cachedSaves[id] = { error: "Unable to determine preset encoding", id };
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return cachedSaves[id]!;
|
||||
}
|
||||
cachedSaves[id] = { ...JSON.parse(save), id };
|
||||
} catch (error) {
|
||||
cachedSaves[id] = { error, id };
|
||||
console.warn(
|
||||
|
@ -170,7 +191,19 @@ function exportSave(id: string) {
|
|||
if (player.id === id) {
|
||||
saveToExport = save();
|
||||
} else {
|
||||
saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(saves.value[id]))));
|
||||
saveToExport = JSON.stringify(saves.value[id]);
|
||||
switch (projInfo.saveEncoding) {
|
||||
default:
|
||||
console.warn(`Unknown save encoding: ${projInfo.saveEncoding}. Defaulting to lz`);
|
||||
case "lz":
|
||||
saveToExport = LZString.compressToUTF16(saveToExport);
|
||||
break;
|
||||
case "base64":
|
||||
saveToExport = btoa(unescape(encodeURIComponent(saveToExport)));
|
||||
break;
|
||||
case "plain":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Put on clipboard. Using the clipboard API asks for permissions and stuff
|
||||
|
@ -189,10 +222,7 @@ function duplicateSave(id: string) {
|
|||
}
|
||||
|
||||
const playerData = { ...saves.value[id], id: getUniqueID() };
|
||||
localStorage.setItem(
|
||||
playerData.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||
);
|
||||
save(playerData as PlayerData);
|
||||
|
||||
settings.saves.push(playerData.id);
|
||||
}
|
||||
|
@ -214,12 +244,22 @@ function openSave(id: string) {
|
|||
}
|
||||
|
||||
function newFromPreset(preset: string) {
|
||||
const playerData = JSON.parse(decodeURIComponent(escape(atob(preset))));
|
||||
if (preset[0] === "{") {
|
||||
// plaintext. No processing needed
|
||||
} else if (preset[0] === "e") {
|
||||
// Assumed to be base64, which starts with e
|
||||
preset = decodeURIComponent(escape(atob(preset)));
|
||||
} else if (preset[0] === "ᯡ") {
|
||||
// Assumed to be lz, which starts with ᯡ
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
preset = LZString.decompressFromUTF16(preset)!;
|
||||
} else {
|
||||
console.warn("Unable to determine preset encoding", preset);
|
||||
return;
|
||||
}
|
||||
const playerData = JSON.parse(preset);
|
||||
playerData.id = getUniqueID();
|
||||
localStorage.setItem(
|
||||
playerData.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||
);
|
||||
save(playerData as PlayerData);
|
||||
|
||||
settings.saves.push(playerData.id);
|
||||
}
|
||||
|
@ -232,7 +272,7 @@ function editSave(id: string, newName: string) {
|
|||
player.name = newName;
|
||||
save();
|
||||
} else {
|
||||
localStorage.setItem(id, btoa(unescape(encodeURIComponent(JSON.stringify(currSave)))));
|
||||
save(currSave as PlayerData);
|
||||
cachedSaves[id] = undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@
|
|||
|
||||
"maxTickLength": 3600,
|
||||
"offlineLimit": 1,
|
||||
"enablePausing": true
|
||||
"enablePausing": true,
|
||||
"saveEncoding": "lz"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import projInfo from "data/projInfo.json";
|
|||
import { Themes } from "data/themes";
|
||||
import { CoercableComponent } from "features/feature";
|
||||
import { globalBus } from "game/events";
|
||||
import LZString from "lz-string";
|
||||
import { hardReset } from "util/save";
|
||||
import { reactive, watch } from "vue";
|
||||
|
||||
|
@ -23,20 +24,44 @@ const state = reactive<Partial<Settings>>({
|
|||
|
||||
watch(
|
||||
state,
|
||||
state =>
|
||||
localStorage.setItem(
|
||||
projInfo.id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(state))))
|
||||
),
|
||||
state => {
|
||||
let stringifiedSettings = JSON.stringify(state);
|
||||
switch (projInfo.saveEncoding) {
|
||||
default:
|
||||
console.warn(`Unknown save encoding: ${projInfo.saveEncoding}. Defaulting to lz`);
|
||||
case "lz":
|
||||
stringifiedSettings = LZString.compressToUTF16(stringifiedSettings);
|
||||
break;
|
||||
case "base64":
|
||||
stringifiedSettings = btoa(unescape(encodeURIComponent(stringifiedSettings)));
|
||||
break;
|
||||
case "plain":
|
||||
break;
|
||||
}
|
||||
localStorage.setItem(projInfo.id, stringifiedSettings);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
export default window.settings = state as Settings;
|
||||
|
||||
export function loadSettings(): void {
|
||||
try {
|
||||
const item: string | null = localStorage.getItem(projInfo.id);
|
||||
let item: string | null = localStorage.getItem(projInfo.id);
|
||||
if (item != null && item !== "") {
|
||||
const settings = JSON.parse(decodeURIComponent(escape(atob(item))));
|
||||
if (item[0] === "{") {
|
||||
// plaintext. No processing needed
|
||||
} else if (item[0] === "e") {
|
||||
// Assumed to be base64, which starts with e
|
||||
item = decodeURIComponent(escape(atob(item)));
|
||||
} else if (item[0] === "ᯡ") {
|
||||
// Assumed to be lz, which starts with ᯡ
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
item = LZString.decompressFromUTF16(item)!;
|
||||
} else {
|
||||
console.warn("Unable to determine settings encoding", item);
|
||||
return;
|
||||
}
|
||||
const settings = JSON.parse(item);
|
||||
if (typeof settings === "object") {
|
||||
Object.assign(state, settings);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import projInfo from "data/projInfo.json";
|
|||
import player, { Player, PlayerData, stringifySave } from "game/player";
|
||||
import settings, { loadSettings } from "game/settings";
|
||||
import { ProxyState } from "./proxies";
|
||||
import LZString from "lz-string";
|
||||
|
||||
export function setupInitialStore(player: Partial<PlayerData> = {}): Player {
|
||||
return Object.assign(
|
||||
|
@ -23,9 +24,21 @@ export function setupInitialStore(player: Partial<PlayerData> = {}): Player {
|
|||
) as Player;
|
||||
}
|
||||
|
||||
export function save(): string {
|
||||
const stringifiedSave = btoa(unescape(encodeURIComponent(stringifySave(player[ProxyState]))));
|
||||
localStorage.setItem(player.id, stringifiedSave);
|
||||
export function save(playerData?: PlayerData): string {
|
||||
let stringifiedSave = stringifySave(playerData ?? player[ProxyState]);
|
||||
switch (projInfo.saveEncoding) {
|
||||
default:
|
||||
console.warn(`Unknown save encoding: ${projInfo.saveEncoding}. Defaulting to lz`);
|
||||
case "lz":
|
||||
stringifiedSave = LZString.compressToUTF16(stringifiedSave);
|
||||
break;
|
||||
case "base64":
|
||||
stringifiedSave = btoa(unescape(encodeURIComponent(stringifiedSave)));
|
||||
break;
|
||||
case "plain":
|
||||
break;
|
||||
}
|
||||
localStorage.setItem((playerData ?? player[ProxyState]).id, stringifiedSave);
|
||||
return stringifiedSave;
|
||||
}
|
||||
|
||||
|
@ -34,12 +47,24 @@ export async function load(): Promise<void> {
|
|||
loadSettings();
|
||||
|
||||
try {
|
||||
const save = localStorage.getItem(settings.active);
|
||||
let save = localStorage.getItem(settings.active);
|
||||
if (save == null) {
|
||||
await loadSave(newSave());
|
||||
return;
|
||||
}
|
||||
const player = JSON.parse(decodeURIComponent(escape(atob(save))));
|
||||
if (save[0] === "{") {
|
||||
// plaintext. No processing needed
|
||||
} else if (save[0] === "e") {
|
||||
// Assumed to be base64, which starts with e
|
||||
save = decodeURIComponent(escape(atob(save)));
|
||||
} else if (save[0] === "ᯡ") {
|
||||
// Assumed to be lz, which starts with ᯡ
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
save = LZString.decompressFromUTF16(save)!;
|
||||
} else {
|
||||
throw `Unable to determine save encoding`;
|
||||
}
|
||||
const player = JSON.parse(save);
|
||||
if (player.modID !== projInfo.id) {
|
||||
await loadSave(newSave());
|
||||
return;
|
||||
|
@ -55,7 +80,7 @@ export async function load(): Promise<void> {
|
|||
export function newSave(): PlayerData {
|
||||
const id = getUniqueID();
|
||||
const player = setupInitialStore({ id });
|
||||
localStorage.setItem(id, btoa(unescape(encodeURIComponent(stringifySave(player)))));
|
||||
save(player);
|
||||
|
||||
settings.saves.push(id);
|
||||
|
||||
|
|
Loading…
Reference in a new issue