Skip to content

Commit

Permalink
v2.2.1 (#293)
Browse files Browse the repository at this point in the history
* handle enums differently
* Enums are frozen and fed directly to the proxy
* Enums are not included in the NSFull definition, allowing samekeys for RamCostTree<API>, InternalAPI<API>, and ExternalAPI<API>
* Rewrote a lot of the ramcalc test, with better typing thanks to the samekeys above
* Fix ns1 for proxy (args, pid, and enums after above changes were not being added to ns1 scripts.)
Update changelog and bump version to 2.2.1
  • Loading branch information
Snarling authored Jan 4, 2023
1 parent 5f18b87 commit b90849e
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 126 deletions.
19 changes: 10 additions & 9 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
Changelog
=========

v2.2.0 - Jan 2 2023 Development Reboot
--------------------------------------
v2.2.1 Hotfixes
---------------

Hotfixes:
Hotfix / bugfix:

* (@d0sboots) Implemented a new API wrapping solution that prevents the need for binding functions to ns when placing them in a new variable, but maintains and perhaps improves upon the performance gains from the previous v2.2.0 changes.
* Fixed some issues with savegames failing to load, or causing the main engine loop to stall after load.
* Fixed an issue where .script files were not receiving the correct args when ran
* Fixed an issue with sleeve HP calculation
* Possible fix for MathJax "Typesetting Failed" errors
* The Faction Work XP fix listed below was also added during hotfixes.
* There was an issue with Corporations decaying their employees to 0 stats, even though the minimum was supposed to be 5. Fixed, and raised minimum to 10.
* There was an issue with Corporations decaying their employees to 0 stats, even though the minimum was supposed to be 5. Moved the variable storing the min decay value to corporation constants, and raised it to 10.
* Regenerated documentation at https://github.com/bitburner-official/bitburner-src/blob/dev/markdown/bitburner.ns.md due to corporation changes related to min decay stats.
* Faction XP was unintentionally providing 20x the experience gain as it did prior to v2.0. This caused faction work to exceed gym/university as the optimal way to gain experience. Values have been reduced to only about 2x what they were prior to v2.0, and they are no longer better than gym/university.


v2.2.0 - Jan 2 2023 Development Reboot
--------------------------------------

Dev notes

Expand All @@ -24,7 +28,7 @@ Dev notes

BREAKING API CHANGES:

* (ns2 only) ns functions use the 'this' value from ns: if you move the function to its own variable off of ns, it needs to be bound to ns. The internal changes that make this necessary led to very large performance gains for running many scripts at once. e.g.:
* No longer applicable as of v2.2.1! (ns2 only) ns functions use the 'this' value from ns: if you move the function to its own variable off of ns, it needs to be bound to ns. The internal changes that make this necessary led to very large performance gains for running many scripts at once. e.g.:

const tprint1 = ns.tprint; // This doesn't work and will error out when calling tprint1();

Expand Down Expand Up @@ -66,9 +70,6 @@ TUTORIAL
* Removed NS1/NS2 selection. Tutorial now only references .js files (NS1 is essentially deprecated) (@Mughur)
* Fix Ram Text (by @jaculler)

FACTION WORK
* Faction XP was unintentionally providing 20x the experience gain as it did prior to v2.0. This caused faction work to exceed gym/university as the optimal way to gain experience. Values have been reduced to only about 2x what they were prior to v2.0, and they are no longer better than gym/university.

NETSCRIPT

* Base NS API:
Expand Down
27 changes: 15 additions & 12 deletions src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ export const CONSTANTS: {
Donations: number; // number of blood/plasma/palette donation the dev have verified., boosts NFG
LatestUpdate: string;
} = {
VersionString: "2.2.0",
VersionNumber: 28,
VersionString: "2.2.1",
VersionNumber: 29,

// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
Expand Down Expand Up @@ -232,17 +232,24 @@ export const CONSTANTS: {
Donations: 41,

LatestUpdate: `
v2.2.0 Hotfixes:
v2.2.1 Hotfixes
Hotfix / bugfix:
* (@d0sboots) Implemented a new API wrapping solution that prevents the need for binding functions to ns when placing
them in a new variable, but maintains and perhaps improves upon the performance gains from the previous v2.2.0
changes.
* Fixed some issues with savegames failing to load, or causing the main engine loop to stall after load.
* Fixed an issue where .script files were not receiving the correct args when ran
* Fixed an issue with sleeve HP calculation
* Possible fix for MathJax "Typesetting Failed" errors
* The Faction Work XP fix listed below was also added during hotfixes.
* There was an issue with Corporations decaying their employees to 0 stats, even though the minimum was supposed to
be 5. Moved the variable storing the min decay value to corporation constants, and raised it to 10.
* Regenerated documentation at https://github.com/bitburner-official/bitburner-src/blob/dev/markdown/bitburner.ns.md
due to corporation changes related to min decay stats.
* Faction XP was unintentionally providing 20x the experience gain as it did prior to v2.0. This caused faction work
to exceed gym/university as the optimal way to gain experience. Values have been reduced to only about 2x what
they were prior to v2.0, and they are no longer better than gym/university.
v2.2.0 - Jan 2 2023 Development Reboot
Expand All @@ -253,8 +260,9 @@ export const CONSTANTS: {
removed functions will provide an error guiding you to the new replacement function to use instead.
BREAKING API CHANGES:
* (ns2 only) ns functions use the 'this' value from ns: if you move the function to its own variable off of ns, it
needs to be bound to ns. e.g.:
* No longer applicable as of v2.2.1!
Prior to v2.2.1: (ns2 only) ns functions use the 'this' value from ns: if you move the function to its own
variable off of ns, it needs to be bound to ns. e.g.:
const tprint1 = ns.tprint; // This doesn't work and will error out when calling tprint1();
const tprint = ns.tprint.bind(ns); // This works because the 'this' value is preserved.
The internal changes that make this necessary led to very large performance gains for running many scripts at once.
Expand Down Expand Up @@ -295,11 +303,6 @@ export const CONSTANTS: {
* Removed NS1/NS2 selection. Tutorial now only references .js files (NS1 is essentially deprecated) (@Mughur)
* Fix Ram Text (by @jaculler)
FACTION WORK
* Faction XP was unintentionally providing 20x the experience gain as it did prior to v2.0. This caused faction work
to exceed gym/university as the optimal way to gain experience. Values have been reduced to only about 2x what
they were prior to v2.0, and they are no longer better than gym/university.
NETSCRIPT
* Added ns.pid property to access a script's PID without a function call. (@jeek)
* Much faster API wrapping on script launch. (@d0sboots) To support this, ns functions need to keep their "this"
Expand Down
47 changes: 17 additions & 30 deletions src/Netscript/APIWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
import { getRamCost } from "./RamCostGenerator";
import type { WorkerScript } from "./WorkerScript";
import { helpers } from "./NetscriptHelpers";
import { ScriptArg } from "./ScriptArg";
import { cloneDeep } from "lodash";

/** 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;

// 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 Enums
? Enums
: key extends "args"
? ScriptArg[] // "args" required to be ScriptArg[]
: API[key] extends APIFn
? WrappedFn
: ExternalAPI<API[key]>;
[key in keyof API]: API[key] extends APIFn ? WrappedFn : ExternalAPI<API[key]>;
};

export type InternalAPI<API> = {
[key in keyof API]: API[key] extends Enums
? API[key] & Enums
: key extends "args"
? ScriptArg[]
: API[key] extends APIFn
? InternalFn<API[key]>
: InternalAPI<API[key]>;
[key in keyof API]: API[key] extends APIFn ? InternalFn<API[key]> : InternalAPI<API[key]>;
};

export type NetscriptContext = {
Expand All @@ -54,40 +40,41 @@ export function NSProxy<API>(
ownKeys(__target: unknown) {
return Reflect.ownKeys(ns);
},
getOwnPropertyDescriptor(__target: unknown, key: string) {
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!");
},
get(__target: unknown, key: string, __receiver: any) {
const ours = memoed[key as keyof API];
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 as keyof API];
const field = ns[key];
if (!field) return field;

if (key === "enums") {
const enumObj = Object.freeze(cloneDeep(field as Enums));
for (const member of Object.values(enumObj)) Object.freeze(member);
return ((memoed[key as keyof API] as Enums) = enumObj);
}
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 chack
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 keyof API] as WrappedFn) = wrappedFunction);
return ((memoed[key] as WrappedFn) = wrappedFunction);
}
if (typeof field === "object") {
// TODO unplanned: Make this work generically
return ((memoed[key as keyof API] as unknown) = NSProxy(ws, field as InternalAPI<unknown>, [...tree, key]));
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.");
Expand Down
11 changes: 4 additions & 7 deletions src/Netscript/RamCostGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { Player } from "@player";
import { NSFull } from "../NetscriptFunctions";

/** This type assumes any value that isn't an API layer or a function has been omitted (enum) */
type RamCostTree<API> = Omit<
{
[Property in keyof API]: API[Property] extends () => unknown ? number | (() => number) : RamCostTree<API[Property]>;
},
"enums"
>;
/** The API does not include enums, args, or pid. */
export type RamCostTree<API> = {
[key in keyof API]: API[key] extends () => unknown ? number | (() => number) : RamCostTree<API[key]>;
};

/** Constants for assigning costs to ns functions */
export const RamCostConstants = {
Expand Down
7 changes: 4 additions & 3 deletions src/NetscriptFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@ export const enums: NSEnums = {
ToastVariant,
UniversityClassType,
};
for (const val of Object.values(enums)) Object.freeze(val);
Object.freeze(enums);

export type NSFull = Readonly<Omit<NS & INetscriptExtra, "pid" | "args">>;
export type NSFull = Readonly<Omit<NS & INetscriptExtra, "pid" | "args" | "enums">>;

export const ns: InternalAPI<NSFull> = {
enums,
singularity: NetscriptSingularity(),
gang: NetscriptGang(),
bladeburner: NetscriptBladeburner(),
Expand Down Expand Up @@ -1894,7 +1895,7 @@ Object.assign(ns, {
});

export function NetscriptFunctions(ws: WorkerScript): ExternalAPI<NSFull> {
return NSProxy(ws, ns, [], { args: ws.args.slice(), pid: ws.pid });
return NSProxy(ws, ns, [], { args: ws.args.slice(), pid: ws.pid, enums });
}

const possibleLogs = Object.fromEntries([...getFunctionNames(ns, "")].map((a) => [a, true]));
Expand Down
5 changes: 5 additions & 0 deletions src/NetscriptWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ 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[]) => {
Expand Down
Loading

0 comments on commit b90849e

Please sign in to comment.