/** * The worker agent for running a script instance. Each running script instance * has its own underlying WorkerScript object. * * Note that these objects are not saved and re-loaded when the game is refreshed. * Instead, whenever the game is opened, WorkerScripts are re-created from * RunningScript objects */ import type React from "react"; import type { BaseServer } from "../Server/BaseServer"; import type { NSFull } from "../NetscriptFunctions"; import type { ScriptFilePath } from "../Paths/ScriptFilePath"; import type { RunningScript } from "../Script/RunningScript"; import type { Script } from "../Script/Script"; import type { ScriptArg } from "@nsdefs"; import type { ScriptDeath } from "./ScriptDeath"; import { Environment } from "./Environment"; import { RamCostConstants } from "./RamCostGenerator"; import { GetServer } from "../Server/AllServers"; export class WorkerScript { /** Script's arguments */ args: ScriptArg[]; /** Copy of the script's code */ code = ""; /** * Holds the timeoutID (numeric value) for whenever this script is blocked by a * timed Netscript function. i.e. Holds the return value of setTimeout() */ delay: number | null = null; /** Holds the Promise reject() function while the script is "blocked" by an async op */ delayReject: ((reason?: ScriptDeath) => void) | undefined = undefined; /** Stores names of all functions that have logging disabled */ disableLogs: Record = {}; /** * Used for dynamic RAM calculation. Stores names of all functions that have * already been checked by this script. * TODO: Could probably just combine this with loadedFns? */ dynamicLoadedFns: Record = {}; /** Tracks dynamic RAM usage */ dynamicRamUsage: number = RamCostConstants.Base; /** Netscript Environment for this script */ env: Environment; /** * Used for static RAM calculation. Stores names of all functions that have * already been checked by this script */ loadedFns: Record = {}; /** Filename of script */ name: ScriptFilePath; /** Script's output/return value. Currently not used or implemented */ output = ""; /** * Process ID. Must be an integer. Used for efficient script * killing and removal. */ pid: number; /** Reference to underlying RunningScript object */ scriptRef: RunningScript; /** hostname on which this script is running */ hostname: string; /**Map of functions called when the script ends. */ atExit: Map void> = new Map(); constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => NSFull) { this.name = runningScriptObj.filename; this.hostname = runningScriptObj.server; const sanitizedPid = Math.round(pid); if (typeof sanitizedPid !== "number" || isNaN(sanitizedPid)) { throw new Error(`Invalid PID when constructing WorkerScript: ${pid}`); } this.pid = sanitizedPid; runningScriptObj.pid = sanitizedPid; // Get the underlying script's code const server = GetServer(this.hostname); if (server == null) { throw new Error(`WorkerScript constructed with invalid server ip: ${this.hostname}`); } const script = server.scripts.get(this.name); if (!script) { throw new Error(`WorkerScript constructed with invalid script filename: ${this.name}`); } this.code = script.code; this.scriptRef = runningScriptObj; this.args = runningScriptObj.args.slice(); this.env = new Environment(); if (typeof nsFuncsGenerator === "function") { this.env.vars = nsFuncsGenerator(this); } } /** Returns the Server on which this script is running */ getServer(): BaseServer { const server = GetServer(this.hostname); if (server == null) throw new Error(`Script ${this.name} pid ${this.pid} is running on non-existent server?`); return server; } /** * Returns the Script object for the underlying script. * Returns null if it cannot be found (which would be a bug) */ getScript(): Script | null { const server = this.getServer(); const script = server.scripts.get(this.name); if (!script) { console.error( "Failed to find underlying Script object in WorkerScript.getScript(). This probably means somethings wrong", ); return null; } return script; } shouldLog(fn: string): boolean { return !(this.disableLogs.ALL || this.disableLogs[fn]); } log(func: string, txt: () => string): void { if (this.shouldLog(func)) { if (func && txt) { this.scriptRef.log(`${func}: ${txt()}`); } else if (func) { this.scriptRef.log(func); } else { this.scriptRef.log(txt()); } } } print(txt: React.ReactNode): void { this.scriptRef.log(txt); } }