mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-24 16:43:51 +01:00
CODEBASE: Recheck all usages of typecasting with JSON.parse (#1775)
This commit is contained in:
parent
70a231e421
commit
8c4fcfe045
@ -1,3 +1,4 @@
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Reviver } from "../utils/JSONReviver";
|
||||
import { BaseGift } from "./BaseGift";
|
||||
|
||||
@ -6,15 +7,26 @@ import { StaneksGift } from "./StaneksGift";
|
||||
export let staneksGift = new StaneksGift();
|
||||
|
||||
export function loadStaneksGift(saveString: string): void {
|
||||
if (saveString) {
|
||||
staneksGift = JSON.parse(saveString, Reviver) as StaneksGift;
|
||||
} else {
|
||||
let staneksGiftData: unknown;
|
||||
try {
|
||||
staneksGiftData = JSON.parse(saveString, Reviver);
|
||||
if (!(staneksGiftData instanceof StaneksGift)) {
|
||||
throw new Error(`Data of Stanek's Gift is not an instance of "StaneksGift"`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
console.error("Invalid StaneksGiftSave:", saveString);
|
||||
staneksGift = new StaneksGift();
|
||||
setTimeout(() => {
|
||||
dialogBoxCreate(`Cannot load data of Stanek's Gift. Stanek's Gift is reset. Error: ${error}.`);
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
staneksGift = staneksGiftData;
|
||||
}
|
||||
|
||||
export function zeros(width: number, height: number): number[][] {
|
||||
const array: number[][] = [];
|
||||
const array = [];
|
||||
|
||||
for (let i = 0; i < width; ++i) {
|
||||
array.push(Array<number>(height).fill(0));
|
||||
@ -24,14 +36,16 @@ export function zeros(width: number, height: number): number[][] {
|
||||
}
|
||||
|
||||
export function calculateGrid(gift: BaseGift): number[][] {
|
||||
const newgrid = zeros(gift.width(), gift.height()) as unknown as number[][];
|
||||
const newGrid = zeros(gift.width(), gift.height());
|
||||
for (let i = 0; i < gift.width(); i++) {
|
||||
for (let j = 0; j < gift.height(); j++) {
|
||||
const fragment = gift.fragmentAt(i, j);
|
||||
if (!fragment) continue;
|
||||
newgrid[i][j] = 1;
|
||||
if (!fragment) {
|
||||
continue;
|
||||
}
|
||||
newGrid[i][j] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return newgrid;
|
||||
return newGrid;
|
||||
}
|
||||
|
@ -11,6 +11,11 @@ export function setPlayer(playerObj: PlayerObject): void {
|
||||
}
|
||||
|
||||
export function loadPlayer(saveString: string): PlayerObject {
|
||||
/**
|
||||
* If we want to check player with "instanceof PlayerObject", we have to import PlayerObject normally (not "import
|
||||
* type"). It will create a cyclic dependency. Fixing this cyclic dependency is really hard. It's not worth the
|
||||
* effort, so we typecast it here.
|
||||
*/
|
||||
const player = JSON.parse(saveString, Reviver) as PlayerObject;
|
||||
player.money = parseFloat(player.money + "");
|
||||
player.exploits = sanitizeExploits(player.exploits);
|
||||
|
@ -40,6 +40,10 @@ export class Remote {
|
||||
}
|
||||
|
||||
function handleMessageEvent(this: WebSocket, e: MessageEvent): void {
|
||||
/**
|
||||
* Validating e.data and the result of JSON.parse() is too troublesome, so we typecast them here. If the data is
|
||||
* invalid, it means the RFA "client" (the tool that the player is using) is buggy, but that's not our problem.
|
||||
*/
|
||||
const msg = JSON.parse(e.data as string) as RFAMessage;
|
||||
|
||||
if (!msg.method || !RFARequestHandler[msg.method]) {
|
||||
|
@ -45,6 +45,7 @@ import { downloadContentAsFile } from "./utils/FileUtils";
|
||||
import { showAPIBreaks } from "./utils/APIBreaks/APIBreak";
|
||||
import { breakInfos261 } from "./utils/APIBreaks/2.6.1";
|
||||
import { handleGetSaveDataInfoError } from "./Netscript/ErrorMessages";
|
||||
import { isObject } from "./utils/helpers/typeAssertion";
|
||||
|
||||
/* SaveObject.js
|
||||
* Defines the object used to save/load games
|
||||
@ -220,23 +221,25 @@ class BitburnerSaveObject {
|
||||
}
|
||||
|
||||
if (!decodedSaveData || decodedSaveData === "") {
|
||||
return Promise.reject(new Error("Save game is invalid"));
|
||||
console.error("decodedSaveData:", decodedSaveData);
|
||||
return Promise.reject(new Error("Save game is invalid. The save data cannot be decoded."));
|
||||
}
|
||||
|
||||
let parsedSaveData;
|
||||
let parsedSaveData: unknown;
|
||||
try {
|
||||
parsedSaveData = JSON.parse(decodedSaveData) as {
|
||||
ctor: string;
|
||||
data: {
|
||||
PlayerSave: string;
|
||||
};
|
||||
};
|
||||
parsedSaveData = JSON.parse(decodedSaveData);
|
||||
} catch (error) {
|
||||
console.error(error); // We'll handle below
|
||||
}
|
||||
|
||||
if (!parsedSaveData || parsedSaveData.ctor !== "BitburnerSaveObject" || !parsedSaveData.data) {
|
||||
return Promise.reject(new Error("Save game did not seem valid"));
|
||||
if (
|
||||
!isObject(parsedSaveData) ||
|
||||
parsedSaveData.ctor !== "BitburnerSaveObject" ||
|
||||
!isObject(parsedSaveData.data) ||
|
||||
typeof parsedSaveData.data.PlayerSave !== "string"
|
||||
) {
|
||||
console.error("decodedSaveData:", decodedSaveData);
|
||||
return Promise.reject(new Error("Save game is invalid. The decoded save data is not valid."));
|
||||
}
|
||||
|
||||
const data: ImportData = {
|
||||
|
@ -3,6 +3,7 @@ import { defaultTheme } from "../Themes/Themes";
|
||||
import { defaultStyles } from "../Themes/Styles";
|
||||
import { CursorStyle, CursorBlinking, WordWrapOptions } from "../ScriptEditor/ui/Options";
|
||||
import { defaultMonacoTheme } from "../ScriptEditor/ui/themes";
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
|
||||
/**
|
||||
* This function won't be able to catch **all** invalid hostnames, and it's still fine. In order to validate a hostname
|
||||
@ -157,12 +158,8 @@ export const Settings = {
|
||||
disableSuffixes: false,
|
||||
|
||||
load(saveString: string) {
|
||||
const save = JSON.parse(saveString) as {
|
||||
theme?: typeof Settings.theme;
|
||||
styles?: typeof Settings.styles;
|
||||
overview?: typeof Settings.overview;
|
||||
EditorTheme?: typeof Settings.EditorTheme;
|
||||
};
|
||||
const save: unknown = JSON.parse(saveString);
|
||||
objectAssert(save);
|
||||
save.theme && Object.assign(Settings.theme, save.theme);
|
||||
save.styles && Object.assign(Settings.styles, save.styles);
|
||||
save.overview && Object.assign(Settings.overview, save.overview);
|
||||
|
@ -35,7 +35,9 @@ function getFriendlyType(v: unknown): string {
|
||||
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
||||
}
|
||||
|
||||
//All assertion functions used here should return the friendlyType of the input.
|
||||
export function isObject(v: unknown): v is Record<string, unknown> {
|
||||
return getFriendlyType(v) === "object";
|
||||
}
|
||||
|
||||
/** For non-objects, and for array/null, throws an error with the friendlyType of v. */
|
||||
export function objectAssert(v: unknown): asserts v is Record<string, unknown> {
|
||||
|
@ -10,13 +10,15 @@ import { Companies } from "../../src/Company/Companies";
|
||||
|
||||
describe("Check Save File Continuity", () => {
|
||||
establishInitialConditions();
|
||||
// Calling getSaveString forces save info to update
|
||||
saveObject.getSaveData();
|
||||
beforeAll(async () => {
|
||||
// Calling getSaveString forces save info to update
|
||||
await saveObject.getSaveData();
|
||||
});
|
||||
|
||||
const savesToTest = ["FactionsSave", "PlayerSave", "CompaniesSave", "GoSave"] as const;
|
||||
for (const saveToTest of savesToTest) {
|
||||
test(`${saveToTest} continuity`, () => {
|
||||
const parsed = JSON.parse(saveObject[saveToTest]);
|
||||
const parsed: unknown = JSON.parse(saveObject[saveToTest]);
|
||||
expect(parsed).toMatchSnapshot();
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user