mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
UI: Show useful error messages when loading unsupported save data from newer versions (#2425)
This commit is contained in:
@@ -160,6 +160,20 @@ function assertParsedSaveData(parsedSaveData: unknown): asserts parsedSaveData i
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We sometimes need the raw data in the loaded save object for debugging and showing useful error messages. This object
|
||||
* contains only what we need.
|
||||
*/
|
||||
export const loadedSaveObjectMiniDump = {
|
||||
/**
|
||||
* If VersionSave exists, it is always a string. It has 3 formats:
|
||||
* - x.y: Very early versions (0.1-0.17) used this format.
|
||||
* - x.y.z: Starting from roughly 0.17, we used this format. Note that in some commits, we mistakenly used the x.y format.
|
||||
* - x: Starting from v1, we used the version number instead of the version string.
|
||||
*/
|
||||
VersionSave: undefined as string | undefined,
|
||||
};
|
||||
|
||||
class BitburnerSaveObject implements BitburnerSaveObjectType {
|
||||
PlayerSave = "";
|
||||
AllServersSave = "";
|
||||
@@ -426,6 +440,18 @@ async function loadGame(saveData: SaveData): Promise<boolean> {
|
||||
const jsonSaveString = await decodeSaveData(saveData);
|
||||
|
||||
const saveObj: unknown = JSON.parse(jsonSaveString, Reviver);
|
||||
|
||||
// Extract VersionSave ASAP for debugging and showing useful error messages later. Some checks here are redundant (
|
||||
// e.g., the object assertion) because we will do them again later, but that's okay.
|
||||
if (
|
||||
saveObj != null &&
|
||||
typeof saveObj === "object" &&
|
||||
"VersionSave" in saveObj &&
|
||||
typeof saveObj.VersionSave === "string"
|
||||
) {
|
||||
loadedSaveObjectMiniDump.VersionSave = saveObj.VersionSave;
|
||||
}
|
||||
|
||||
assertBitburnerSaveObjectType(saveObj);
|
||||
|
||||
// "Mandatory"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Settings } from "../../Settings/Settings";
|
||||
import { load } from "../../db";
|
||||
import { Router } from "../GameRoot";
|
||||
import { Page } from "../Router";
|
||||
import { type CrashReport, newIssueUrl, getCrashReport } from "../../utils/ErrorHelper";
|
||||
import { type CrashReport, newIssueUrl, getCrashReport, isSaveDataFromNewerVersions } from "../../utils/ErrorHelper";
|
||||
import { DeleteGameButton } from "./DeleteGameButton";
|
||||
import { SoftResetButton } from "./SoftResetButton";
|
||||
|
||||
@@ -16,6 +16,9 @@ import { InvalidSaveData, UnsupportedSaveData } from "../../utils/SaveDataUtils"
|
||||
import { downloadContentAsFile } from "../../utils/FileUtils";
|
||||
import { debounce } from "lodash";
|
||||
import { Engine } from "../../engine";
|
||||
import { JSONReviverError } from "../../utils/GenericReviver";
|
||||
import { loadedSaveObjectMiniDump } from "../../SaveObject";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
|
||||
export let RecoveryMode = false;
|
||||
let sourceError: unknown;
|
||||
@@ -108,6 +111,19 @@ export function RecoveryRoot({ softReset, crashReport, resetError }: IProps): Re
|
||||
Your save data is invalid. Please import a valid backup save file.
|
||||
</Typography>
|
||||
);
|
||||
} else if (
|
||||
sourceError instanceof JSONReviverError &&
|
||||
isSaveDataFromNewerVersions(loadedSaveObjectMiniDump.VersionSave)
|
||||
) {
|
||||
instructions = (
|
||||
<Typography variant="h5" color={Settings.theme.warning}>
|
||||
Your save data is from a newer version (Version number: {loadedSaveObjectMiniDump.VersionSave}). The current
|
||||
version number is {CONSTANTS.VersionNumber}.
|
||||
<br />
|
||||
Please check if you are using the correct build. This may happen when you load the save data of the dev build
|
||||
(Steam Beta or https://bitburner-official.github.io/bitburner-src) on the stable build.
|
||||
</Typography>
|
||||
);
|
||||
} else {
|
||||
instructions = (
|
||||
<Box>
|
||||
|
||||
@@ -195,3 +195,18 @@ Copy your save here if possible
|
||||
issueUrl,
|
||||
};
|
||||
}
|
||||
|
||||
export function isSaveDataFromNewerVersions(versionSave?: string): boolean {
|
||||
if (versionSave == null) {
|
||||
return false;
|
||||
}
|
||||
// x.y and x.y.z formats are from pre-v1 versions.
|
||||
if (versionSave.includes(".")) {
|
||||
return false;
|
||||
}
|
||||
const versionNumber = Number(versionSave);
|
||||
if (!Number.isFinite(versionNumber) || versionNumber <= CONSTANTS.VersionNumber) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { constructorsForReviver, isReviverValue } from "./JSONReviver";
|
||||
import { validateObject } from "./Validator";
|
||||
|
||||
export class JSONReviverError extends Error {
|
||||
ctor: string;
|
||||
constructor(message: string, ctor: string) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.ctor = ctor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic "smart reviver" function.
|
||||
* Looks for object values with a `ctor` property and a `data` property.
|
||||
@@ -25,7 +34,7 @@ export function Reviver(_key: string, value: unknown): any {
|
||||
return value.data;
|
||||
}
|
||||
// Missing constructor with no special handling. Throw error.
|
||||
throw new Error(`Could not locate constructor named ${value.ctor}. If the save data is valid, this is a bug.`);
|
||||
throw new JSONReviverError(`Could not locate constructor named ${value.ctor}.`, value.ctor);
|
||||
}
|
||||
|
||||
const obj = ctor.fromJSON(value);
|
||||
|
||||
Reference in New Issue
Block a user