bitburner-src/src/Netscript/APIWrapper.ts

102 lines
3.0 KiB
TypeScript
Raw Normal View History

import { getRamCost } from "./RamCostGenerator";
import type { WorkerScript } from "./WorkerScript";
import { Player } from "../Player";
2022-08-08 19:43:41 +02:00
import { helpers } from "./NetscriptHelpers";
2022-07-15 07:51:30 +02:00
type ExternalFunction = (...args: unknown[]) => unknown;
2022-05-24 23:51:48 +02:00
export type ExternalAPI = {
2022-03-30 01:11:13 +02:00
[string: string]: ExternalAPI | ExternalFunction;
2022-04-07 02:00:54 +02:00
};
2022-03-30 01:11:13 +02:00
type InternalFunction<F extends (...args: unknown[]) => unknown> = (ctx: NetscriptContext) => F;
2022-05-24 23:51:48 +02:00
2022-03-30 01:11:13 +02:00
export type InternalAPI<API> = {
2022-04-07 02:00:54 +02:00
[Property in keyof API]: API[Property] extends ExternalFunction
? InternalFunction<API[Property]>
: API[Property] extends object
2022-04-07 02:00:54 +02:00
? InternalAPI<API[Property]>
: never;
};
2022-03-30 01:11:13 +02:00
type WrappedNetscriptFunction = (...args: unknown[]) => unknown;
type WrappedNetscriptAPI = {
readonly [string: string]: WrappedNetscriptAPI | WrappedNetscriptFunction;
2022-04-07 02:00:54 +02:00
};
export type NetscriptContext = {
log: (message: () => string) => void;
workerScript: WorkerScript;
function: string;
};
function wrapFunction(
2022-07-15 07:51:30 +02:00
wrappedAPI: ExternalAPI,
2022-04-07 02:00:54 +02:00
workerScript: WorkerScript,
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
2022-04-07 02:00:54 +02:00
...tree: string[]
): void {
2022-04-08 02:02:59 +02:00
const functionPath = tree.join(".");
const functionName = tree.pop();
2022-04-07 02:00:54 +02:00
if (typeof functionName !== "string") {
2022-08-08 19:43:41 +02:00
throw helpers.makeRuntimeRejectMsg(workerScript, "Failure occured while wrapping netscript api");
}
const ctx = {
2022-04-07 02:00:54 +02:00
log: (message: () => string) => {
workerScript.log(functionPath, message);
2022-04-07 02:00:54 +02:00
},
workerScript,
function: functionName,
};
function wrappedFunction(...args: unknown[]): unknown {
2022-08-08 19:43:41 +02:00
helpers.checkEnvFlags(ctx);
helpers.updateDynamicRam(ctx, getRamCost(Player, ...tree, ctx.function));
return func(ctx)(...args);
}
const parent = getNestedProperty(wrappedAPI, tree);
Object.defineProperty(parent, functionName, {
value: wrappedFunction,
2022-04-07 02:00:54 +02:00
writable: true,
2022-04-08 02:02:59 +02:00
enumerable: true,
});
}
2022-04-07 02:00:54 +02:00
export function wrapAPI(
wrappedAPI: ExternalAPI,
workerScript: WorkerScript,
2022-07-20 07:37:41 +02:00
namespace: object,
2022-04-07 02:00:54 +02:00
...tree: string[]
): WrappedNetscriptAPI {
2022-07-20 07:37:41 +02:00
for (const [key, value] of Object.entries(namespace)) {
2022-07-20 17:34:49 +02:00
if (typeof value === "function") {
2022-08-08 19:43:41 +02:00
wrapFunction(wrappedAPI, workerScript, value, ...tree, key);
2022-07-20 17:34:49 +02:00
} else if (Array.isArray(value)) {
setNestedProperty(wrappedAPI, value.slice(), key);
2022-07-20 17:34:49 +02:00
} else if (typeof value === "object") {
2022-08-08 19:43:41 +02:00
wrapAPI(wrappedAPI, workerScript, value, ...tree, key);
2022-07-20 17:34:49 +02:00
} else {
setNestedProperty(wrappedAPI, value, ...tree, key);
}
}
return wrappedAPI;
}
function setNestedProperty(root: any, value: unknown, ...tree: string[]): void {
let target = root;
const key = tree.pop();
if (!key) throw new Error("Failure occured 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;
}