Added support for reading/loading from different encodings

This commit is contained in:
thepaperpilot 2022-04-17 21:15:38 -05:00
parent 36c96a4e44
commit cfe378020a
6 changed files with 163 additions and 43 deletions

27
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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;
}
}

View file

@ -18,5 +18,6 @@
"maxTickLength": 3600,
"offlineLimit": 1,
"enablePausing": true
"enablePausing": true,
"saveEncoding": "lz"
}

View file

@ -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);
}

View file

@ -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);