mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-28 10:33:47 +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 { Reviver } from "../utils/JSONReviver";
|
||||||
import { BaseGift } from "./BaseGift";
|
import { BaseGift } from "./BaseGift";
|
||||||
|
|
||||||
@ -6,15 +7,26 @@ import { StaneksGift } from "./StaneksGift";
|
|||||||
export let staneksGift = new StaneksGift();
|
export let staneksGift = new StaneksGift();
|
||||||
|
|
||||||
export function loadStaneksGift(saveString: string): void {
|
export function loadStaneksGift(saveString: string): void {
|
||||||
if (saveString) {
|
let staneksGiftData: unknown;
|
||||||
staneksGift = JSON.parse(saveString, Reviver) as StaneksGift;
|
try {
|
||||||
} else {
|
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();
|
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[][] {
|
export function zeros(width: number, height: number): number[][] {
|
||||||
const array: number[][] = [];
|
const array = [];
|
||||||
|
|
||||||
for (let i = 0; i < width; ++i) {
|
for (let i = 0; i < width; ++i) {
|
||||||
array.push(Array<number>(height).fill(0));
|
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[][] {
|
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 i = 0; i < gift.width(); i++) {
|
||||||
for (let j = 0; j < gift.height(); j++) {
|
for (let j = 0; j < gift.height(); j++) {
|
||||||
const fragment = gift.fragmentAt(i, j);
|
const fragment = gift.fragmentAt(i, j);
|
||||||
if (!fragment) continue;
|
if (!fragment) {
|
||||||
newgrid[i][j] = 1;
|
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 {
|
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;
|
const player = JSON.parse(saveString, Reviver) as PlayerObject;
|
||||||
player.money = parseFloat(player.money + "");
|
player.money = parseFloat(player.money + "");
|
||||||
player.exploits = sanitizeExploits(player.exploits);
|
player.exploits = sanitizeExploits(player.exploits);
|
||||||
|
@ -40,6 +40,10 @@ export class Remote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleMessageEvent(this: WebSocket, e: MessageEvent): void {
|
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;
|
const msg = JSON.parse(e.data as string) as RFAMessage;
|
||||||
|
|
||||||
if (!msg.method || !RFARequestHandler[msg.method]) {
|
if (!msg.method || !RFARequestHandler[msg.method]) {
|
||||||
|
@ -45,6 +45,7 @@ import { downloadContentAsFile } from "./utils/FileUtils";
|
|||||||
import { showAPIBreaks } from "./utils/APIBreaks/APIBreak";
|
import { showAPIBreaks } from "./utils/APIBreaks/APIBreak";
|
||||||
import { breakInfos261 } from "./utils/APIBreaks/2.6.1";
|
import { breakInfos261 } from "./utils/APIBreaks/2.6.1";
|
||||||
import { handleGetSaveDataInfoError } from "./Netscript/ErrorMessages";
|
import { handleGetSaveDataInfoError } from "./Netscript/ErrorMessages";
|
||||||
|
import { isObject } from "./utils/helpers/typeAssertion";
|
||||||
|
|
||||||
/* SaveObject.js
|
/* SaveObject.js
|
||||||
* Defines the object used to save/load games
|
* Defines the object used to save/load games
|
||||||
@ -220,23 +221,25 @@ class BitburnerSaveObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!decodedSaveData || decodedSaveData === "") {
|
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 {
|
try {
|
||||||
parsedSaveData = JSON.parse(decodedSaveData) as {
|
parsedSaveData = JSON.parse(decodedSaveData);
|
||||||
ctor: string;
|
|
||||||
data: {
|
|
||||||
PlayerSave: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error); // We'll handle below
|
console.error(error); // We'll handle below
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parsedSaveData || parsedSaveData.ctor !== "BitburnerSaveObject" || !parsedSaveData.data) {
|
if (
|
||||||
return Promise.reject(new Error("Save game did not seem valid"));
|
!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 = {
|
const data: ImportData = {
|
||||||
|
@ -3,6 +3,7 @@ import { defaultTheme } from "../Themes/Themes";
|
|||||||
import { defaultStyles } from "../Themes/Styles";
|
import { defaultStyles } from "../Themes/Styles";
|
||||||
import { CursorStyle, CursorBlinking, WordWrapOptions } from "../ScriptEditor/ui/Options";
|
import { CursorStyle, CursorBlinking, WordWrapOptions } from "../ScriptEditor/ui/Options";
|
||||||
import { defaultMonacoTheme } from "../ScriptEditor/ui/themes";
|
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
|
* 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,
|
disableSuffixes: false,
|
||||||
|
|
||||||
load(saveString: string) {
|
load(saveString: string) {
|
||||||
const save = JSON.parse(saveString) as {
|
const save: unknown = JSON.parse(saveString);
|
||||||
theme?: typeof Settings.theme;
|
objectAssert(save);
|
||||||
styles?: typeof Settings.styles;
|
|
||||||
overview?: typeof Settings.overview;
|
|
||||||
EditorTheme?: typeof Settings.EditorTheme;
|
|
||||||
};
|
|
||||||
save.theme && Object.assign(Settings.theme, save.theme);
|
save.theme && Object.assign(Settings.theme, save.theme);
|
||||||
save.styles && Object.assign(Settings.styles, save.styles);
|
save.styles && Object.assign(Settings.styles, save.styles);
|
||||||
save.overview && Object.assign(Settings.overview, save.overview);
|
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;
|
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. */
|
/** 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> {
|
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", () => {
|
describe("Check Save File Continuity", () => {
|
||||||
establishInitialConditions();
|
establishInitialConditions();
|
||||||
// Calling getSaveString forces save info to update
|
beforeAll(async () => {
|
||||||
saveObject.getSaveData();
|
// Calling getSaveString forces save info to update
|
||||||
|
await saveObject.getSaveData();
|
||||||
|
});
|
||||||
|
|
||||||
const savesToTest = ["FactionsSave", "PlayerSave", "CompaniesSave", "GoSave"] as const;
|
const savesToTest = ["FactionsSave", "PlayerSave", "CompaniesSave", "GoSave"] as const;
|
||||||
for (const saveToTest of savesToTest) {
|
for (const saveToTest of savesToTest) {
|
||||||
test(`${saveToTest} continuity`, () => {
|
test(`${saveToTest} continuity`, () => {
|
||||||
const parsed = JSON.parse(saveObject[saveToTest]);
|
const parsed: unknown = JSON.parse(saveObject[saveToTest]);
|
||||||
expect(parsed).toMatchSnapshot();
|
expect(parsed).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user