mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-21 21:52:30 +01:00
Merge pull request #4052 from Snarling/ns1wrapper
NETSCRIPT: FIX #4037 ns1 wraps deeper layers correctly.
This commit is contained in:
commit
c2796e164a
@ -94,11 +94,6 @@ export class WorkerScript {
|
||||
*/
|
||||
ramUsage = 0;
|
||||
|
||||
/**
|
||||
* Whether or not this workerScript is currently running
|
||||
*/
|
||||
running = false;
|
||||
|
||||
/**
|
||||
* Reference to underlying RunningScript object
|
||||
*/
|
||||
|
@ -702,7 +702,7 @@ const base: InternalAPI<NS> = {
|
||||
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:
|
||||
(ctx: NetscriptContext) =>
|
||||
@ -718,7 +718,7 @@ const base: InternalAPI<NS> = {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`);
|
||||
}
|
||||
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:
|
||||
(ctx: NetscriptContext) =>
|
||||
@ -740,12 +740,11 @@ const base: InternalAPI<NS> = {
|
||||
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);
|
||||
|
||||
helpers.log(ctx, () => `Will execute '${scriptname}' in ${spawnDelay} seconds`);
|
||||
|
||||
ctx.workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
|
||||
if (killWorkerScript(ctx.workerScript)) {
|
||||
helpers.log(ctx, () => "Exiting...");
|
||||
}
|
||||
@ -820,7 +819,6 @@ const base: InternalAPI<NS> = {
|
||||
return scriptsKilled > 0;
|
||||
},
|
||||
exit: (ctx: NetscriptContext) => (): void => {
|
||||
ctx.workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
|
||||
if (killWorkerScript(ctx.workerScript)) {
|
||||
helpers.log(ctx, () => "Exiting...");
|
||||
} else {
|
||||
|
@ -37,7 +37,7 @@ export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
|
||||
throw new Error("The answer provided was not a number, string, or array");
|
||||
|
||||
// Convert answer to string.
|
||||
const answerStr = typeof answer === 'string' ? answer : JSON.stringify(answer);
|
||||
const answerStr = typeof answer === "string" ? answer : JSON.stringify(answer);
|
||||
const creward = contract.reward;
|
||||
if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
|
||||
|
||||
|
@ -92,7 +92,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||
}
|
||||
const runningScriptObj = new RunningScript(script, []); // No args
|
||||
runningScriptObj.threads = 1; // Only 1 thread
|
||||
startWorkerScript(player, runningScriptObj, home);
|
||||
startWorkerScript(runningScriptObj, home);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -235,8 +235,6 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||
runAfterReset(cbScript);
|
||||
}, 0);
|
||||
|
||||
// Prevent ctx.workerScript from "finishing execution naturally"
|
||||
ctx.workerScript.running = false;
|
||||
killWorkerScript(ctx.workerScript);
|
||||
},
|
||||
installAugmentations: (ctx: NetscriptContext) =>
|
||||
@ -255,7 +253,6 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||
runAfterReset(cbScript);
|
||||
}, 0);
|
||||
|
||||
ctx.workerScript.running = false; // Prevent ctx.workerScript from "finishing execution naturally"
|
||||
killWorkerScript(ctx.workerScript);
|
||||
return true;
|
||||
},
|
||||
|
@ -34,10 +34,8 @@ import { simple as walksimple } from "acorn-walk";
|
||||
import { areFilesEqual } from "./Terminal/DirectoryHelpers";
|
||||
import { Player } from "./Player";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { IPlayer } from "./PersonObjects/IPlayer";
|
||||
import { ScriptArg } from "./Netscript/ScriptArg";
|
||||
import { helpers } from "./Netscript/NetscriptHelpers";
|
||||
import { NS } from "./ScriptEditor/NetscriptDefinitions";
|
||||
|
||||
// Netscript Ports are instantiated here
|
||||
export const NetscriptPorts: IPort[] = [];
|
||||
@ -61,10 +59,9 @@ export function prestigeWorkerScripts(): void {
|
||||
// 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
|
||||
// that resolves or rejects when the corresponding worker script is done.
|
||||
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<void> {
|
||||
workerScript.running = true;
|
||||
function startNetscript2Script(workerScript: WorkerScript): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
executeJSScript(player, workerScript.getServer().scripts, workerScript)
|
||||
executeJSScript(Player, workerScript.getServer().scripts, workerScript)
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
@ -98,7 +95,6 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
|
||||
function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
const code = workerScript.code;
|
||||
workerScript.running = true;
|
||||
|
||||
//Process imports
|
||||
let codeWithImports, codeLineOffset;
|
||||
@ -109,48 +105,31 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
} catch (e: unknown) {
|
||||
dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + String(e));
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const interpreterInitialization = function (int: Interpreter, scope: unknown): void {
|
||||
interface NS1 extends NS {
|
||||
function wrapNS1Layer(int: Interpreter, intLayer: unknown, path: string[] = []) {
|
||||
//TODO: Better typing layers of interpreter scope and ns
|
||||
interface BasicObject {
|
||||
[key: string]: any;
|
||||
}
|
||||
//Add the Netscript environment
|
||||
const ns = NetscriptFunctions(workerScript) as NS1;
|
||||
for (const name of Object.keys(ns)) {
|
||||
const entry = ns[name];
|
||||
const nsLayer = path.reduce((prev, newPath) => prev[newPath], workerScript.env.vars as BasicObject);
|
||||
for (const [name, entry] of Object.entries(nsLayer)) {
|
||||
if (typeof entry === "function") {
|
||||
// Async functions need to be wrapped. See JS-Interpreter documentation
|
||||
const asyncFuncs = ["hack", "grow", "weaken", "sleep", "prompt", "manualHack", "share", "wget"];
|
||||
|
||||
if (asyncFuncs.includes(name)) {
|
||||
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 - 1; ++i) {
|
||||
if (typeof args[i] === "object" || Array.isArray(args[i])) {
|
||||
fnArgs.push(int.pseudoToNative(args[i]));
|
||||
} else {
|
||||
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 wrapper = async (...args: unknown[]) => {
|
||||
// This async wrapper is sent a resolver function as an extra arg.
|
||||
// See JSInterpreter.js:3209
|
||||
try {
|
||||
const callback = args.pop() as (value: unknown) => void;
|
||||
const result = await entry(...args.map(int.pseudoToNative));
|
||||
return callback(int.nativeToPseudo(result));
|
||||
} catch (e: unknown) {
|
||||
// TODO: Unify error handling, this was stolen from previous async handler
|
||||
if (typeof e === "string") {
|
||||
console.error(e);
|
||||
const errorTextArray = e.split("|DELIMITER|");
|
||||
const hostname = errorTextArray[1];
|
||||
const scriptName = errorTextArray[2];
|
||||
const errorMsg = errorTextArray[3];
|
||||
@ -159,70 +138,29 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
msg += errorMsg;
|
||||
dialogBoxCreate(msg);
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
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 {
|
||||
//bladeburner, or anything else
|
||||
int.setProperty(scope, name, int.nativeToPseudo(entry));
|
||||
// new object layer, e.g. bladeburner
|
||||
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;
|
||||
try {
|
||||
interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset);
|
||||
interpreter = new Interpreter(codeWithImports, wrapNS1Layer, codeLineOffset);
|
||||
} catch (e: unknown) {
|
||||
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + String(e));
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -421,13 +359,8 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): { c
|
||||
* corresponding WorkerScript), and add the RunningScript to the server on which
|
||||
* it is active
|
||||
*/
|
||||
export function startWorkerScript(
|
||||
player: IPlayer,
|
||||
runningScript: RunningScript,
|
||||
server: BaseServer,
|
||||
parent?: WorkerScript,
|
||||
): number {
|
||||
if (createAndAddWorkerScript(player, runningScript, server, parent)) {
|
||||
export function startWorkerScript(runningScript: RunningScript, server: BaseServer, parent?: WorkerScript): number {
|
||||
if (createAndAddWorkerScript(runningScript, server, parent)) {
|
||||
// Push onto runningScripts.
|
||||
// This has to come after createAndAddWorkerScript() because that fn updates RAM usage
|
||||
server.runScript(runningScript);
|
||||
@ -447,12 +380,7 @@ export function startWorkerScript(
|
||||
* @param {Server} server - Server on which the script is to be run
|
||||
* returns {boolean} indicating whether or not the workerScript was successfully added
|
||||
*/
|
||||
function createAndAddWorkerScript(
|
||||
player: IPlayer,
|
||||
runningScriptObj: RunningScript,
|
||||
server: BaseServer,
|
||||
parent?: WorkerScript,
|
||||
): boolean {
|
||||
function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseServer, parent?: WorkerScript): boolean {
|
||||
// Update server's ram usage
|
||||
let threads = 1;
|
||||
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
|
||||
@ -496,7 +424,7 @@ function createAndAddWorkerScript(
|
||||
// Start the script's execution
|
||||
let scriptExecution: Promise<void> | null = null; // Script's resulting promise
|
||||
if (workerScript.name.endsWith(".js")) {
|
||||
scriptExecution = startNetscript2Script(player, workerScript);
|
||||
scriptExecution = startNetscript2Script(workerScript);
|
||||
} else {
|
||||
scriptExecution = startNetscript1Script(workerScript);
|
||||
if (!(scriptExecution instanceof Promise)) {
|
||||
@ -508,15 +436,12 @@ function createAndAddWorkerScript(
|
||||
// running status to false
|
||||
scriptExecution
|
||||
.then(function () {
|
||||
workerScript.running = false;
|
||||
workerScript.env.stopFlag = true;
|
||||
// On natural death, the earnings are transfered to the parent if it still exists.
|
||||
if (parent !== undefined) {
|
||||
if (parent.running) {
|
||||
if (parent !== undefined && !parent.env.stopFlag) {
|
||||
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
|
||||
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
|
||||
}
|
||||
}
|
||||
|
||||
killWorkerScript(workerScript);
|
||||
workerScript.log("", () => "Script finished running");
|
||||
@ -583,7 +508,7 @@ export function updateOnlineScriptTimes(numCycles = 1): void {
|
||||
* Called when the game is loaded. Loads all running scripts (from all servers)
|
||||
* 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;
|
||||
if (skipScriptLoad) {
|
||||
Terminal.warn("Skipped loading player scripts during startup");
|
||||
@ -604,7 +529,7 @@ export function loadAllRunningScripts(player: IPlayer): void {
|
||||
} else {
|
||||
for (let j = 0; j < server.runningScripts.length; ++j) {
|
||||
const fileName = server.runningScripts[j].filename;
|
||||
createAndAddWorkerScript(player, server.runningScripts[j], server);
|
||||
createAndAddWorkerScript(server.runningScripts[j], server);
|
||||
|
||||
if (!server.runningScripts[j]) {
|
||||
// createAndAddWorkerScript can modify the server.runningScripts array if a script is invalid
|
||||
@ -623,7 +548,6 @@ export function loadAllRunningScripts(player: IPlayer): void {
|
||||
* Run a script from inside another script (run(), exec(), spawn(), etc.)
|
||||
*/
|
||||
export function runScriptFromScript(
|
||||
player: IPlayer,
|
||||
caller: string,
|
||||
server: BaseServer,
|
||||
scriptname: string,
|
||||
@ -698,7 +622,7 @@ export function runScriptFromScript(
|
||||
runningScriptObj.threads = threads;
|
||||
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}'`);
|
||||
|
@ -76,7 +76,7 @@ export function runScript(
|
||||
const runningScript = new RunningScript(script, args);
|
||||
runningScript.threads = numThreads;
|
||||
|
||||
const success = startWorkerScript(player, runningScript, server);
|
||||
const success = startWorkerScript(runningScript, server);
|
||||
if (!success) {
|
||||
terminal.error(`Failed to start script`);
|
||||
return;
|
||||
|
@ -278,7 +278,7 @@ const Engine: {
|
||||
Player.gainMoney(offlineHackingIncome, "hacking");
|
||||
// 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) {
|
||||
Player.focus = true;
|
||||
|
@ -15,7 +15,6 @@ import { workerScripts } from "../../Netscript/WorkerScripts";
|
||||
import { startWorkerScript } from "../../NetscriptWorker";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
import { findRunningScript } from "../../Script/ScriptHelpers";
|
||||
import { Player } from "../../Player";
|
||||
import { debounce } from "lodash";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { ANSIITypography } from "./ANSIITypography";
|
||||
@ -159,7 +158,7 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
if (server === null) return;
|
||||
const s = findRunningScript(script.filename, script.args, server);
|
||||
if (s === null) {
|
||||
startWorkerScript(Player, script, server);
|
||||
startWorkerScript(script, server);
|
||||
} else {
|
||||
setScript(s);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user