mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-18 12:15:44 +01:00
NETSCRIPT: More ns Proxy changes (#297)
This commit is contained in:
parent
3281b785ce
commit
4eef9eec03
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2dev",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bitburner",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2dev",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
"dependencies": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2dev",
|
||||
"main": "electron-main.js",
|
||||
"author": {
|
||||
"name": "Daniel Xie, Olivier Gagnon, et al."
|
||||
|
@ -3,18 +3,14 @@ import type { WorkerScript } from "./WorkerScript";
|
||||
import { helpers } from "./NetscriptHelpers";
|
||||
|
||||
/** 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 APIFn = (...args: any[]) => unknown;
|
||||
/** Type for internal, unwrapped ctx function that produces an APIFunction */
|
||||
type InternalFn<F extends APIFn> = (ctx: NetscriptContext) => ((...args: unknown[]) => ReturnType<F>) & F;
|
||||
/** Type constraint for an API layer. They must all fit this "shape". */
|
||||
type GenericAPI = { [key: string]: APIFn | GenericAPI };
|
||||
|
||||
// args, enums, and pid are excluded from the API for typing purposes via the definition of NSFull.
|
||||
// They do in fact exist on the external API (but are absent on the internal API and ramcost tree)
|
||||
export type ExternalAPI<API> = {
|
||||
[key in keyof API]: API[key] extends APIFn ? WrappedFn : ExternalAPI<API[key]>;
|
||||
};
|
||||
|
||||
export type InternalAPI<API> = {
|
||||
[key in keyof API]: API[key] extends APIFn ? InternalFn<API[key]> : InternalAPI<API[key]>;
|
||||
};
|
||||
@ -25,70 +21,91 @@ export type NetscriptContext = {
|
||||
functionPath: string;
|
||||
};
|
||||
|
||||
export function NSProxy<API>(
|
||||
class NSProxyHandler<API extends GenericAPI> {
|
||||
ns: API;
|
||||
ws: WorkerScript;
|
||||
tree: string[];
|
||||
additionalData: Record<string, unknown>;
|
||||
memoed: API = {} as API;
|
||||
|
||||
constructor(ws: WorkerScript, ns: API, tree: string[], additionalData: Record<string, unknown>) {
|
||||
this.ns = ns;
|
||||
this.ws = ws;
|
||||
this.tree = tree;
|
||||
this.additionalData = additionalData;
|
||||
Object.assign(this.memoed, additionalData);
|
||||
}
|
||||
|
||||
has(__target: unknown, key: string): boolean {
|
||||
return Reflect.has(this.ns, key) || Reflect.has(this.additionalData, key);
|
||||
}
|
||||
|
||||
ownKeys(__target: unknown): (string | symbol)[] {
|
||||
return [...Reflect.ownKeys(this.ns), ...Reflect.ownKeys(this.additionalData)];
|
||||
}
|
||||
|
||||
getOwnPropertyDescriptor(__target: unknown, key: keyof API & string): PropertyDescriptor | undefined {
|
||||
if (!this.has(__target, key)) return undefined;
|
||||
return { value: this.get(__target, key, this), configurable: true, enumerable: true, writable: false };
|
||||
}
|
||||
|
||||
defineProperty(__target: unknown, __key: unknown, __attrs: unknown): boolean {
|
||||
throw new TypeError("ns instances are not modifiable!");
|
||||
}
|
||||
|
||||
set(__target: unknown, __key: unknown, __attrs: unknown): boolean {
|
||||
// Redundant with defineProperty, but we'll be explicit
|
||||
throw new TypeError("ns instances are not modifiable!");
|
||||
}
|
||||
|
||||
get(__target: unknown, key: keyof API & string, __receiver: any) {
|
||||
const ours = this.memoed[key];
|
||||
if (ours) return ours;
|
||||
|
||||
const field = this.ns[key];
|
||||
if (!field) return field;
|
||||
|
||||
if (typeof field === "function") {
|
||||
const arrayPath = [...this.tree, key];
|
||||
const functionPath = arrayPath.join(".");
|
||||
const ctx = { workerScript: this.ws, function: key, functionPath };
|
||||
// Only do the context-binding once, instead of each time the function
|
||||
// is called.
|
||||
const func: any = field(ctx);
|
||||
const wrappedFunction = function (...args: unknown[]): unknown {
|
||||
// What remains *must* be called every time.
|
||||
helpers.checkEnvFlags(ctx);
|
||||
helpers.updateDynamicRam(ctx, getRamCost(...arrayPath));
|
||||
return func(...args);
|
||||
};
|
||||
return ((this.memoed[key] as APIFn) = wrappedFunction);
|
||||
}
|
||||
if (typeof field === "object") {
|
||||
return ((this.memoed[key] as GenericAPI) = NSProxy(this.ws, field as InternalAPI<GenericAPI>, [
|
||||
...this.tree,
|
||||
key,
|
||||
]));
|
||||
}
|
||||
console.warn(`Unexpected data while wrapping API.`, "tree:", this.tree, "key:", key, "field:", field);
|
||||
throw new Error("Error while wrapping netscript API. See console.");
|
||||
}
|
||||
}
|
||||
|
||||
export function NSProxy<API extends GenericAPI>(
|
||||
ws: WorkerScript,
|
||||
ns: InternalAPI<API>,
|
||||
tree: string[],
|
||||
additionalData?: Record<string, unknown>,
|
||||
): ExternalAPI<API> {
|
||||
const memoed: ExternalAPI<API> = Object.assign({} as ExternalAPI<API>, additionalData ?? {});
|
||||
|
||||
const handler = {
|
||||
has(__target: unknown, key: string) {
|
||||
return Reflect.has(ns, key);
|
||||
},
|
||||
ownKeys(__target: unknown) {
|
||||
return Reflect.ownKeys(ns);
|
||||
},
|
||||
getOwnPropertyDescriptor(__target: unknown, key: keyof API & string) {
|
||||
if (!Reflect.has(ns, key)) return undefined;
|
||||
return { value: this.get(__target, key, this), configurable: true, enumerable: true, writable: false };
|
||||
},
|
||||
defineProperty(__target: unknown, __key: unknown, __attrs: unknown) {
|
||||
throw new TypeError("ns instances are not modifiable!");
|
||||
},
|
||||
set(__target: unknown, __key: unknown, __attrs: unknown) {
|
||||
throw new TypeError("ns instances are not modifiable!");
|
||||
},
|
||||
get(__target: unknown, key: keyof API & string, __receiver: any) {
|
||||
const ours = memoed[key];
|
||||
if (ours) return ours;
|
||||
|
||||
const field = ns[key];
|
||||
if (!field) return field;
|
||||
|
||||
if (typeof field === "function") {
|
||||
const arrayPath = [...tree, key];
|
||||
const functionPath = arrayPath.join(".");
|
||||
const wrappedFunction = function (...args: unknown[]): unknown {
|
||||
const ctx = { workerScript: ws, function: key, functionPath };
|
||||
const func = field(ctx); //Allows throwing before ram check, for removedFunction
|
||||
helpers.checkEnvFlags(ctx);
|
||||
helpers.updateDynamicRam(ctx, getRamCost(...tree, key));
|
||||
return func(...args);
|
||||
};
|
||||
return ((memoed[key] as WrappedFn) = wrappedFunction);
|
||||
}
|
||||
if (typeof field === "object") {
|
||||
// TODO unplanned: Make this work generically
|
||||
return ((memoed[key] as ExternalAPI<API[keyof API]>) = NSProxy(ws, field as InternalAPI<API[keyof API]>, [
|
||||
...tree,
|
||||
key,
|
||||
]));
|
||||
}
|
||||
console.warn(`Unexpected data while wrapping API.`, "tree:", tree, "key:", key, "field:", field);
|
||||
throw new Error("Error while wrapping netscript API. See console.");
|
||||
},
|
||||
};
|
||||
|
||||
additionalData: Record<string, unknown> = {},
|
||||
): API {
|
||||
const handler = new NSProxyHandler(ws, ns, tree, additionalData);
|
||||
// We target an empty Object, so that unproxied methods don't do anything.
|
||||
// We *can't* freeze the target, because it would break invariants on ownKeys.
|
||||
return new Proxy({}, handler) as ExternalAPI<API>;
|
||||
return new Proxy({}, handler) as API;
|
||||
}
|
||||
|
||||
/** Specify when a function was removed from the game, and its replacement function. */
|
||||
export function removedFunction(version: string, replacement: string, replaceMsg?: boolean) {
|
||||
return (ctx: NetscriptContext) => {
|
||||
return (ctx: NetscriptContext) => () => {
|
||||
throw helpers.makeRuntimeErrorMsg(
|
||||
ctx,
|
||||
`Function removed in ${version}. ${replaceMsg ? replacement : `Please use ${replacement} instead.`}`,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { NSFull } from "../NetscriptFunctions";
|
||||
import { ExternalAPI } from "./APIWrapper";
|
||||
|
||||
/**
|
||||
* The environment in which a script runs. The environment holds
|
||||
@ -14,5 +13,5 @@ export class Environment {
|
||||
runningFn = "";
|
||||
|
||||
/** Environment variables (currently only Netscript functions) */
|
||||
vars: ExternalAPI<NSFull> | null = null;
|
||||
vars: NSFull | null = null;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import { GetServer } from "../Server/AllServers";
|
||||
import { BaseServer } from "../Server/BaseServer";
|
||||
import { ScriptDeath } from "./ScriptDeath";
|
||||
import { ScriptArg } from "./ScriptArg";
|
||||
import { ExternalAPI } from "./APIWrapper";
|
||||
import { NSFull } from "../NetscriptFunctions";
|
||||
|
||||
export class WorkerScript {
|
||||
@ -84,11 +83,7 @@ export class WorkerScript {
|
||||
/** Function called when the script ends. */
|
||||
atExit?: () => void;
|
||||
|
||||
constructor(
|
||||
runningScriptObj: RunningScript,
|
||||
pid: number,
|
||||
nsFuncsGenerator?: (ws: WorkerScript) => ExternalAPI<NSFull>,
|
||||
) {
|
||||
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => NSFull) {
|
||||
this.name = runningScriptObj.filename;
|
||||
this.hostname = runningScriptObj.server;
|
||||
|
||||
|
@ -72,7 +72,7 @@ import { Flags } from "./NetscriptFunctions/Flags";
|
||||
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
|
||||
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
|
||||
import { recentScripts } from "./Netscript/RecentScripts";
|
||||
import { ExternalAPI, InternalAPI, removedFunction, NSProxy } from "./Netscript/APIWrapper";
|
||||
import { InternalAPI, removedFunction, NSProxy } from "./Netscript/APIWrapper";
|
||||
import { INetscriptExtra } from "./NetscriptFunctions/Extra";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { getBitNodeMultipliers } from "./BitNode/BitNode";
|
||||
@ -1894,7 +1894,7 @@ Object.assign(ns, {
|
||||
getServerRam: removedFunction("v2.2.0", "getServerMaxRam and getServerUsedRam"),
|
||||
});
|
||||
|
||||
export function NetscriptFunctions(ws: WorkerScript): ExternalAPI<NSFull> {
|
||||
export function NetscriptFunctions(ws: WorkerScript): NSFull {
|
||||
return NSProxy(ws, ns, [], { args: ws.args.slice(), pid: ws.pid, enums });
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { InternalAPI } from "../Netscript/APIWrapper";
|
||||
import { helpers } from "../Netscript/NetscriptHelpers";
|
||||
import { Terminal } from "../Terminal";
|
||||
|
||||
export interface INetscriptExtra {
|
||||
export type INetscriptExtra = {
|
||||
heart: {
|
||||
break(): number;
|
||||
};
|
||||
@ -18,7 +18,7 @@ export interface INetscriptExtra {
|
||||
rainbow(guess: string): void;
|
||||
iKnowWhatImDoing(): void;
|
||||
printRaw(value: React.ReactNode): void;
|
||||
}
|
||||
};
|
||||
|
||||
export function NetscriptExtra(): InternalAPI<INetscriptExtra> {
|
||||
return {
|
||||
|
@ -83,11 +83,6 @@ async function startNetscript1Script(workerScript: WorkerScript): Promise<void>
|
||||
type BasicObject = Record<string, any>;
|
||||
const wrappedNS = NetscriptFunctions(workerScript);
|
||||
function wrapNS1Layer(int: Interpreter, intLayer: unknown, nsLayer = wrappedNS as BasicObject) {
|
||||
if (nsLayer === wrappedNS) {
|
||||
int.setProperty(intLayer, "args", int.nativeToPseudo(nsLayer.args));
|
||||
int.setProperty(intLayer, "enums", int.nativeToPseudo(nsLayer.enums));
|
||||
int.setProperty(intLayer, "pid", nsLayer.pid);
|
||||
}
|
||||
for (const [name, entry] of Object.entries(nsLayer)) {
|
||||
if (typeof entry === "function") {
|
||||
const wrapper = async (...args: unknown[]) => {
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { NSFull } from "../NetscriptFunctions";
|
||||
import { ExternalAPI } from "../Netscript/APIWrapper";
|
||||
import { AutocompleteData } from "@nsdefs";
|
||||
|
||||
export interface ScriptModule {
|
||||
main?: (ns: ExternalAPI<NSFull>) => unknown;
|
||||
main?: (ns: NSFull) => unknown;
|
||||
autocomplete?: (data: AutocompleteData, flags: string[]) => unknown;
|
||||
}
|
||||
|
93
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
93
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -1000,7 +1000,7 @@ interface NetscriptPort {
|
||||
* Stock market API
|
||||
* @public
|
||||
*/
|
||||
export interface TIX {
|
||||
export type TIX = {
|
||||
/**
|
||||
* Returns true if the player has access to a WSE Account
|
||||
* @remarks RAM cost: 0.05 GB
|
||||
@ -1439,7 +1439,7 @@ export interface TIX {
|
||||
* @returns True if you successfully purchased it or if you already have access, false otherwise.
|
||||
*/
|
||||
purchaseTixApi(): boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Singularity API
|
||||
@ -1448,7 +1448,7 @@ export interface TIX {
|
||||
* Source-File 4 levels.
|
||||
* @public
|
||||
*/
|
||||
export interface Singularity {
|
||||
export type Singularity = {
|
||||
/**
|
||||
* Backup game save.
|
||||
* @remarks
|
||||
@ -2292,7 +2292,7 @@ export interface Singularity {
|
||||
* @returns - An object representing the current work. Fields depend on the kind of work.
|
||||
*/
|
||||
getCurrentWork(): any | null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hacknet API
|
||||
@ -2300,7 +2300,7 @@ export interface Singularity {
|
||||
* Not all these functions are immediately available.
|
||||
* @public
|
||||
*/
|
||||
export interface Hacknet {
|
||||
export type Hacknet = {
|
||||
/**
|
||||
* Get the number of hacknet nodes you own.
|
||||
* @remarks
|
||||
@ -2651,7 +2651,7 @@ export interface Hacknet {
|
||||
* @returns Multiplier.
|
||||
*/
|
||||
getTrainingMult(): number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bladeburner API
|
||||
@ -2660,7 +2660,7 @@ export interface Hacknet {
|
||||
* or have Source-File 7 in order to use this API.
|
||||
* @public
|
||||
*/
|
||||
export interface Bladeburner {
|
||||
export type Bladeburner = {
|
||||
/**
|
||||
* List all contracts.
|
||||
* @remarks
|
||||
@ -3148,13 +3148,13 @@ export interface Bladeburner {
|
||||
*
|
||||
* @returns whether player is a member of bladeburner division. */
|
||||
inBladeburner(): boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Coding Contract API
|
||||
* @public
|
||||
*/
|
||||
export interface CodingContract {
|
||||
export type CodingContract = {
|
||||
/**
|
||||
* Attempts a coding contract, returning a reward string on success or empty string on failure.
|
||||
* @remarks
|
||||
@ -3259,7 +3259,7 @@ export interface CodingContract {
|
||||
* RAM cost: 2 GB
|
||||
*/
|
||||
getContractTypes(): string[];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gang API
|
||||
@ -3267,7 +3267,7 @@ export interface CodingContract {
|
||||
* If you are not in BitNode-2, then you must have Source-File 2 in order to use this API.
|
||||
* @public
|
||||
*/
|
||||
export interface Gang {
|
||||
export type Gang = {
|
||||
/**
|
||||
* Create a gang.
|
||||
* @remarks
|
||||
@ -3526,7 +3526,7 @@ export interface Gang {
|
||||
* @returns Bonus time for the Gang mechanic in milliseconds.
|
||||
*/
|
||||
getBonusTime(): number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sleeve API
|
||||
@ -3534,7 +3534,7 @@ export interface Gang {
|
||||
* If you are not in BitNode-10, then you must have Source-File 10 in order to use this API.
|
||||
* @public
|
||||
*/
|
||||
export interface Sleeve {
|
||||
export type Sleeve = {
|
||||
/**
|
||||
* Get the number of sleeves you own.
|
||||
* @remarks
|
||||
@ -3764,7 +3764,7 @@ export interface Sleeve {
|
||||
* @returns True if the sleeve started working out, false otherwise.
|
||||
*/
|
||||
setToBladeburnerAction(sleeveNumber: number, action: string, contract?: string): boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Grafting API
|
||||
@ -3772,7 +3772,7 @@ export interface Sleeve {
|
||||
* This API requires Source-File 10 to use.
|
||||
* @public
|
||||
*/
|
||||
export interface Grafting {
|
||||
export type Grafting = {
|
||||
/**
|
||||
* Retrieve the grafting cost of an aug.
|
||||
* @remarks
|
||||
@ -3819,13 +3819,13 @@ export interface Grafting {
|
||||
* @throws Will error if called while you are not in New Tokyo.
|
||||
*/
|
||||
graftAugmentation(augName: string, focus?: boolean): boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Skills formulas
|
||||
* @public
|
||||
*/
|
||||
interface SkillsFormulas {
|
||||
type SkillsFormulas = {
|
||||
/**
|
||||
* Calculate skill level.
|
||||
* @param exp - experience for that skill
|
||||
@ -3840,7 +3840,7 @@ interface SkillsFormulas {
|
||||
* @returns The calculated exp required.
|
||||
*/
|
||||
calculateExp(skill: number, skillMult?: number): number;
|
||||
}
|
||||
};
|
||||
|
||||
/** @public */
|
||||
interface WorkStats {
|
||||
@ -3859,7 +3859,7 @@ interface WorkStats {
|
||||
* Work formulas
|
||||
* @public
|
||||
*/
|
||||
interface WorkFormulas {
|
||||
type WorkFormulas = {
|
||||
crimeSuccessChance(person: Person, crimeType: CrimeType | `${CrimeType}`): number;
|
||||
/** @returns The WorkStats gained when completing one instance of the specified crime. */
|
||||
crimeGains(person: Person, crimeType: CrimeType | `${CrimeType}`): WorkStats;
|
||||
@ -3875,13 +3875,13 @@ interface WorkFormulas {
|
||||
factionGains(person: Person, workType: FactionWorkType | `${FactionWorkType}`, favor: number): WorkStats;
|
||||
/** @returns The WorkStats applied every game cycle (200ms) by performing the specified company work. */
|
||||
companyGains(person: Person, companyName: string, workType: JobName | `${JobName}`, favor: number): WorkStats;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reputation formulas
|
||||
* @public
|
||||
*/
|
||||
interface ReputationFormulas {
|
||||
type ReputationFormulas = {
|
||||
/**
|
||||
* Calculate the total required amount of faction reputation to reach a target favor.
|
||||
* @param favor - target faction favor.
|
||||
@ -3902,13 +3902,13 @@ interface ReputationFormulas {
|
||||
* @param player - Player info from {@link NS.getPlayer | getPlayer}
|
||||
*/
|
||||
repFromDonation(amount: number, player: Person): number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hacking formulas
|
||||
* @public
|
||||
*/
|
||||
interface HackingFormulas {
|
||||
type HackingFormulas = {
|
||||
/**
|
||||
* Calculate hack chance.
|
||||
* (Ex: 0.25 would indicate a 25% chance of success.)
|
||||
@ -3968,13 +3968,13 @@ interface HackingFormulas {
|
||||
* @returns The calculated weaken time.
|
||||
*/
|
||||
weakenTime(server: Server, player: Person): number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hacknet Node formulas
|
||||
* @public
|
||||
*/
|
||||
interface HacknetNodesFormulas {
|
||||
type HacknetNodesFormulas = {
|
||||
/**
|
||||
* Calculate money gain rate.
|
||||
* @param level - level of the node.
|
||||
@ -4020,13 +4020,13 @@ interface HacknetNodesFormulas {
|
||||
* @returns An object with all hacknet node constants used by the game.
|
||||
*/
|
||||
constants(): HacknetNodeConstants;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hacknet Server formulas
|
||||
* @public
|
||||
*/
|
||||
interface HacknetServersFormulas {
|
||||
type HacknetServersFormulas = {
|
||||
/**
|
||||
* Calculate hash gain rate.
|
||||
* @param level - level of the server.
|
||||
@ -4087,13 +4087,13 @@ interface HacknetServersFormulas {
|
||||
* @returns An object with all hacknet server constants used by the game.
|
||||
*/
|
||||
constants(): HacknetServerConstants;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gang formulas
|
||||
* @public
|
||||
*/
|
||||
interface GangFormulas {
|
||||
type GangFormulas = {
|
||||
/**
|
||||
* Calculate the wanted penalty.
|
||||
* @param gang - Gang info from {@link Gang.getGangInformation | getGangInformation}
|
||||
@ -4138,7 +4138,7 @@ interface GangFormulas {
|
||||
* @returns The calculated ascension mult.
|
||||
*/
|
||||
ascensionMultiplier(points: number): number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Formulas API
|
||||
@ -4146,7 +4146,7 @@ interface GangFormulas {
|
||||
* You need Formulas.exe on your home computer to use this API.
|
||||
* @public
|
||||
*/
|
||||
export interface Formulas {
|
||||
export type Formulas = {
|
||||
mockServer(): Server;
|
||||
mockPlayer(): Player;
|
||||
mockPerson(): Person;
|
||||
@ -4164,7 +4164,7 @@ export interface Formulas {
|
||||
gang: GangFormulas;
|
||||
/** Work formulas */
|
||||
work: WorkFormulas;
|
||||
}
|
||||
};
|
||||
|
||||
/** @public */
|
||||
interface Fragment {
|
||||
@ -4189,7 +4189,7 @@ interface ActiveFragment {
|
||||
* Stanek's Gift API.
|
||||
* @public
|
||||
*/
|
||||
interface Stanek {
|
||||
type Stanek = {
|
||||
/**
|
||||
* Stanek's Gift width.
|
||||
* @remarks
|
||||
@ -4295,7 +4295,7 @@ interface Stanek {
|
||||
* false otherwise.
|
||||
*/
|
||||
acceptGift(): boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/** @public */
|
||||
interface InfiltrationReward {
|
||||
@ -4321,7 +4321,7 @@ interface InfiltrationLocation {
|
||||
* Infiltration API.
|
||||
* @public
|
||||
*/
|
||||
interface Infiltration {
|
||||
type Infiltration = {
|
||||
/**
|
||||
* Get all locations that can be infiltrated.
|
||||
* @remarks
|
||||
@ -4338,13 +4338,13 @@ interface Infiltration {
|
||||
* @returns Infiltration data for given location.
|
||||
*/
|
||||
getInfiltration(location: string): InfiltrationLocation;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* User Interface API.
|
||||
* @public
|
||||
*/
|
||||
interface UserInterface {
|
||||
type UserInterface = {
|
||||
/**
|
||||
* Get the current window size
|
||||
* @remarks
|
||||
@ -4427,7 +4427,7 @@ interface UserInterface {
|
||||
* RAM cost: 0.2 GB
|
||||
*/
|
||||
clearTerminal(): void;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Collection of all functions passed to scripts
|
||||
@ -4456,7 +4456,7 @@ interface UserInterface {
|
||||
* {@link https://bitburner.readthedocs.io/en/latest/netscript/netscriptjs.html| ns2 in-game docs}
|
||||
* <hr>
|
||||
*/
|
||||
export interface NS {
|
||||
export type NS = {
|
||||
/**
|
||||
* Namespace for hacknet functions.
|
||||
* @remarks RAM cost: 4 GB
|
||||
@ -6853,7 +6853,7 @@ export interface NS {
|
||||
getSharePower(): number;
|
||||
|
||||
enums: NSEnums;
|
||||
}
|
||||
};
|
||||
|
||||
// BASE ENUMS
|
||||
/** @public */
|
||||
@ -7073,7 +7073,7 @@ export type NSEnums = {
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface OfficeAPI {
|
||||
export type OfficeAPI = {
|
||||
/**
|
||||
* Hire an employee.
|
||||
* @param divisionName - Name of the division
|
||||
@ -7165,7 +7165,7 @@ export interface OfficeAPI {
|
||||
* @returns Cost of upgrading the office
|
||||
*/
|
||||
getOfficeSizeUpgradeCost(divisionName: string, city: CityName | `${CityName}`, asize: number): number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Corporation Warehouse API
|
||||
@ -7173,7 +7173,7 @@ export interface OfficeAPI {
|
||||
* Requires the Warehouse API upgrade from your corporation.
|
||||
* @public
|
||||
*/
|
||||
export interface WarehouseAPI {
|
||||
export type WarehouseAPI = {
|
||||
/**
|
||||
* Set material sell data.
|
||||
* @param divisionName - Name of the division
|
||||
@ -7396,13 +7396,13 @@ export interface WarehouseAPI {
|
||||
* @returns true if warehouse is present, false if not
|
||||
*/
|
||||
hasWarehouse(divisionName: string, city: CityName | `${CityName}`): boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Corporation API
|
||||
* @public
|
||||
*/
|
||||
export interface Corporation extends WarehouseAPI, OfficeAPI {
|
||||
export type Corporation = {
|
||||
/** Returns whether the player has a corporation. Does not require API access.
|
||||
* @returns whether the player has a corporation */
|
||||
hasCorporation(): boolean;
|
||||
@ -7513,7 +7513,8 @@ export interface Corporation extends WarehouseAPI, OfficeAPI {
|
||||
* “Bonus time” makes the game progress faster.
|
||||
* @returns Bonus time for the Corporation mechanic in milliseconds. */
|
||||
getBonusTime(): number;
|
||||
}
|
||||
} & WarehouseAPI &
|
||||
OfficeAPI;
|
||||
|
||||
/** Product rating information
|
||||
* @public */
|
||||
|
@ -7,7 +7,7 @@ import { Script } from "../../../src/Script/Script";
|
||||
import { WorkerScript } from "../../../src/Netscript/WorkerScript";
|
||||
import { calculateRamUsage } from "../../../src/Script/RamCalculations";
|
||||
import { ns } from "../../../src/NetscriptFunctions";
|
||||
import { ExternalAPI, InternalAPI } from "src/Netscript/APIWrapper";
|
||||
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
import { Singularity } from "@nsdefs";
|
||||
|
||||
type PotentiallyAsyncFunction = (arg?: unknown) => { catch?: PotentiallyAsyncFunction };
|
||||
@ -20,15 +20,10 @@ function getFunction(fn: unknown) {
|
||||
function grabCost<API>(ramEntry: RamCostTree<API>[keyof API]) {
|
||||
if (typeof ramEntry === "function") return ramEntry();
|
||||
if (typeof ramEntry === "number") return ramEntry;
|
||||
throw new Error("Invalid ramcost");
|
||||
throw new Error("Invalid ramcost: " + ramEntry);
|
||||
}
|
||||
function isRemovedFunction(fn: Function) {
|
||||
try {
|
||||
fn();
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
function isRemovedFunction(ctx: NetscriptContext, fn: (ctx: NetscriptContext) => (...__: unknown[]) => unknown) {
|
||||
return /REMOVED FUNCTION/.test(fn(ctx) + "");
|
||||
}
|
||||
|
||||
describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
@ -112,7 +107,7 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
describe("ns", () => {
|
||||
function testLayer<API>(
|
||||
internalLayer: InternalAPI<API>,
|
||||
externalLayer: ExternalAPI<API>,
|
||||
externalLayer: API,
|
||||
ramLayer: RamCostTree<API>,
|
||||
path: string[],
|
||||
extraLayerCost: number,
|
||||
@ -122,18 +117,22 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
const newPath = [...path, key as string];
|
||||
if (typeof val === "function") {
|
||||
// Removed functions have no ram cost and should be skipped.
|
||||
if (isRemovedFunction(val)) return;
|
||||
if (isRemovedFunction({ workerScript }, val)) return;
|
||||
const fn = getFunction(externalLayer[key]);
|
||||
const fnName = newPath.join(".");
|
||||
if (!(key in ramLayer)) {
|
||||
throw new Error("Missing ramcost for " + fnName);
|
||||
}
|
||||
const expectedRam = grabCost(ramLayer[key]);
|
||||
it(`${fnName}()`, () => combinedRamCheck(fn, newPath, expectedRam, extraLayerCost));
|
||||
}
|
||||
//A layer should be the only other option. Hacknet is currently the only layer with a layer cost.
|
||||
else {
|
||||
else if (typeof val === "object" && key !== "enums") {
|
||||
//hacknet is currently the only layer with a layer cost.
|
||||
const layerCost = key === "hacknet" ? 4 : 0;
|
||||
testLayer(val as InternalAPI<unknown>, externalLayer[key], ramLayer[key], newPath, layerCost);
|
||||
}
|
||||
// Other things like args, enums, etc. have no cost
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -149,7 +148,7 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
const singObjects = (
|
||||
Object.entries(ns.singularity) as [keyof Singularity, InternalAPI<Singularity>[keyof Singularity]][]
|
||||
)
|
||||
.filter(([_, v]) => typeof v === "function" && !isRemovedFunction(v))
|
||||
.filter(([__, v]) => typeof v === "function" && !isRemovedFunction({ workerScript }, v))
|
||||
.map(([name]) => {
|
||||
return {
|
||||
name,
|
||||
|
Loading…
Reference in New Issue
Block a user