mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 22:38:34 +02:00
NETSCRIPT: Expose more enums for player use (#198)
* Add support for enums at other ns layers * APIWrapper: simplified wrapping algorithm and modified to just use cloneDeep to copy enums instead of recursively wrapping enums as if they were new API layers * Improve APIWrapper typing * Changed some typings at RamCostGenerator to allow for enums at different levels without enums needing a ram cost * Added enums to ns.corporation, removed getter functions that were being used instead. * Add FactionWorkType for player use * Add ClassType and CompanyWorkPos enums * Change netscriptDefinitions to expect members of these new enums where appropriate.
This commit is contained in:
@@ -2,30 +2,40 @@ import { getRamCost } from "./RamCostGenerator";
|
||||
import type { WorkerScript } from "./WorkerScript";
|
||||
import { helpers } from "./NetscriptHelpers";
|
||||
import { ScriptArg } from "./ScriptArg";
|
||||
import { NSEnums } from "src/ScriptEditor/NetscriptDefinitions";
|
||||
import { NSFull } from "src/NetscriptFunctions";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
type ExternalFunction = (...args: any[]) => void;
|
||||
/** Generic type for an enums object */
|
||||
type Enums = Record<string, Record<string, string>>;
|
||||
/** Permissive type for the documented API functions */
|
||||
type APIFn = (...args: any[]) => void;
|
||||
/** Type for the actual wrapped function given to the player */
|
||||
type WrappedFn = (...args: unknown[]) => unknown;
|
||||
/** Type for internal, unwrapped ctx function that produces an APIFunction */
|
||||
type InternalFn<F extends APIFn> = (ctx: NetscriptContext) => ((...args: unknown[]) => ReturnType<F>) & F;
|
||||
type Key<API> = keyof API & string;
|
||||
|
||||
export type ExternalAPILayer = {
|
||||
[key: string]: ExternalAPILayer | ExternalFunction | ScriptArg[];
|
||||
export type ExternalAPI<API> = {
|
||||
[key in keyof API]: API[key] extends Enums
|
||||
? Enums
|
||||
: key extends "args"
|
||||
? ScriptArg[] // "args" required to be ScriptArg[]
|
||||
: API[key] extends APIFn
|
||||
? WrappedFn
|
||||
: ExternalAPI<API[key]>;
|
||||
};
|
||||
|
||||
type InternalFunction<F extends ExternalFunction> = (
|
||||
ctx: NetscriptContext,
|
||||
) => ((...args: unknown[]) => ReturnType<F>) & F;
|
||||
|
||||
export type InternalAPI<API> = {
|
||||
[Property in keyof API]: API[Property] extends ExternalFunction
|
||||
? InternalFunction<API[Property]>
|
||||
: API[Property] extends NSEnums
|
||||
? NSEnums
|
||||
: API[Property] extends ScriptArg[]
|
||||
[key in keyof API]: API[key] extends Enums
|
||||
? API[key] & Enums
|
||||
: key extends "args"
|
||||
? ScriptArg[]
|
||||
: API[Property] extends object
|
||||
? InternalAPI<API[Property]>
|
||||
: never;
|
||||
: API[key] extends APIFn
|
||||
? InternalFn<API[key]>
|
||||
: InternalAPI<API[key]>;
|
||||
};
|
||||
/** Any of the possible values on a internal API layer */
|
||||
type InternalValues = Enums | ScriptArg[] | InternalFn<APIFn> | InternalAPI<unknown>;
|
||||
|
||||
export type NetscriptContext = {
|
||||
workerScript: WorkerScript;
|
||||
@@ -33,77 +43,36 @@ export type NetscriptContext = {
|
||||
functionPath: string;
|
||||
};
|
||||
|
||||
function wrapFunction(
|
||||
wrappedAPI: ExternalAPILayer,
|
||||
workerScript: WorkerScript,
|
||||
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
|
||||
...tree: string[]
|
||||
): void {
|
||||
const functionPath = tree.join(".");
|
||||
const functionName = tree.pop();
|
||||
if (typeof functionName !== "string") {
|
||||
throw helpers.makeBasicErrorMsg(workerScript, "Failure occurred while wrapping netscript api", "INITIALIZATION");
|
||||
}
|
||||
const ctx = {
|
||||
workerScript,
|
||||
function: functionName,
|
||||
functionPath,
|
||||
};
|
||||
function wrappedFunction(...args: unknown[]): unknown {
|
||||
helpers.checkEnvFlags(ctx);
|
||||
helpers.updateDynamicRam(ctx, getRamCost(...tree, ctx.function));
|
||||
return func(ctx)(...args);
|
||||
}
|
||||
const parent = getNestedProperty(wrappedAPI, tree);
|
||||
Object.defineProperty(parent, functionName, {
|
||||
value: wrappedFunction,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function wrapAPI(workerScript: WorkerScript, namespace: object, args: ScriptArg[]): NSFull {
|
||||
const wrappedAPI = wrapAPILayer({}, workerScript, namespace);
|
||||
wrappedAPI.args = args;
|
||||
return wrappedAPI as unknown as NSFull;
|
||||
}
|
||||
|
||||
export function wrapAPILayer(
|
||||
wrappedAPI: ExternalAPILayer,
|
||||
workerScript: WorkerScript,
|
||||
namespace: object,
|
||||
...tree: string[]
|
||||
) {
|
||||
for (const [key, value] of Object.entries(namespace)) {
|
||||
if (typeof value === "function") {
|
||||
wrapFunction(wrappedAPI, workerScript, value, ...tree, key);
|
||||
} else if (Array.isArray(value)) {
|
||||
setNestedProperty(wrappedAPI, value.slice(), key);
|
||||
} else if (typeof value === "object") {
|
||||
wrapAPILayer(wrappedAPI, workerScript, value, ...tree, key);
|
||||
} else {
|
||||
setNestedProperty(wrappedAPI, value, ...tree, key);
|
||||
export function wrapAPI(ws: WorkerScript, internalAPI: InternalAPI<NSFull>, args: ScriptArg[]): ExternalAPI<NSFull> {
|
||||
function wrapAPILayer<API>(eLayer: ExternalAPI<API>, iLayer: InternalAPI<API>, tree: string[]): ExternalAPI<API> {
|
||||
for (const [key, value] of Object.entries(iLayer) as [Key<API>, InternalValues][]) {
|
||||
if (key === "enums") {
|
||||
(eLayer[key] as Enums) = cloneDeep(value as Enums);
|
||||
} else if (key === "args") continue;
|
||||
// Args are added in wrapAPI function and should only exist at top level
|
||||
else if (typeof value === "function") {
|
||||
wrapFunction(eLayer, value as InternalFn<APIFn>, tree, key);
|
||||
} else if (typeof value === "object") {
|
||||
wrapAPILayer((eLayer[key] = {} as ExternalAPI<API>[Key<API>]), value, [...tree, key as string]);
|
||||
} else {
|
||||
console.warn(`Unexpected data while wrapping API.`, "tree:", tree, "key:", key, "value:", value);
|
||||
throw new Error("Error while wrapping netscript API. See console.");
|
||||
}
|
||||
}
|
||||
return eLayer;
|
||||
}
|
||||
function wrapFunction<API>(eLayer: ExternalAPI<API>, func: InternalFn<APIFn>, tree: string[], key: Key<API>) {
|
||||
const arrayPath = [...tree, key];
|
||||
const functionPath = arrayPath.join(".");
|
||||
const ctx = { workerScript: ws, function: key, functionPath };
|
||||
function wrappedFunction(...args: unknown[]): unknown {
|
||||
helpers.checkEnvFlags(ctx);
|
||||
helpers.updateDynamicRam(ctx, getRamCost(...tree, key));
|
||||
return func(ctx)(...args);
|
||||
}
|
||||
(eLayer[key] as WrappedFn) = wrappedFunction;
|
||||
}
|
||||
|
||||
const wrappedAPI = wrapAPILayer({ args } as ExternalAPI<NSFull>, internalAPI, []);
|
||||
return wrappedAPI;
|
||||
}
|
||||
|
||||
function setNestedProperty(root: any, value: unknown, ...tree: string[]): void {
|
||||
let target = root;
|
||||
const key = tree.pop();
|
||||
if (!key) throw new Error("Failure occurred while wrapping netscript api (setNestedProperty)");
|
||||
for (const branch of tree) {
|
||||
target[branch] ??= {};
|
||||
target = target[branch];
|
||||
}
|
||||
target[key] = value;
|
||||
}
|
||||
|
||||
function getNestedProperty(root: any, tree: string[]): unknown {
|
||||
let target = root;
|
||||
for (const branch of tree) {
|
||||
target[branch] ??= {};
|
||||
target = target[branch];
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user