BUGFIX: More savegame loading fixes (#543)

* Fix loading issues back to pre-1.0
* Be more robust about issues with files not being maps
* Avoid non-fatal error when there's no LastExportBonus
This commit is contained in:
David Walker
2023-05-29 04:10:26 -07:00
committed by GitHub
parent 5f2a1c3f27
commit e51527aa86
3 changed files with 66 additions and 71 deletions
+51 -5
View File
@@ -5,8 +5,9 @@ import { Script } from "../Script/Script";
import { TextFile } from "../TextFile";
import { IReturnStatus } from "../types";
import { ScriptFilePath, hasScriptExtension } from "../Paths/ScriptFilePath";
import { TextFilePath, hasTextExtension } from "../Paths/TextFilePath";
import { ScriptFilePath, resolveScriptFilePath, hasScriptExtension } from "../Paths/ScriptFilePath";
import { Directory, resolveDirectory } from "../Paths/Directory";
import { TextFilePath, resolveTextFilePath, hasTextExtension } from "../Paths/TextFilePath";
import { Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { matchScriptPathExact } from "../utils/helpers/scriptKey";
@@ -313,9 +314,54 @@ export abstract class BaseServer implements IServer {
// Initializes a Server Object from a JSON save state
// Called by subclasses, not Reviver.
static fromJSONBase<T extends BaseServer>(value: IReviverValue, ctor: new () => T, keys: readonly (keyof T)[]): T {
const result = Generic_fromJSON(ctor, value.data, keys);
result.savedScripts = value.data.runningScripts;
return result;
const server = Generic_fromJSON(ctor, value.data, keys);
server.savedScripts = value.data.runningScripts;
// If textFiles is not an array, we've already done the 2.3 migration to textFiles and scripts as maps + path changes.
if (!Array.isArray(server.textFiles)) return server;
// Migrate to using maps for scripts and textfiles. This is done here, directly at load, instead of the
// usual upgrade logic, for two reasons:
// 1) Our utility functions depend on it, so the upgrade logic itself needs the data to be in maps, even the logic
// written earlier than 2.3!
// 2) If the upgrade logic throws, and then you soft-reset at the recovery screen (or maybe don't even see the
// recovery screen), you can end up with a "migrated" save that still has arrays.
const newDirectory = resolveDirectory("v2.3FileChanges/") as Directory;
let invalidScriptCount = 0;
// There was a brief dev window where Server.scripts was already a map but the filepath changes weren't in yet.
// Thus, we can't skip this logic just because it's already a map.
const oldScripts = Array.isArray(server.scripts) ? (server.scripts as Script[]) : [...server.scripts.values()];
server.scripts = new JSONMap();
// In case somehow there are previously valid filenames that can't be sanitized, they will go in a new directory with a note.
for (const script of oldScripts) {
let newFilePath = resolveScriptFilePath(script.filename);
if (!newFilePath) {
newFilePath = `${newDirectory}script${++invalidScriptCount}.js` as ScriptFilePath;
script.content = `// Original path: ${script.filename}. Path was no longer valid\n` + script.content;
}
script.filename = newFilePath;
server.scripts.set(newFilePath, script);
}
let invalidTextCount = 0;
const oldTextFiles = server.textFiles as (TextFile & { fn?: string })[];
server.textFiles = new JSONMap();
for (const textFile of oldTextFiles) {
const oldName = textFile.fn ?? textFile.filename;
delete textFile.fn;
let newFilePath = resolveTextFilePath(oldName);
if (!newFilePath) {
newFilePath = `${newDirectory}text${++invalidTextCount}.txt` as TextFilePath;
textFile.content = `// Original path: ${textFile.filename}. Path was no longer valid\n` + textFile.content;
}
textFile.filename = newFilePath;
server.textFiles.set(newFilePath, textFile);
}
if (invalidScriptCount || invalidTextCount) {
// If we had to migrate names, don't run scripts for this server.
server.savedScripts = [];
}
return server;
}
// Customize a prune list for a subclass.