mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-19 07:48:37 +02:00
SCRIPTS: Script modules are reused when they are imported (#461)
Also corrects some compile race conditions.
This commit is contained in:
+87
-71
@@ -1,85 +1,101 @@
|
||||
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
|
||||
|
||||
import { validateObject } from "./Validator";
|
||||
import { ObjectValidator, validateObject } from "./Validator";
|
||||
|
||||
export interface IReviverValue {
|
||||
ctor: string;
|
||||
data: any;
|
||||
}
|
||||
function isReviverValue(value: unknown): value is IReviverValue {
|
||||
return (
|
||||
typeof value === "object" && value !== null && "ctor" in value && typeof value.ctor === "string" && "data" in value
|
||||
);
|
||||
}
|
||||
|
||||
// A generic "smart reviver" function.
|
||||
// Looks for object values with a `ctor` property and
|
||||
// a `data` property. If it finds them, and finds a matching
|
||||
// constructor that has a `fromJSON` property on it, it hands
|
||||
// off to that `fromJSON` function, passing in the value.
|
||||
export function Reviver(key: string, value: IReviverValue | null): any {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof value === "object" && typeof value.ctor === "string" && typeof value.data !== "undefined") {
|
||||
// Compatibility for version v0.43.1
|
||||
// TODO Remove this eventually
|
||||
if (value.ctor === "AllServersMap") {
|
||||
console.warn("Converting AllServersMap for v0.43.1");
|
||||
return value.data;
|
||||
/**
|
||||
* A generic "smart reviver" function.
|
||||
* Looks for object values with a `ctor` property and a `data` property.
|
||||
* If it finds them, and finds a matching constructor, it hands
|
||||
* off to that `fromJSON` function, passing in the value. */
|
||||
export function Reviver(_key: string, value: unknown): any {
|
||||
if (!isReviverValue(value)) return value;
|
||||
const ctor = constructorsForReviver[value.ctor];
|
||||
if (!ctor) {
|
||||
// Known missing constructors with special handling.
|
||||
switch (value.ctor) {
|
||||
case "AllServersMap":
|
||||
console.warn("Converting AllServersMap for v0.43.1");
|
||||
return value.data;
|
||||
}
|
||||
|
||||
const ctor = Reviver.constructors[value.ctor];
|
||||
|
||||
if (typeof ctor === "function" && typeof ctor.fromJSON === "function") {
|
||||
const obj = ctor.fromJSON(value);
|
||||
if (ctor.validationData !== undefined) {
|
||||
validateObject(obj, ctor.validationData);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace Reviver {
|
||||
export const constructors: { [key: string]: any } = {};
|
||||
}
|
||||
|
||||
// A generic "toJSON" function that creates the data expected
|
||||
// by Reviver.
|
||||
// `ctorName` The name of the constructor to use to revive it
|
||||
// `obj` The object being serialized
|
||||
// `keys` (Optional) Array of the properties to serialize,
|
||||
// if not given then all of the objects "own" properties
|
||||
// that don't have function values will be serialized.
|
||||
// (Note: If you list a property in `keys`, it will be serialized
|
||||
// regardless of whether it's an "own" property.)
|
||||
// Returns: The structure (which will then be turned into a string
|
||||
// as part of the JSON.stringify algorithm)
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function Generic_toJSON(ctorName: string, obj: Record<string, any>, keys?: string[]): IReviverValue {
|
||||
if (!keys) {
|
||||
keys = Object.keys(obj); // Only "own" properties are included
|
||||
// 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.`);
|
||||
}
|
||||
|
||||
const data: Record<string, unknown> = {};
|
||||
for (let index = 0; index < keys.length; ++index) {
|
||||
const key = keys[index];
|
||||
data[key] = obj[key];
|
||||
}
|
||||
return { ctor: ctorName, data: data };
|
||||
}
|
||||
|
||||
// A generic "fromJSON" function for use with Reviver: Just calls the
|
||||
// constructor function with no arguments, then applies all of the
|
||||
// key/value pairs from the raw data to the instance. Only useful for
|
||||
// constructors that can be reasonably called without arguments!
|
||||
// `ctor` The constructor to call
|
||||
// `data` The data to apply
|
||||
// Returns: The object
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function Generic_fromJSON<T>(ctor: new () => T, data: any): T {
|
||||
const obj: any = new ctor();
|
||||
for (const name in data) {
|
||||
obj[name] = data[name];
|
||||
const obj = ctor.fromJSON(value);
|
||||
if (ctor.validationData !== undefined) {
|
||||
validateObject(obj, ctor.validationData);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export const constructorsForReviver: Partial<
|
||||
Record<
|
||||
string,
|
||||
(new () => object) & {
|
||||
fromJSON: (value: IReviverValue) => any;
|
||||
validationData?: ObjectValidator<any>;
|
||||
}
|
||||
>
|
||||
> = {};
|
||||
|
||||
/**
|
||||
* A generic "toJSON" function that creates the data expected by Reviver.
|
||||
*
|
||||
* @param ctorName String name of the constructor, part of the reviver JSON.
|
||||
* @param obj The object to convert to stringified data in the reviver JSON.
|
||||
* @param keys If provided, only these keys will be saved to the reviver JSON data. */
|
||||
export function Generic_toJSON<T extends Record<string, any>>(
|
||||
ctorName: string,
|
||||
obj: T,
|
||||
keys?: readonly (keyof T)[],
|
||||
): IReviverValue {
|
||||
const data = {} as T;
|
||||
// keys provided: only save data for the provided keys
|
||||
if (keys) {
|
||||
for (const key of keys) data[key] = obj[key];
|
||||
return { ctor: ctorName, data: data };
|
||||
}
|
||||
// no keys provided: save all own keys of the object
|
||||
for (const [key, val] of Object.entries(obj) as [keyof T, T[keyof T]][]) data[key] = val;
|
||||
return { ctor: ctorName, data: data };
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic "fromJSON" function for use with Reviver: Just calls the
|
||||
* constructor function with no arguments, then applies all of the
|
||||
* key/value pairs from the raw data to the instance. Only useful for
|
||||
* constructors that can be reasonably called without arguments!
|
||||
*
|
||||
* @param ctor The constructor to call
|
||||
* @param data The saved data to restore to the constructed object
|
||||
* @param keys If provided, only these keys will be restored from data.
|
||||
* @returns The object */
|
||||
export function Generic_fromJSON<T extends Record<string, any>>(
|
||||
ctor: new () => T,
|
||||
// data can actually be anything. We're just pretending it has the right keys for T. Save data is not type validated.
|
||||
data: Record<keyof T, any>,
|
||||
keys?: readonly (keyof T)[],
|
||||
): T {
|
||||
const obj = new ctor();
|
||||
// If keys were provided, just load the provided keys (if they are in the data)
|
||||
if (keys) {
|
||||
for (const key of keys) {
|
||||
const val = data[key];
|
||||
if (val) obj[key] = val;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
// No keys provided: load every key in data
|
||||
for (const [key, val] of Object.entries(data) as [keyof T, T[keyof T]][]) obj[key] = val;
|
||||
return obj;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user