Fix/Unify NS1 wrapper

This commit is contained in:
Snarling 2022-08-27 20:56:12 -04:00
parent 3ee55b2918
commit 6f36e9cdc5
6 changed files with 53 additions and 113 deletions

@ -702,7 +702,7 @@ const base: InternalAPI<NS> = {
throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev."); throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev.");
} }
return runScriptFromScript(Player, "run", scriptServer, scriptname, args, ctx.workerScript, threads); return runScriptFromScript("run", scriptServer, scriptname, args, ctx.workerScript, threads);
}, },
exec: exec:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -718,7 +718,7 @@ const base: InternalAPI<NS> = {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`);
} }
const server = helpers.getServer(ctx, hostname); const server = helpers.getServer(ctx, hostname);
return runScriptFromScript(Player, "exec", server, scriptname, args, ctx.workerScript, threads); return runScriptFromScript("exec", server, scriptname, args, ctx.workerScript, threads);
}, },
spawn: spawn:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -740,7 +740,7 @@ const base: InternalAPI<NS> = {
throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev"); throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev");
} }
return runScriptFromScript(Player, "spawn", scriptServer, scriptname, args, ctx.workerScript, threads); return runScriptFromScript("spawn", scriptServer, scriptname, args, ctx.workerScript, threads);
}, spawnDelay * 1e3); }, spawnDelay * 1e3);
helpers.log(ctx, () => `Will execute '${scriptname}' in ${spawnDelay} seconds`); helpers.log(ctx, () => `Will execute '${scriptname}' in ${spawnDelay} seconds`);

@ -92,7 +92,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
} }
const runningScriptObj = new RunningScript(script, []); // No args const runningScriptObj = new RunningScript(script, []); // No args
runningScriptObj.threads = 1; // Only 1 thread runningScriptObj.threads = 1; // Only 1 thread
startWorkerScript(player, runningScriptObj, home); startWorkerScript(runningScriptObj, home);
} }
} }
}; };

@ -34,10 +34,8 @@ import { simple as walksimple } from "acorn-walk";
import { areFilesEqual } from "./Terminal/DirectoryHelpers"; import { areFilesEqual } from "./Terminal/DirectoryHelpers";
import { Player } from "./Player"; import { Player } from "./Player";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import { IPlayer } from "./PersonObjects/IPlayer";
import { ScriptArg } from "./Netscript/ScriptArg"; import { ScriptArg } from "./Netscript/ScriptArg";
import { helpers } from "./Netscript/NetscriptHelpers"; import { helpers } from "./Netscript/NetscriptHelpers";
import { NS } from "./ScriptEditor/NetscriptDefinitions";
// Netscript Ports are instantiated here // Netscript Ports are instantiated here
export const NetscriptPorts: IPort[] = []; export const NetscriptPorts: IPort[] = [];
@ -61,10 +59,10 @@ export function prestigeWorkerScripts(): void {
// JS script promises need a little massaging to have the same guarantees as netscript // JS script promises need a little massaging to have the same guarantees as netscript
// promises. This does said massaging and kicks the script off. It returns a promise // promises. This does said massaging and kicks the script off. It returns a promise
// that resolves or rejects when the corresponding worker script is done. // that resolves or rejects when the corresponding worker script is done.
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<void> { function startNetscript2Script(workerScript: WorkerScript): Promise<void> {
workerScript.running = true; workerScript.running = true;
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
executeJSScript(player, workerScript.getServer().scripts, workerScript) executeJSScript(Player, workerScript.getServer().scripts, workerScript)
.then(() => { .then(() => {
resolve(); resolve();
}) })
@ -114,43 +112,27 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
return Promise.resolve(); return Promise.resolve();
} }
const interpreterInitialization = function (int: Interpreter, scope: unknown): void { function wrapNS1Layer(int: Interpreter, intLayer: unknown, path: string[] = []) {
interface NS1 extends NS { //TODO: Better typing layers of interpreter scope and ns
interface BasicObject {
[key: string]: any; [key: string]: any;
} }
//Add the Netscript environment const nsLayer = path.reduce((prev, newPath) => prev[newPath], workerScript.env.vars as BasicObject);
const ns = NetscriptFunctions(workerScript) as NS1; for (const [name, entry] of Object.entries(nsLayer)) {
for (const name of Object.keys(ns)) {
const entry = ns[name];
if (typeof entry === "function") { if (typeof entry === "function") {
// Async functions need to be wrapped. See JS-Interpreter documentation // Async functions need to be wrapped. See JS-Interpreter documentation
const asyncFuncs = ["hack", "grow", "weaken", "sleep", "prompt", "manualHack", "share", "wget"]; const wrapper = async (...args: unknown[]) => {
// This async wrapper is sent a resolver function as an extra arg.
if (asyncFuncs.includes(name)) { // See JSInterpreter.js:3209
const tempWrapper = function (...args: unknown[]): void { try {
const fnArgs = []; const callback = args.pop() as (value: unknown) => void;
const result = await entry(...args.map(int.pseudoToNative));
//All of the Object/array elements are in JSInterpreter format, so return callback(int.nativeToPseudo(result));
//we have to convert them back to native format to pass them to these fns } catch (e: unknown) {
for (let i = 0; i < args.length - 1; ++i) { // TODO: Unify error handling, this was stolen from previous async handler
if (typeof args[i] === "object" || Array.isArray(args[i])) { if (typeof e === "string") {
fnArgs.push(int.pseudoToNative(args[i])); console.error(e);
} else { const errorTextArray = e.split("|DELIMITER|");
fnArgs.push(args[i]);
}
}
const callb = args[args.length - 1];
const fnPromise = entry(...fnArgs);
fnPromise
.then(function (res: unknown) {
if (typeof callb === "function") {
callb(res);
}
})
.catch(function (err: unknown) {
if (typeof err === "string") {
console.error(err);
const errorTextArray = err.split("|DELIMITER|");
const hostname = errorTextArray[1]; const hostname = errorTextArray[1];
const scriptName = errorTextArray[2]; const scriptName = errorTextArray[2];
const errorMsg = errorTextArray[3]; const errorMsg = errorTextArray[3];
@ -161,64 +143,25 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
workerScript.env.stopFlag = true; workerScript.env.stopFlag = true;
workerScript.running = false; workerScript.running = false;
killWorkerScript(workerScript); killWorkerScript(workerScript);
return Promise.resolve(); return;
} }
});
};
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
} else if (
name === "sprintf" ||
name === "vsprintf" ||
name === "scp" ||
name == "write" ||
name === "tryWritePort" ||
name === "run" ||
name === "exec"
) {
const tempWrapper = function (...args: unknown[]): void {
const fnArgs = [];
//All of the Object/array elements are in JSInterpreter format, so
//we have to convert them back to native format to pass them to these fns
for (let i = 0; i < args.length; ++i) {
if (typeof args[i] === "object" || Array.isArray(args[i])) {
fnArgs.push(int.pseudoToNative(args[i]));
} else {
fnArgs.push(args[i]);
}
}
return entry(...fnArgs);
};
int.setProperty(scope, name, int.createNativeFunction(tempWrapper));
} else {
const tempWrapper = function (...args: unknown[]): unknown {
const res = entry(...args);
if (res == null) {
return res;
} else if (res.constructor === Array || res === Object(res)) {
//Objects and Arrays must be converted to the interpreter's format
return int.nativeToPseudo(res);
} else {
return res;
} }
}; };
int.setProperty(scope, name, int.createNativeFunction(tempWrapper)); int.setProperty(intLayer, name, int.createAsyncFunction(wrapper));
} } else if (Array.isArray(entry) || typeof entry !== "object"){
// args, strings on enums, etc
int.setProperty(intLayer, name, int.nativeToPseudo(entry));
} else { } else {
//bladeburner, or anything else // new object layer, e.g. bladeburner
int.setProperty(scope, name, int.nativeToPseudo(entry)); int.setProperty(intLayer, name, int.nativeToPseudo({}));
wrapNS1Layer(int, (intLayer as BasicObject).properties[name], [...path, name]);
} }
} }
//Add the arguments
int.setProperty(scope, "args", int.nativeToPseudo(workerScript.args));
}; };
let interpreter: Interpreter; let interpreter: Interpreter;
try { try {
interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset); interpreter = new Interpreter(codeWithImports, wrapNS1Layer, codeLineOffset);
} catch (e: unknown) { } catch (e: unknown) {
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + String(e)); dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + String(e));
workerScript.env.stopFlag = true; workerScript.env.stopFlag = true;
@ -422,12 +365,11 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): { c
* it is active * it is active
*/ */
export function startWorkerScript( export function startWorkerScript(
player: IPlayer,
runningScript: RunningScript, runningScript: RunningScript,
server: BaseServer, server: BaseServer,
parent?: WorkerScript, parent?: WorkerScript,
): number { ): number {
if (createAndAddWorkerScript(player, runningScript, server, parent)) { if (createAndAddWorkerScript(runningScript, server, parent)) {
// Push onto runningScripts. // Push onto runningScripts.
// This has to come after createAndAddWorkerScript() because that fn updates RAM usage // This has to come after createAndAddWorkerScript() because that fn updates RAM usage
server.runScript(runningScript); server.runScript(runningScript);
@ -448,7 +390,6 @@ export function startWorkerScript(
* returns {boolean} indicating whether or not the workerScript was successfully added * returns {boolean} indicating whether or not the workerScript was successfully added
*/ */
function createAndAddWorkerScript( function createAndAddWorkerScript(
player: IPlayer,
runningScriptObj: RunningScript, runningScriptObj: RunningScript,
server: BaseServer, server: BaseServer,
parent?: WorkerScript, parent?: WorkerScript,
@ -496,7 +437,7 @@ function createAndAddWorkerScript(
// Start the script's execution // Start the script's execution
let scriptExecution: Promise<void> | null = null; // Script's resulting promise let scriptExecution: Promise<void> | null = null; // Script's resulting promise
if (workerScript.name.endsWith(".js")) { if (workerScript.name.endsWith(".js")) {
scriptExecution = startNetscript2Script(player, workerScript); scriptExecution = startNetscript2Script(workerScript);
} else { } else {
scriptExecution = startNetscript1Script(workerScript); scriptExecution = startNetscript1Script(workerScript);
if (!(scriptExecution instanceof Promise)) { if (!(scriptExecution instanceof Promise)) {
@ -583,7 +524,7 @@ export function updateOnlineScriptTimes(numCycles = 1): void {
* Called when the game is loaded. Loads all running scripts (from all servers) * Called when the game is loaded. Loads all running scripts (from all servers)
* into worker scripts so that they will start running * into worker scripts so that they will start running
*/ */
export function loadAllRunningScripts(player: IPlayer): void { export function loadAllRunningScripts(): void {
const skipScriptLoad = window.location.href.toLowerCase().indexOf("?noscripts") !== -1; const skipScriptLoad = window.location.href.toLowerCase().indexOf("?noscripts") !== -1;
if (skipScriptLoad) { if (skipScriptLoad) {
Terminal.warn("Skipped loading player scripts during startup"); Terminal.warn("Skipped loading player scripts during startup");
@ -604,7 +545,7 @@ export function loadAllRunningScripts(player: IPlayer): void {
} else { } else {
for (let j = 0; j < server.runningScripts.length; ++j) { for (let j = 0; j < server.runningScripts.length; ++j) {
const fileName = server.runningScripts[j].filename; const fileName = server.runningScripts[j].filename;
createAndAddWorkerScript(player, server.runningScripts[j], server); createAndAddWorkerScript(server.runningScripts[j], server);
if (!server.runningScripts[j]) { if (!server.runningScripts[j]) {
// createAndAddWorkerScript can modify the server.runningScripts array if a script is invalid // createAndAddWorkerScript can modify the server.runningScripts array if a script is invalid
@ -623,7 +564,6 @@ export function loadAllRunningScripts(player: IPlayer): void {
* Run a script from inside another script (run(), exec(), spawn(), etc.) * Run a script from inside another script (run(), exec(), spawn(), etc.)
*/ */
export function runScriptFromScript( export function runScriptFromScript(
player: IPlayer,
caller: string, caller: string,
server: BaseServer, server: BaseServer,
scriptname: string, scriptname: string,
@ -698,7 +638,7 @@ export function runScriptFromScript(
runningScriptObj.threads = threads; runningScriptObj.threads = threads;
runningScriptObj.server = server.hostname; runningScriptObj.server = server.hostname;
return startWorkerScript(player, runningScriptObj, server, workerScript); return startWorkerScript(runningScriptObj, server, workerScript);
} }
workerScript.log(caller, () => `Could not find script '${scriptname}' on '${server.hostname}'`); workerScript.log(caller, () => `Could not find script '${scriptname}' on '${server.hostname}'`);

@ -76,7 +76,7 @@ export function runScript(
const runningScript = new RunningScript(script, args); const runningScript = new RunningScript(script, args);
runningScript.threads = numThreads; runningScript.threads = numThreads;
const success = startWorkerScript(player, runningScript, server); const success = startWorkerScript(runningScript, server);
if (!success) { if (!success) {
terminal.error(`Failed to start script`); terminal.error(`Failed to start script`);
return; return;

@ -278,7 +278,7 @@ const Engine: {
Player.gainMoney(offlineHackingIncome, "hacking"); Player.gainMoney(offlineHackingIncome, "hacking");
// Process offline progress // Process offline progress
loadAllRunningScripts(Player); // This also takes care of offline production for those scripts loadAllRunningScripts(); // This also takes care of offline production for those scripts
if (Player.currentWork !== null) { if (Player.currentWork !== null) {
Player.focus = true; Player.focus = true;

@ -159,7 +159,7 @@ function LogWindow(props: IProps): React.ReactElement {
if (server === null) return; if (server === null) return;
const s = findRunningScript(script.filename, script.args, server); const s = findRunningScript(script.filename, script.args, server);
if (s === null) { if (s === null) {
startWorkerScript(Player, script, server); startWorkerScript(script, server);
} else { } else {
setScript(s); setScript(s);
} }