diff --git a/src/Netscript/APIWrapper.ts b/src/Netscript/APIWrapper.ts index 924d67c14..5c491b5ef 100644 --- a/src/Netscript/APIWrapper.ts +++ b/src/Netscript/APIWrapper.ts @@ -2,6 +2,8 @@ import { getRamCost } from "./RamCostGenerator"; import type { WorkerScript } from "./WorkerScript"; import { Player } from "../Player"; import { helpers } from "./NetscriptHelpers"; +import { ScriptArg } from "./ScriptArg"; +import { NSEnums } from "src/ScriptEditor/NetscriptDefinitions"; type ExternalFunction = (...args: unknown[]) => unknown; export type ExternalAPI = { @@ -13,6 +15,10 @@ type InternalFunction unknown> = (ctx: Netscri export type InternalAPI = { [Property in keyof API]: API[Property] extends ExternalFunction ? InternalFunction + : API[Property] extends NSEnums + ? NSEnums + : API[Property] extends ScriptArg[] + ? ScriptArg[] : API[Property] extends object ? InternalAPI : never; diff --git a/src/Netscript/NetscriptHelpers.ts b/src/Netscript/NetscriptHelpers.ts index f525f03ea..872155393 100644 --- a/src/Netscript/NetscriptHelpers.ts +++ b/src/Netscript/NetscriptHelpers.ts @@ -26,6 +26,8 @@ import { IPlayer } from "../PersonObjects/IPlayer"; import { FormulaGang } from "../Gang/formulas/formulas"; import { GangMember } from "../Gang/GangMember"; import { GangMemberTask } from "../Gang/GangMemberTask"; +import { RunningScript } from "../Script/RunningScript"; +import { toNative } from "../NetscriptFunctions/toNative"; //Helpers that are not specific to use in WorkerScripts/NetscriptContexts should be in src/utils/helpers instead @@ -34,6 +36,7 @@ export const helpers = { string, number, scriptArgs, + argsToString, isScriptErrorMessage, //Error checking and generation makeRuntimeRejectMsg, @@ -56,6 +59,7 @@ export const helpers = { gangMember, gangTask, log, + getRunningScriptByArgs, }; export type ScriptIdentifier = //This was previously in INetscriptHelper.ts, may move to its own file or a generic types file. @@ -66,12 +70,14 @@ export type ScriptIdentifier = //This was previously in INetscriptHelper.ts, ma args: ScriptArg[]; }; +/** If v is a number or string, returns the string representation. Error for other non-strings. */ function string(ctx: NetscriptContext, argName: string, v: unknown): string { if (typeof v === "string") return v; if (typeof v === "number") return v + ""; // cast to string; throw makeRuntimeErrorMsg(ctx, `'${argName}' should be a string.`); } +/** Validates v as non-NaN number, or as a string representation of a number, and returns that number. Error on NaN or non-number. */ function number(ctx: NetscriptContext, argName: string, v: unknown): number { if (typeof v === "string") { const x = parseFloat(v); @@ -83,11 +89,13 @@ function number(ctx: NetscriptContext, argName: string, v: unknown): number { throw makeRuntimeErrorMsg(ctx, `'${argName}' should be a number.`); } +/** Validates args as a ScriptArg[]. Throws an error if it is not. */ function scriptArgs(ctx: NetscriptContext, args: unknown) { if (!isScriptArgs(args)) throw makeRuntimeErrorMsg(ctx, "'args' is not an array of script args"); return args; } +/** Determines if the given msg string is an error created by makeRuntimeRejectMsg. */ function isScriptErrorMessage(msg: string): boolean { if (!isString(msg)) { return false; @@ -96,6 +104,26 @@ function isScriptErrorMessage(msg: string): boolean { return splitMsg.length == 4; } +/** Used to convert multiple arguments for tprint or print into a single string. */ +function argsToString(args: unknown[]): string { + let out = ""; + for (let arg of args) { + if (arg === null) { + out += "null"; + continue; + } + if (arg === undefined) { + out += "undefined"; + continue; + } + arg = toNative(arg); + out += typeof arg === "object" ? JSON.stringify(arg) : `${arg}`; + } + + return out; +} + +/** Creates an error message string containing hostname, scriptname, and the error message msg */ function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string { for (const scriptUrl of workerScript.scriptRef.dependencies) { msg = msg.replace(new RegExp(scriptUrl.url, "g"), scriptUrl.filename); @@ -104,6 +132,7 @@ function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string { return "|DELIMITER|" + workerScript.hostname + "|DELIMITER|" + workerScript.name + "|DELIMITER|" + msg; } +/** Creates an error message string with a stack trace. */ function makeRuntimeErrorMsg(ctx: NetscriptContext, msg: string): string { const errstack = new Error().stack; if (errstack === undefined) throw new Error("how did we not throw an error?"); @@ -174,6 +203,7 @@ function makeRuntimeErrorMsg(ctx: NetscriptContext, msg: string): string { return makeRuntimeRejectMsg(workerScript, rejectMsg); } +/** Validate requested number of threads for h/g/w options */ function resolveNetscriptRequestedThreads(ctx: NetscriptContext, requestedThreads?: number): number { const threads = ctx.workerScript.scriptRef.threads; if (!requestedThreads) { @@ -195,6 +225,7 @@ function resolveNetscriptRequestedThreads(ctx: NetscriptContext, requestedThread return requestedThreadsAsInt; } +/** Validate singularity access by throwing an error if the player does not have access. */ function checkSingularityAccess(ctx: NetscriptContext): void { if (Player.bitNodeN !== 4 && Player.sourceFileLvl(4) === 0) { throw makeRuntimeErrorMsg( @@ -205,6 +236,7 @@ function checkSingularityAccess(ctx: NetscriptContext): void { } } +/** Create an error if a script is dead or if concurrent ns function calls are made */ function checkEnvFlags(ctx: NetscriptContext): void { const ws = ctx.workerScript; if (ws.env.stopFlag) throw new ScriptDeath(ws); @@ -503,3 +535,42 @@ function gangTask(ctx: NetscriptContext, t: unknown): GangMemberTask { function log(ctx: NetscriptContext, message: () => string) { ctx.workerScript.log(ctx.functionPath, message); } + +/** + * Searches for and returns the RunningScript object for the specified script. + * If the 'fn' argument is not specified, this returns the current RunningScript. + * @param {string} fn - Filename of script + * @param {string} hostname - Hostname/ip of the server on which the script resides + * @param {any[]} scriptArgs - Running script's arguments + * @returns {RunningScript} + * Running script identified by the parameters, or null if no such script + * exists, or the current running script if the first argument 'fn' + * is not specified. + */ +function getRunningScriptByArgs( + ctx: NetscriptContext, + fn: string, + hostname: string, + scriptArgs: ScriptArg[], +): RunningScript | null { + if (!Array.isArray(scriptArgs)) { + throw helpers.makeRuntimeRejectMsg( + ctx.workerScript, + `Invalid scriptArgs argument passed into getRunningScript() from ${ctx.function}(). ` + + `This is probably a bug. Please report to game developer`, + ); + } + + if (fn != null && typeof fn === "string") { + // Get Logs of another script + if (hostname == null) { + hostname = ctx.workerScript.hostname; + } + const server = helpers.getServer(ctx, hostname); + + return findRunningScript(fn, scriptArgs, server); + } + + // If no arguments are specified, return the current RunningScript + return ctx.workerScript.scriptRef; +} diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index a7717e90e..8a09972e9 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -115,2093 +115,2081 @@ interface NS extends INS { infiltration: IInfiltration; } +const argsToString = function (args: unknown[]): string { + let out = ""; + for (let arg of args) { + if (arg === null) { + out += "null"; + continue; + } + if (arg === undefined) { + out += "undefined"; + continue; + } + arg = toNative(arg); + out += typeof arg === "object" ? JSON.stringify(arg) : `${arg}`; + } + + return out; +}; + +function getFunctionNames(obj: object, prefix: string): string[] { + const functionNames: string[] = []; + for (const [key, value] of Object.entries(obj)) { + if (key === "args") { + continue; + } else if (typeof value == "function") { + functionNames.push(prefix + key); + } else if (typeof value == "object") { + functionNames.push(...getFunctionNames(value, key + ".")); + } + } + return functionNames; +} +const getRunningScriptByPid = function (pid: number): RunningScript | null { + for (const server of GetAllServers()) { + const runningScript = findRunningScriptByPid(pid, server); + if (runningScript) return runningScript; + } + return null; +}; + +const getRunningScript = (ctx: NetscriptContext, ident: ScriptIdentifier): RunningScript | null => { + if (typeof ident === "number") { + return getRunningScriptByPid(ident); + } else { + return getRunningScriptByArgs(ctx, ident.scriptname, ident.hostname, ident.args); + } +}; + +/** + * Searches for and returns the RunningScript object for the specified script. + * If the 'fn' argument is not specified, this returns the current RunningScript. + * @param {string} fn - Filename of script + * @param {string} hostname - Hostname/ip of the server on which the script resides + * @param {any[]} scriptArgs - Running script's arguments + * @returns {RunningScript} + * Running script identified by the parameters, or null if no such script + * exists, or the current running script if the first argument 'fn' + * is not specified. + */ +const getRunningScriptByArgs = function ( + ctx: NetscriptContext, + fn: string, + hostname: string, + scriptArgs: ScriptArg[], +): RunningScript | null { + if (!Array.isArray(scriptArgs)) { + throw helpers.makeRuntimeRejectMsg( + ctx.workerScript, + `Invalid scriptArgs argument passed into getRunningScript() from ${ctx.function}(). ` + + `This is probably a bug. Please report to game developer`, + ); + } + + if (fn != null && typeof fn === "string") { + // Get Logs of another script + if (hostname == null) { + hostname = ctx.workerScript.hostname; + } + const server = helpers.getServer(ctx, hostname); + + return findRunningScript(fn, scriptArgs, server); + } + + // If no arguments are specified, return the current RunningScript + return ctx.workerScript.scriptRef; +}; +/** + * Helper function for getting the error log message when the user specifies + * a nonexistent running script + * @param {string} fn - Filename of script + * @param {string} hostname - Hostname/ip of the server on which the script resides + * @param {any[]} scriptArgs - Running script's arguments + * @returns {string} Error message to print to logs + */ +const getCannotFindRunningScriptErrorMessage = function (ident: ScriptIdentifier): string { + if (typeof ident === "number") return `Cannot find running script with pid: ${ident}`; + + return `Cannot find running script ${ident.scriptname} on server ${ident.hostname} with args: ${arrayToString( + ident.args, + )}`; +}; +/** + * Sanitizes a `RunningScript` to remove sensitive information, making it suitable for + * return through an NS function. + * @see NS.getRecentScripts + * @see NS.getRunningScript + * @param runningScript Existing, internal RunningScript + * @returns A sanitized, NS-facing copy of the RunningScript + */ +const createPublicRunningScript = function (runningScript: RunningScript): IRunningScript { + return { + args: runningScript.args.slice(), + filename: runningScript.filename, + logs: runningScript.logs.slice(), + offlineExpGained: runningScript.offlineExpGained, + offlineMoneyMade: runningScript.offlineMoneyMade, + offlineRunningTime: runningScript.offlineRunningTime, + onlineExpGained: runningScript.onlineExpGained, + onlineMoneyMade: runningScript.onlineMoneyMade, + onlineRunningTime: runningScript.onlineRunningTime, + pid: runningScript.pid, + ramUsage: runningScript.ramUsage, + server: runningScript.server, + threads: runningScript.threads, + }; +}; +/** + * Used to fail a function if the function's target is a Hacknet Server. + * This is used for functions that should run on normal Servers, but not Hacknet Servers + * @param {Server} server - Target server + * @param {string} callingFn - Name of calling function. For logging purposes + * @returns {boolean} True if the server is a Hacknet Server, false otherwise + */ +const failOnHacknetServer = function (ctx:NetscriptContext, server: BaseServer, callingFn = ""): boolean { + if (server instanceof HacknetServer) { + ctx.workerScript.log(callingFn, () => `Does not work on Hacknet Servers`); + return true; + } else { + return false; + } +}; + export function NetscriptFunctions(workerScript: WorkerScript): NS { - /** - * Searches for and returns the RunningScript object for the specified script. - * If the 'fn' argument is not specified, this returns the current RunningScript. - * @param {string} fn - Filename of script - * @param {string} hostname - Hostname/ip of the server on which the script resides - * @param {any[]} scriptArgs - Running script's arguments - * @returns {RunningScript} - * Running script identified by the parameters, or null if no such script - * exists, or the current running script if the first argument 'fn' - * is not specified. - */ - const getRunningScriptByArgs = function ( - ctx: NetscriptContext, - fn: string, - hostname: string, - scriptArgs: ScriptArg[], - ): RunningScript | null { - if (!Array.isArray(scriptArgs)) { - throw helpers.makeRuntimeRejectMsg( - workerScript, - `Invalid scriptArgs argument passed into getRunningScript() from ${ctx.function}(). ` + - `This is probably a bug. Please report to game developer`, - ); - } - if (fn != null && typeof fn === "string") { - // Get Logs of another script - if (hostname == null) { - hostname = workerScript.hostname; - } + const wrappedNS = wrapAPI({}, workerScript, ns) as unknown as INS; + (wrappedNS.args as ScriptArg[]) = workerScript.args; + return wrappedNS; +} + +const base: InternalAPI = { + args: [], + enums: { + toast: ToastVariant, + }, + + singularity: NetscriptSingularity(), + gang: NetscriptGang(), + bladeburner: NetscriptBladeburner(), + codingcontract: NetscriptCodingContract(), + sleeve: NetscriptSleeve(), + corporation: NetscriptCorporation(), + stanek: NetscriptStanek(), + infiltration: NetscriptInfiltration(), + ui: NetscriptUserInterface(), + formulas: NetscriptFormulas(), + stock: NetscriptStockMarket(), + grafting: NetscriptGrafting(), + hacknet: NetscriptHacknet(), + sprintf: () => sprintf, + vsprintf: () => vsprintf, + scan: + (ctx: NetscriptContext) => + (_hostname: unknown = ctx.workerScript.hostname): string[] => { + const hostname = helpers.string(ctx, "hostname", _hostname); const server = helpers.getServer(ctx, hostname); - - return findRunningScript(fn, scriptArgs, server); - } - - // If no arguments are specified, return the current RunningScript - return workerScript.scriptRef; - }; - - const getRunningScriptByPid = function (pid: number): RunningScript | null { - for (const server of GetAllServers()) { - const runningScript = findRunningScriptByPid(pid, server); - if (runningScript) return runningScript; - } - return null; - }; - - const getRunningScript = (ctx: NetscriptContext, ident: ScriptIdentifier): RunningScript | null => { - if (typeof ident === "number") { - return getRunningScriptByPid(ident); - } else { - return getRunningScriptByArgs(ctx, ident.scriptname, ident.hostname, ident.args); - } - }; - - /** - * Sanitizes a `RunningScript` to remove sensitive information, making it suitable for - * return through an NS function. - * @see NS.getRecentScripts - * @see NS.getRunningScript - * @param runningScript Existing, internal RunningScript - * @returns A sanitized, NS-facing copy of the RunningScript - */ - const createPublicRunningScript = function (runningScript: RunningScript): IRunningScript { - return { - args: runningScript.args.slice(), - filename: runningScript.filename, - logs: runningScript.logs.slice(), - offlineExpGained: runningScript.offlineExpGained, - offlineMoneyMade: runningScript.offlineMoneyMade, - offlineRunningTime: runningScript.offlineRunningTime, - onlineExpGained: runningScript.onlineExpGained, - onlineMoneyMade: runningScript.onlineMoneyMade, - onlineRunningTime: runningScript.onlineRunningTime, - pid: runningScript.pid, - ramUsage: runningScript.ramUsage, - server: runningScript.server, - threads: runningScript.threads, - }; - }; - - /** - * Helper function for getting the error log message when the user specifies - * a nonexistent running script - * @param {string} fn - Filename of script - * @param {string} hostname - Hostname/ip of the server on which the script resides - * @param {any[]} scriptArgs - Running script's arguments - * @returns {string} Error message to print to logs - */ - const getCannotFindRunningScriptErrorMessage = function (ident: ScriptIdentifier): string { - if (typeof ident === "number") return `Cannot find running script with pid: ${ident}`; - - return `Cannot find running script ${ident.scriptname} on server ${ident.hostname} with args: ${arrayToString( - ident.args, - )}`; - }; - - /** - * Used to fail a function if the function's target is a Hacknet Server. - * This is used for functions that should run on normal Servers, but not Hacknet Servers - * @param {Server} server - Target server - * @param {string} callingFn - Name of calling function. For logging purposes - * @returns {boolean} True if the server is a Hacknet Server, false otherwise - */ - const failOnHacknetServer = function (server: BaseServer, callingFn = ""): boolean { - if (server instanceof HacknetServer) { - workerScript.log(callingFn, () => `Does not work on Hacknet Servers`); - return true; - } else { - return false; - } - }; - - const argsToString = function (args: unknown[]): string { - let out = ""; - for (let arg of args) { - if (arg === null) { - out += "null"; - continue; + const out = []; + for (let i = 0; i < server.serversOnNetwork.length; i++) { + const s = getServerOnNetwork(server, i); + if (s === null) continue; + const entry = s.hostname; + if (entry === null) continue; + out.push(entry); } - if (arg === undefined) { - out += "undefined"; - continue; + helpers.log(ctx, () => `returned ${server.serversOnNetwork.length} connections for ${server.hostname}`); + return out; + }, + hack: + (ctx: NetscriptContext) => + (_hostname: unknown, { threads: requestedThreads, stock }: BasicHGWOptions = {}): Promise => { + const hostname = helpers.string(ctx, "hostname", _hostname); + return helpers.hack(ctx, hostname, false, { threads: requestedThreads, stock: stock }); + }, + hackAnalyzeThreads: + (ctx: NetscriptContext) => + (_hostname: unknown, _hackAmount: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const hackAmount = helpers.number(ctx, "hackAmount", _hackAmount); + + // Check argument validity + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return -1; + } + if (isNaN(hackAmount)) { + throw helpers.makeRuntimeErrorMsg( + ctx, + `Invalid hackAmount argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric.`, + ); } - arg = toNative(arg); - out += typeof arg === "object" ? JSON.stringify(arg) : `${arg}`; - } - return out; - }; + if (hackAmount < 0 || hackAmount > server.moneyAvailable) { + return -1; + } else if (hackAmount === 0) { + return 0; + } - const singularity = NetscriptSingularity(Player, workerScript); - const base: InternalAPI = { - args: workerScript.args as unknown as any, - enums: { - toast: ToastVariant, - } as unknown as any, + const percentHacked = calculatePercentMoneyHacked(server, Player); - singularity: singularity, - gang: NetscriptGang(Player, workerScript), - bladeburner: NetscriptBladeburner(Player, workerScript), - codingcontract: NetscriptCodingContract(Player, workerScript), - sleeve: NetscriptSleeve(Player), - corporation: NetscriptCorporation(Player), - stanek: NetscriptStanek(Player, workerScript), - infiltration: NetscriptInfiltration(Player), - ui: NetscriptUserInterface(), - formulas: NetscriptFormulas(Player), - stock: NetscriptStockMarket(Player, workerScript), - grafting: NetscriptGrafting(Player), - hacknet: NetscriptHacknet(Player, workerScript), - sprintf: () => sprintf, - vsprintf: () => vsprintf, - scan: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname): string[] => { + if (percentHacked === 0 || server.moneyAvailable === 0) { + return 0; // To prevent returning infinity below + } + + return hackAmount / Math.floor(server.moneyAvailable * percentHacked); + }, + hackAnalyze: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 0; + } + + return calculatePercentMoneyHacked(server, Player); + }, + hackAnalyzeSecurity: + (ctx: NetscriptContext) => + (_threads: unknown, _hostname?: unknown): number => { + let threads = helpers.number(ctx, "threads", _threads); + if (_hostname) { const hostname = helpers.string(ctx, "hostname", _hostname); const server = helpers.getServer(ctx, hostname); - const out = []; - for (let i = 0; i < server.serversOnNetwork.length; i++) { - const s = getServerOnNetwork(server, i); - if (s === null) continue; - const entry = s.hostname; - if (entry === null) continue; - out.push(entry); - } - helpers.log(ctx, () => `returned ${server.serversOnNetwork.length} connections for ${server.hostname}`); - return out; - }, - hack: - (ctx: NetscriptContext) => - (_hostname: unknown, { threads: requestedThreads, stock }: BasicHGWOptions = {}): Promise => { - const hostname = helpers.string(ctx, "hostname", _hostname); - return helpers.hack(ctx, hostname, false, { threads: requestedThreads, stock: stock }); - }, - hackAnalyzeThreads: - (ctx: NetscriptContext) => - (_hostname: unknown, _hackAmount: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const hackAmount = helpers.number(ctx, "hackAmount", _hackAmount); - - // Check argument validity - const server = helpers.getServer(ctx, hostname); if (!(server instanceof Server)) { helpers.log(ctx, () => "Cannot be executed on this server."); - return -1; - } - if (isNaN(hackAmount)) { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Invalid hackAmount argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric.`, - ); - } - - if (hackAmount < 0 || hackAmount > server.moneyAvailable) { - return -1; - } else if (hackAmount === 0) { return 0; } const percentHacked = calculatePercentMoneyHacked(server, Player); - if (percentHacked === 0 || server.moneyAvailable === 0) { - return 0; // To prevent returning infinity below + if (percentHacked > 0) { + // thread count is limited to the maximum number of threads needed + threads = Math.min(threads, Math.ceil(1 / percentHacked)); } + } - return hackAmount / Math.floor(server.moneyAvailable * percentHacked); - }, - hackAnalyze: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); + return CONSTANTS.ServerFortifyAmount * threads; + }, + hackAnalyzeChance: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 0; - } + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 0; + } - return calculatePercentMoneyHacked(server, Player); - }, - hackAnalyzeSecurity: - (ctx: NetscriptContext) => - (_threads: unknown, _hostname?: unknown): number => { - let threads = helpers.number(ctx, "threads", _threads); - if (_hostname) { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 0; - } - - const percentHacked = calculatePercentMoneyHacked(server, Player); - - if (percentHacked > 0) { - // thread count is limited to the maximum number of threads needed - threads = Math.min(threads, Math.ceil(1 / percentHacked)); - } - } - - return CONSTANTS.ServerFortifyAmount * threads; - }, - hackAnalyzeChance: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 0; - } - - return calculateHackingChance(server, Player); - }, - sleep: - (ctx: NetscriptContext) => - async (_time: unknown = 0): Promise => { - const time = helpers.number(ctx, "time", _time); - if (time === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); - } - helpers.log(ctx, () => `Sleeping for ${time} milliseconds`); - return helpers.netscriptDelay(ctx, time).then(function () { - return Promise.resolve(true); - }); - }, - asleep: (ctx: NetscriptContext) => - function (_time: unknown = 0): Promise { - const time = helpers.number(ctx, "time", _time); - helpers.log(ctx, () => `Sleeping for ${time} milliseconds`); - return new Promise((resolve) => setTimeout(() => resolve(true), time)); - }, - grow: - (ctx: NetscriptContext) => - async (_hostname: unknown, { threads: requestedThreads, stock }: BasicHGWOptions = {}): Promise => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const threads = helpers.resolveNetscriptRequestedThreads( - ctx, - requestedThreads ?? workerScript.scriptRef.threads, - ); - - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return Promise.resolve(0); - } - - const host = GetServer(workerScript.hostname); - if (host === null) { - throw new Error("Workerscript host is null"); - } - - // No root access or skill level too low - const canHack = netscriptCanGrow(server); - if (!canHack.res) { - throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || ""); - } - - const growTime = calculateGrowTime(server, Player); - helpers.log( - ctx, - () => - `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( - growTime * 1000, - true, - )} (t=${numeralWrapper.formatThreads(threads)}).`, - ); - return helpers.netscriptDelay(ctx, growTime * 1000).then(function () { - const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable; - processSingleServerGrowth(server, threads, Player, host.cpuCores); - const moneyAfter = server.moneyAvailable; - workerScript.scriptRef.recordGrow(server.hostname, threads); - const expGain = calculateHackingExpGain(server, Player) * threads; - const logGrowPercent = moneyAfter / moneyBefore - 1; - helpers.log( - ctx, - () => - `Available money on '${server.hostname}' grown by ${numeralWrapper.formatPercentage( - logGrowPercent, - 6, - )}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads( - threads, - )}).`, - ); - workerScript.scriptRef.onlineExpGained += expGain; - Player.gainHackingExp(expGain); - if (stock) { - influenceStockThroughServerGrow(server, moneyAfter - moneyBefore); - } - return Promise.resolve(moneyAfter / moneyBefore); - }); - }, - growthAnalyze: - (ctx: NetscriptContext) => - (_hostname: unknown, _growth: unknown, _cores: unknown = 1): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const growth = helpers.number(ctx, "growth", _growth); - const cores = helpers.number(ctx, "cores", _cores); - - // Check argument validity - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 0; - } - if (typeof growth !== "number" || isNaN(growth) || growth < 1 || !isFinite(growth)) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: growth must be numeric and >= 1, is ${growth}.`); - } - - return numCycleForGrowth(server, Number(growth), Player, cores); - }, - growthAnalyzeSecurity: - (ctx: NetscriptContext) => - (_threads: unknown, _hostname?: unknown, _cores?: unknown): number => { - let threads = helpers.number(ctx, "threads", _threads); - if (_hostname) { - const cores = helpers.number(ctx, "cores", _cores) || 1; - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 0; - } - - const maxThreadsNeeded = Math.ceil( - numCycleForGrowthCorrected(server, server.moneyMax, server.moneyAvailable, Player, cores), - ); - - threads = Math.min(threads, maxThreadsNeeded); - } - - return 2 * CONSTANTS.ServerFortifyAmount * threads; - }, - weaken: - (ctx: NetscriptContext) => - async (_hostname: unknown, { threads: requestedThreads }: BasicHGWOptions = {}): Promise => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const threads = helpers.resolveNetscriptRequestedThreads( - ctx, - requestedThreads ?? workerScript.scriptRef.threads, - ); - if (hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); - } - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return Promise.resolve(0); - } - - // No root access or skill level too low - const canHack = netscriptCanWeaken(server); - if (!canHack.res) { - throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || ""); - } - - const weakenTime = calculateWeakenTime(server, Player); - helpers.log( - ctx, - () => - `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( - weakenTime * 1000, - true, - )} (t=${numeralWrapper.formatThreads(threads)})`, - ); - return helpers.netscriptDelay(ctx, weakenTime * 1000).then(function () { - const host = GetServer(workerScript.hostname); - if (host === null) { - helpers.log(ctx, () => "Server is null, did it die?"); - return Promise.resolve(0); - } - const coreBonus = 1 + (host.cpuCores - 1) / 16; - server.weaken(CONSTANTS.ServerWeakenAmount * threads * coreBonus); - workerScript.scriptRef.recordWeaken(server.hostname, threads); - const expGain = calculateHackingExpGain(server, Player) * threads; - helpers.log( - ctx, - () => - `'${server.hostname}' security level weakened to ${ - server.hackDifficulty - }. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`, - ); - workerScript.scriptRef.onlineExpGained += expGain; - Player.gainHackingExp(expGain); - return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads * coreBonus); - }); - }, - weakenAnalyze: - (ctx: NetscriptContext) => - (_threads: unknown, _cores: unknown = 1): number => { - const threads = helpers.number(ctx, "threads", _threads); - const cores = helpers.number(ctx, "cores", _cores); - const coreBonus = 1 + (cores - 1) / 16; - return CONSTANTS.ServerWeakenAmount * threads * coreBonus * BitNodeMultipliers.ServerWeakenRate; - }, - share: (ctx: NetscriptContext) => async (): Promise => { - helpers.log(ctx, () => "Sharing this computer."); - const end = StartSharing( - workerScript.scriptRef.threads * calculateIntelligenceBonus(Player.skills.intelligence, 2), - ); - return helpers.netscriptDelay(ctx, 10000).finally(function () { - helpers.log(ctx, () => "Finished sharing this computer."); - end(); + return calculateHackingChance(server, Player); + }, + sleep: + (ctx: NetscriptContext) => + async (_time: unknown = 0): Promise => { + const time = helpers.number(ctx, "time", _time); + if (time === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); + } + helpers.log(ctx, () => `Sleeping for ${time} milliseconds`); + return helpers.netscriptDelay(ctx, time).then(function () { + return Promise.resolve(true); }); }, - getSharePower: () => (): number => { - return CalculateShareMult(); + asleep: (ctx: NetscriptContext) => + function (_time: unknown = 0): Promise { + const time = helpers.number(ctx, "time", _time); + helpers.log(ctx, () => `Sleeping for ${time} milliseconds`); + return new Promise((resolve) => setTimeout(() => resolve(true), time)); }, - print: - (ctx: NetscriptContext) => - (...args: unknown[]): void => { - if (args.length === 0) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes at least 1 argument."); - } - workerScript.print(argsToString(args)); - }, - printf: - (ctx: NetscriptContext) => - (_format: unknown, ...args: unknown[]): void => { - const format = helpers.string(ctx, "format", _format); - if (typeof format !== "string") { - throw helpers.makeRuntimeErrorMsg(ctx, "First argument must be string for the format."); - } - workerScript.print(vsprintf(format, args)); - }, - tprint: - (ctx: NetscriptContext) => - (...args: unknown[]): void => { - if (args.length === 0) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes at least 1 argument."); - } - const str = argsToString(args); - if (str.startsWith("ERROR") || str.startsWith("FAIL")) { - Terminal.error(`${workerScript.scriptRef.filename}: ${str}`); - return; - } - if (str.startsWith("SUCCESS")) { - Terminal.success(`${workerScript.scriptRef.filename}: ${str}`); - return; - } - if (str.startsWith("WARN")) { - Terminal.warn(`${workerScript.scriptRef.filename}: ${str}`); - return; - } - if (str.startsWith("INFO")) { - Terminal.info(`${workerScript.scriptRef.filename}: ${str}`); - return; - } - Terminal.print(`${workerScript.scriptRef.filename}: ${str}`); - }, - tprintf: - (ctx: NetscriptContext) => - (_format: unknown, ...args: unknown[]): void => { - const format = helpers.string(ctx, "format", _format); - const str = vsprintf(format, args); + grow: + (ctx: NetscriptContext) => + async (_hostname: unknown, { threads: requestedThreads, stock }: BasicHGWOptions = {}): Promise => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const threads = helpers.resolveNetscriptRequestedThreads( + ctx, + requestedThreads ?? ctx.workerScript.scriptRef.threads, + ); - if (str.startsWith("ERROR") || str.startsWith("FAIL")) { - Terminal.error(`${str}`); - return; - } - if (str.startsWith("SUCCESS")) { - Terminal.success(`${str}`); - return; - } - if (str.startsWith("WARN")) { - Terminal.warn(`${str}`); - return; - } - if (str.startsWith("INFO")) { - Terminal.info(`${str}`); - return; - } - Terminal.print(`${str}`); - }, - clearLog: () => (): void => { - workerScript.scriptRef.clearLog(); - }, - disableLog: - (ctx: NetscriptContext) => - (_fn: unknown): void => { - const fn = helpers.string(ctx, "fn", _fn); - if (fn === "ALL") { - for (const fn of Object.keys(possibleLogs)) { - workerScript.disableLogs[fn] = true; - } - helpers.log(ctx, () => `Disabled logging for all functions`); - } else if (possibleLogs[fn] === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${fn}.`); - } else { - workerScript.disableLogs[fn] = true; - helpers.log(ctx, () => `Disabled logging for ${fn}`); - } - }, - enableLog: - (ctx: NetscriptContext) => - (_fn: unknown): void => { - const fn = helpers.string(ctx, "fn", _fn); - if (fn === "ALL") { - for (const fn of Object.keys(possibleLogs)) { - delete workerScript.disableLogs[fn]; - } - helpers.log(ctx, () => `Enabled logging for all functions`); - } else if (possibleLogs[fn] === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${fn}.`); - } - delete workerScript.disableLogs[fn]; - helpers.log(ctx, () => `Enabled logging for ${fn}`); - }, - isLogEnabled: - (ctx: NetscriptContext) => - (_fn: unknown): boolean => { - const fn = helpers.string(ctx, "fn", _fn); - if (possibleLogs[fn] === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${fn}.`); - } - return !workerScript.disableLogs[fn]; - }, - getScriptLogs: - (ctx: NetscriptContext) => - (scriptID: unknown, hostname: unknown, ...scriptArgs: unknown[]): string[] => { - const ident = helpers.scriptIdentifier(ctx, scriptID, hostname, scriptArgs); - const runningScriptObj = getRunningScript(ctx, ident); - if (runningScriptObj == null) { - helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); - return []; - } + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return Promise.resolve(0); + } - return runningScriptObj.logs.slice(); - }, - tail: - (ctx: NetscriptContext) => - (scriptID: unknown, hostname: unknown, ...scriptArgs: unknown[]): void => { - const ident = helpers.scriptIdentifier(ctx, scriptID, hostname, scriptArgs); - const runningScriptObj = getRunningScript(ctx, ident); - if (runningScriptObj == null) { - helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); - return; - } + const host = GetServer(ctx.workerScript.hostname); + if (host === null) { + throw new Error("Workerscript host is null"); + } - LogBoxEvents.emit(runningScriptObj); - }, + // No root access or skill level too low + const canHack = netscriptCanGrow(server); + if (!canHack.res) { + throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || ""); + } - closeTail: - (ctx: NetscriptContext) => - (_pid: unknown = workerScript.scriptRef.pid): void => { - const pid = helpers.number(ctx, "pid", _pid); - //Emit an event to tell the game to close the tail window if it exists - LogBoxCloserEvents.emit(pid); - }, - nuke: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return false; - } - if (server.hasAdminRights) { - helpers.log(ctx, () => `Already have root access to '${server.hostname}'.`); - return true; - } - if (!Player.hasProgram(Programs.NukeProgram.name)) { - throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the NUKE.exe virus!"); - } - if (server.openPortCount < server.numOpenPortsRequired) { - throw helpers.makeRuntimeErrorMsg(ctx, "Not enough ports opened to use NUKE.exe virus."); - } - server.hasAdminRights = true; - helpers.log(ctx, () => `Executed NUKE.exe virus on '${server.hostname}' to gain root access.`); - return true; - }, - brutessh: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.BruteSSHProgram.name)) { - throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the BruteSSH.exe program!"); - } - if (!server.sshPortOpen) { - helpers.log(ctx, () => `Executed BruteSSH.exe on '${server.hostname}' to open SSH port (22).`); - server.sshPortOpen = true; - ++server.openPortCount; - } else { - helpers.log(ctx, () => `SSH Port (22) already opened on '${server.hostname}'.`); - } - return true; - }, - ftpcrack: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - if (hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); - } - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.FTPCrackProgram.name)) { - throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the FTPCrack.exe program!"); - } - if (!server.ftpPortOpen) { - helpers.log(ctx, () => `Executed FTPCrack.exe on '${server.hostname}' to open FTP port (21).`); - server.ftpPortOpen = true; - ++server.openPortCount; - } else { - helpers.log(ctx, () => `FTP Port (21) already opened on '${server.hostname}'.`); - } - return true; - }, - relaysmtp: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - if (hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); - } - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.RelaySMTPProgram.name)) { - throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the relaySMTP.exe program!"); - } - if (!server.smtpPortOpen) { - helpers.log(ctx, () => `Executed relaySMTP.exe on '${server.hostname}' to open SMTP port (25).`); - server.smtpPortOpen = true; - ++server.openPortCount; - } else { - helpers.log(ctx, () => `SMTP Port (25) already opened on '${server.hostname}'.`); - } - return true; - }, - httpworm: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - if (hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument"); - } - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.HTTPWormProgram.name)) { - throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the HTTPWorm.exe program!"); - } - if (!server.httpPortOpen) { - helpers.log(ctx, () => `Executed HTTPWorm.exe on '${server.hostname}' to open HTTP port (80).`); - server.httpPortOpen = true; - ++server.openPortCount; - } else { - helpers.log(ctx, () => `HTTP Port (80) already opened on '${server.hostname}'.`); - } - return true; - }, - sqlinject: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - if (hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); - } - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.SQLInjectProgram.name)) { - throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the SQLInject.exe program!"); - } - if (!server.sqlPortOpen) { - helpers.log(ctx, () => `Executed SQLInject.exe on '${server.hostname}' to open SQL port (1433).`); - server.sqlPortOpen = true; - ++server.openPortCount; - } else { - helpers.log(ctx, () => `SQL Port (1433) already opened on '${server.hostname}'.`); - } - return true; - }, - run: - (ctx: NetscriptContext) => - (_scriptname: unknown, _threads: unknown = 1, ..._args: unknown[]): number => { - const scriptname = helpers.string(ctx, "scriptname", _scriptname); - const threads = helpers.number(ctx, "threads", _threads); - const args = helpers.scriptArgs(ctx, _args); - if (scriptname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Usage: run(scriptname, [numThreads], [arg1], [arg2]...)"); - } - if (isNaN(threads) || threads <= 0) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`); - } - const scriptServer = GetServer(workerScript.hostname); - if (scriptServer == null) { - throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev."); - } - - return runScriptFromScript(Player, "run", scriptServer, scriptname, args, workerScript, threads); - }, - exec: - (ctx: NetscriptContext) => - (_scriptname: unknown, _hostname: unknown, _threads: unknown = 1, ..._args: unknown[]): number => { - const scriptname = helpers.string(ctx, "scriptname", _scriptname); - const hostname = helpers.string(ctx, "hostname", _hostname); - const threads = helpers.number(ctx, "threads", _threads); - const args = helpers.scriptArgs(ctx, _args); - if (scriptname === undefined || hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Usage: exec(scriptname, server, [numThreads], [arg1], [arg2]...)"); - } - if (isNaN(threads) || threads <= 0) { - 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, workerScript, threads); - }, - spawn: - (ctx: NetscriptContext) => - (_scriptname: unknown, _threads: unknown = 1, ..._args: unknown[]): void => { - const scriptname = helpers.string(ctx, "scriptname", _scriptname); - const threads = helpers.number(ctx, "threads", _threads); - const args = helpers.scriptArgs(ctx, _args); - if (!scriptname || !threads) { - throw helpers.makeRuntimeErrorMsg(ctx, "Usage: spawn(scriptname, threads)"); - } - - const spawnDelay = 10; - setTimeout(() => { - if (isNaN(threads) || threads <= 0) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`); - } - const scriptServer = GetServer(workerScript.hostname); - if (scriptServer == null) { - throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev"); - } - - return runScriptFromScript(Player, "spawn", scriptServer, scriptname, args, workerScript, threads); - }, spawnDelay * 1e3); - - helpers.log(ctx, () => `Will execute '${scriptname}' in ${spawnDelay} seconds`); - - workerScript.running = false; // Prevent workerScript from "finishing execution naturally" - if (killWorkerScript(workerScript)) { - helpers.log(ctx, () => "Exiting..."); - } - }, - kill: - (ctx: NetscriptContext) => - (scriptID: unknown, hostname: unknown, ...scriptArgs: unknown[]): boolean => { - const ident = helpers.scriptIdentifier(ctx, scriptID, hostname, scriptArgs); - let res; - const killByPid = typeof ident === "number"; - if (killByPid) { - // Kill by pid - res = killWorkerScript(ident); - } else { - // Kill by filename/hostname - if (scriptID === undefined || hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Usage: kill(scriptname, server, [arg1], [arg2]...)"); - } - - const server = helpers.getServer(ctx, ident.hostname); - const runningScriptObj = getRunningScriptByArgs(ctx, ident.scriptname, ident.hostname, ident.args); - if (runningScriptObj == null) { - helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); - return false; - } - - res = killWorkerScript({ runningScript: runningScriptObj, hostname: server.hostname }); - } - - if (res) { - if (killByPid) { - helpers.log(ctx, () => `Killing script with PID ${ident}`); - } else { - helpers.log(ctx, () => `Killing '${scriptID}' on '${hostname}' with args: ${arrayToString(scriptArgs)}.`); - } - return true; - } else { - if (killByPid) { - helpers.log(ctx, () => `No script with PID ${ident}`); - } else { - helpers.log( - ctx, - () => `No such script '${scriptID}' on '${hostname}' with args: ${arrayToString(scriptArgs)}`, - ); - } - return false; - } - }, - killall: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname, _safetyguard: unknown = true): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const safetyguard = !!_safetyguard; - if (hostname === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Usage: killall(hostname, [safetyguard boolean])"); - } - const server = helpers.getServer(ctx, hostname); - - let scriptsKilled = 0; - - for (let i = server.runningScripts.length - 1; i >= 0; --i) { - if (safetyguard === true && server.runningScripts[i].pid == workerScript.pid) continue; - killWorkerScript({ runningScript: server.runningScripts[i], hostname: server.hostname }); - ++scriptsKilled; - } - WorkerScriptStartStopEventEmitter.emit(); + const growTime = calculateGrowTime(server, Player); + helpers.log( + ctx, + () => + `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( + growTime * 1000, + true, + )} (t=${numeralWrapper.formatThreads(threads)}).`, + ); + return helpers.netscriptDelay(ctx, growTime * 1000).then(function () { + const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable; + processSingleServerGrowth(server, threads, Player, host.cpuCores); + const moneyAfter = server.moneyAvailable; + ctx.workerScript.scriptRef.recordGrow(server.hostname, threads); + const expGain = calculateHackingExpGain(server, Player) * threads; + const logGrowPercent = moneyAfter / moneyBefore - 1; helpers.log( ctx, - () => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`, + () => + `Available money on '${server.hostname}' grown by ${numeralWrapper.formatPercentage( + logGrowPercent, + 6, + )}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)}).`, + ); + ctx.workerScript.scriptRef.onlineExpGained += expGain; + Player.gainHackingExp(expGain); + if (stock) { + influenceStockThroughServerGrow(server, moneyAfter - moneyBefore); + } + return Promise.resolve(moneyAfter / moneyBefore); + }); + }, + growthAnalyze: + (ctx: NetscriptContext) => + (_hostname: unknown, _growth: unknown, _cores: unknown = 1): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const growth = helpers.number(ctx, "growth", _growth); + const cores = helpers.number(ctx, "cores", _cores); + + // Check argument validity + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 0; + } + if (typeof growth !== "number" || isNaN(growth) || growth < 1 || !isFinite(growth)) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: growth must be numeric and >= 1, is ${growth}.`); + } + + return numCycleForGrowth(server, Number(growth), Player, cores); + }, + growthAnalyzeSecurity: + (ctx: NetscriptContext) => + (_threads: unknown, _hostname?: unknown, _cores?: unknown): number => { + let threads = helpers.number(ctx, "threads", _threads); + if (_hostname) { + const cores = helpers.number(ctx, "cores", _cores) || 1; + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 0; + } + + const maxThreadsNeeded = Math.ceil( + numCycleForGrowthCorrected(server, server.moneyMax, server.moneyAvailable, Player, cores), ); - return scriptsKilled > 0; - }, - exit: (ctx: NetscriptContext) => (): void => { - workerScript.running = false; // Prevent workerScript from "finishing execution naturally" - if (killWorkerScript(workerScript)) { - helpers.log(ctx, () => "Exiting..."); + threads = Math.min(threads, maxThreadsNeeded); + } + + return 2 * CONSTANTS.ServerFortifyAmount * threads; + }, + weaken: + (ctx: NetscriptContext) => + async (_hostname: unknown, { threads: requestedThreads }: BasicHGWOptions = {}): Promise => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const threads = helpers.resolveNetscriptRequestedThreads( + ctx, + requestedThreads ?? ctx.workerScript.scriptRef.threads, + ); + if (hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); + } + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return Promise.resolve(0); + } + + // No root access or skill level too low + const canHack = netscriptCanWeaken(server); + if (!canHack.res) { + throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || ""); + } + + const weakenTime = calculateWeakenTime(server, Player); + helpers.log( + ctx, + () => + `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( + weakenTime * 1000, + true, + )} (t=${numeralWrapper.formatThreads(threads)})`, + ); + return helpers.netscriptDelay(ctx, weakenTime * 1000).then(function () { + const host = GetServer(ctx.workerScript.hostname); + if (host === null) { + helpers.log(ctx, () => "Server is null, did it die?"); + return Promise.resolve(0); + } + const coreBonus = 1 + (host.cpuCores - 1) / 16; + server.weaken(CONSTANTS.ServerWeakenAmount * threads * coreBonus); + ctx.workerScript.scriptRef.recordWeaken(server.hostname, threads); + const expGain = calculateHackingExpGain(server, Player) * threads; + helpers.log( + ctx, + () => + `'${server.hostname}' security level weakened to ${ + server.hackDifficulty + }. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`, + ); + ctx.workerScript.scriptRef.onlineExpGained += expGain; + Player.gainHackingExp(expGain); + return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads * coreBonus); + }); + }, + weakenAnalyze: + (ctx: NetscriptContext) => + (_threads: unknown, _cores: unknown = 1): number => { + const threads = helpers.number(ctx, "threads", _threads); + const cores = helpers.number(ctx, "cores", _cores); + const coreBonus = 1 + (cores - 1) / 16; + return CONSTANTS.ServerWeakenAmount * threads * coreBonus * BitNodeMultipliers.ServerWeakenRate; + }, + share: (ctx: NetscriptContext) => async (): Promise => { + helpers.log(ctx, () => "Sharing this computer."); + const end = StartSharing( + ctx.workerScript.scriptRef.threads * calculateIntelligenceBonus(Player.skills.intelligence, 2), + ); + return helpers.netscriptDelay(ctx, 10000).finally(function () { + helpers.log(ctx, () => "Finished sharing this computer."); + end(); + }); + }, + getSharePower: () => (): number => { + return CalculateShareMult(); + }, + print: + (ctx: NetscriptContext) => + (...args: unknown[]): void => { + if (args.length === 0) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes at least 1 argument."); + } + ctx.workerScript.print(argsToString(args)); + }, + printf: + (ctx: NetscriptContext) => + (_format: unknown, ...args: unknown[]): void => { + const format = helpers.string(ctx, "format", _format); + if (typeof format !== "string") { + throw helpers.makeRuntimeErrorMsg(ctx, "First argument must be string for the format."); + } + ctx.workerScript.print(vsprintf(format, args)); + }, + tprint: + (ctx: NetscriptContext) => + (...args: unknown[]): void => { + if (args.length === 0) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes at least 1 argument."); + } + const str = argsToString(args); + if (str.startsWith("ERROR") || str.startsWith("FAIL")) { + Terminal.error(`${ctx.workerScript.scriptRef.filename}: ${str}`); + return; + } + if (str.startsWith("SUCCESS")) { + Terminal.success(`${ctx.workerScript.scriptRef.filename}: ${str}`); + return; + } + if (str.startsWith("WARN")) { + Terminal.warn(`${ctx.workerScript.scriptRef.filename}: ${str}`); + return; + } + if (str.startsWith("INFO")) { + Terminal.info(`${ctx.workerScript.scriptRef.filename}: ${str}`); + return; + } + Terminal.print(`${ctx.workerScript.scriptRef.filename}: ${str}`); + }, + tprintf: + (ctx: NetscriptContext) => + (_format: unknown, ...args: unknown[]): void => { + const format = helpers.string(ctx, "format", _format); + const str = vsprintf(format, args); + + if (str.startsWith("ERROR") || str.startsWith("FAIL")) { + Terminal.error(`${str}`); + return; + } + if (str.startsWith("SUCCESS")) { + Terminal.success(`${str}`); + return; + } + if (str.startsWith("WARN")) { + Terminal.warn(`${str}`); + return; + } + if (str.startsWith("INFO")) { + Terminal.info(`${str}`); + return; + } + Terminal.print(`${str}`); + }, + clearLog: (ctx: NetscriptContext) => (): void => { + ctx.workerScript.scriptRef.clearLog(); + }, + disableLog: + (ctx: NetscriptContext) => + (_fn: unknown): void => { + const fn = helpers.string(ctx, "fn", _fn); + if (fn === "ALL") { + for (const fn of Object.keys(possibleLogs)) { + ctx.workerScript.disableLogs[fn] = true; + } + helpers.log(ctx, () => `Disabled logging for all functions`); + } else if (possibleLogs[fn] === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${fn}.`); } else { - helpers.log(ctx, () => "Failed. This is a bug. Report to dev."); + ctx.workerScript.disableLogs[fn] = true; + helpers.log(ctx, () => `Disabled logging for ${fn}`); } }, - scp: - (ctx: NetscriptContext) => - async ( - _scriptname: unknown, - _destination: unknown, - _source: unknown = workerScript.hostname, - ): Promise => { - const destination = helpers.string(ctx, "destination", _destination); - const source = helpers.string(ctx, "source", _source); - if (Array.isArray(_scriptname)) { - // Recursively call scp on all elements of array - const scripts: string[] = _scriptname; - if (scripts.length === 0) { - throw helpers.makeRuntimeErrorMsg(ctx, "No scripts to copy"); - } - let res = true; - await Promise.all( - scripts.map(async function (script) { - if (!(await NetscriptFunctions(workerScript).scp(script, destination, source))) { - res = false; - } - }), - ); - return Promise.resolve(res); + enableLog: + (ctx: NetscriptContext) => + (_fn: unknown): void => { + const fn = helpers.string(ctx, "fn", _fn); + if (fn === "ALL") { + for (const fn of Object.keys(possibleLogs)) { + delete ctx.workerScript.disableLogs[fn]; } - - const scriptName = helpers.string(ctx, "scriptName", _scriptname); - - // Invalid file type - if (!isValidFilePath(scriptName)) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${scriptName}'`); - } - - // Invalid file name - if (!scriptName.endsWith(".lit") && !isScriptFilename(scriptName) && !scriptName.endsWith("txt")) { - throw helpers.makeRuntimeErrorMsg(ctx, "Only works for scripts, .lit and .txt files"); - } - - const destServer = helpers.getServer(ctx, destination); - const currServ = helpers.getServer(ctx, source); - - // Scp for lit files - if (scriptName.endsWith(".lit")) { - let found = false; - for (let i = 0; i < currServ.messages.length; ++i) { - if (currServ.messages[i] == scriptName) { - found = true; - break; - } - } - - if (!found) { - helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return Promise.resolve(false); - } - - for (let i = 0; i < destServer.messages.length; ++i) { - if (destServer.messages[i] === scriptName) { - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); // Already exists - } - } - destServer.messages.push(scriptName); - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); - } - - // Scp for text files - if (scriptName.endsWith(".txt")) { - let txtFile; - for (let i = 0; i < currServ.textFiles.length; ++i) { - if (currServ.textFiles[i].fn === scriptName) { - txtFile = currServ.textFiles[i]; - break; - } - } - if (txtFile === undefined) { - helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return Promise.resolve(false); - } - - for (let i = 0; i < destServer.textFiles.length; ++i) { - if (destServer.textFiles[i].fn === scriptName) { - // Overwrite - destServer.textFiles[i].text = txtFile.text; - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); - } - } - const newFile = new TextFile(txtFile.fn, txtFile.text); - destServer.textFiles.push(newFile); - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); - } - - // Scp for script files - let sourceScript = null; - for (let i = 0; i < currServ.scripts.length; ++i) { - if (scriptName == currServ.scripts[i].filename) { - sourceScript = currServ.scripts[i]; - break; - } - } - if (sourceScript == null) { - helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return Promise.resolve(false); - } - - // Overwrite script if it already exists - for (let i = 0; i < destServer.scripts.length; ++i) { - if (scriptName == destServer.scripts[i].filename) { - helpers.log(ctx, () => `WARNING: File '${scriptName}' overwritten on '${destServer?.hostname}'`); - const oldScript = destServer.scripts[i]; - // If it's the exact same file don't actually perform the - // copy to avoid recompiling uselessly. Players tend to scp - // liberally. - if (oldScript.code === sourceScript.code) return Promise.resolve(true); - oldScript.code = sourceScript.code; - oldScript.ramUsage = sourceScript.ramUsage; - oldScript.markUpdated(); - return Promise.resolve(true); - } - } - - // Create new script if it does not already exist - const newScript = new Script(Player, scriptName); - newScript.code = sourceScript.code; - newScript.ramUsage = sourceScript.ramUsage; - newScript.server = destServer.hostname; - destServer.scripts.push(newScript); - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return new Promise((resolve) => { - if (destServer === null) { - resolve(false); - return; - } - newScript.updateRamUsage(Player, destServer.scripts).then(() => resolve(true)); - }); - }, - ls: - (ctx: NetscriptContext) => - (_hostname: unknown, _grep: unknown = ""): string[] => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const grep = helpers.string(ctx, "grep", _grep); - - const server = helpers.getServer(ctx, hostname); - - // Get the grep filter, if one exists - let filter = ""; - if (_grep !== undefined) { - filter = grep.toString(); - } - - const allFiles = []; - for (let i = 0; i < server.programs.length; i++) { - if (filter) { - if (server.programs[i].includes(filter)) { - allFiles.push(server.programs[i]); - } - } else { - allFiles.push(server.programs[i]); - } - } - for (let i = 0; i < server.scripts.length; i++) { - if (filter) { - if (server.scripts[i].filename.includes(filter)) { - allFiles.push(server.scripts[i].filename); - } - } else { - allFiles.push(server.scripts[i].filename); - } - } - for (let i = 0; i < server.messages.length; i++) { - if (filter) { - const msg = server.messages[i]; - if (msg.includes(filter)) { - allFiles.push(msg); - } - } else { - allFiles.push(server.messages[i]); - } - } - - for (let i = 0; i < server.textFiles.length; i++) { - if (filter) { - if (server.textFiles[i].fn.includes(filter)) { - allFiles.push(server.textFiles[i].fn); - } - } else { - allFiles.push(server.textFiles[i].fn); - } - } - - for (let i = 0; i < server.contracts.length; ++i) { - if (filter) { - if (server.contracts[i].fn.includes(filter)) { - allFiles.push(server.contracts[i].fn); - } - } else { - allFiles.push(server.contracts[i].fn); - } - } - - // Sort the files alphabetically then print each - allFiles.sort(); - return allFiles; - }, - getRecentScripts: () => (): IRecentScript[] => { - return recentScripts.map((rs) => ({ - timeOfDeath: rs.timeOfDeath, - ...createPublicRunningScript(rs.runningScript), - })); + helpers.log(ctx, () => `Enabled logging for all functions`); + } else if (possibleLogs[fn] === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${fn}.`); + } + delete ctx.workerScript.disableLogs[fn]; + helpers.log(ctx, () => `Enabled logging for ${fn}`); }, - ps: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname): ProcessInfo[] => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - const processes = []; - for (const script of server.runningScripts) { - processes.push({ - filename: script.filename, - threads: script.threads, - args: script.args.slice(), - pid: script.pid, - }); - } - return processes; - }, - hasRootAccess: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); + isLogEnabled: + (ctx: NetscriptContext) => + (_fn: unknown): boolean => { + const fn = helpers.string(ctx, "fn", _fn); + if (possibleLogs[fn] === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${fn}.`); + } + return !ctx.workerScript.disableLogs[fn]; + }, + getScriptLogs: + (ctx: NetscriptContext) => + (scriptID: unknown, hostname: unknown, ...scriptArgs: unknown[]): string[] => { + const ident = helpers.scriptIdentifier(ctx, scriptID, hostname, scriptArgs); + const runningScriptObj = getRunningScript(ctx, ident); + if (runningScriptObj == null) { + helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); + return []; + } - const server = helpers.getServer(ctx, hostname); - return server.hasAdminRights; - }, - getHostname: (ctx: NetscriptContext) => (): string => { - const scriptServer = GetServer(workerScript.hostname); + return runningScriptObj.logs.slice(); + }, + tail: + (ctx: NetscriptContext) => + (scriptID: unknown, hostname: unknown, ...scriptArgs: unknown[]): void => { + const ident = helpers.scriptIdentifier(ctx, scriptID, hostname, scriptArgs); + const runningScriptObj = getRunningScript(ctx, ident); + if (runningScriptObj == null) { + helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); + return; + } + + LogBoxEvents.emit(runningScriptObj); + }, + + closeTail: + (ctx: NetscriptContext) => + (_pid: unknown = ctx.workerScript.scriptRef.pid): void => { + const pid = helpers.number(ctx, "pid", _pid); + //Emit an event to tell the game to close the tail window if it exists + LogBoxCloserEvents.emit(pid); + }, + nuke: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return false; + } + if (server.hasAdminRights) { + helpers.log(ctx, () => `Already have root access to '${server.hostname}'.`); + return true; + } + if (!Player.hasProgram(Programs.NukeProgram.name)) { + throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the NUKE.exe virus!"); + } + if (server.openPortCount < server.numOpenPortsRequired) { + throw helpers.makeRuntimeErrorMsg(ctx, "Not enough ports opened to use NUKE.exe virus."); + } + server.hasAdminRights = true; + helpers.log(ctx, () => `Executed NUKE.exe virus on '${server.hostname}' to gain root access.`); + return true; + }, + brutessh: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return false; + } + if (!Player.hasProgram(Programs.BruteSSHProgram.name)) { + throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the BruteSSH.exe program!"); + } + if (!server.sshPortOpen) { + helpers.log(ctx, () => `Executed BruteSSH.exe on '${server.hostname}' to open SSH port (22).`); + server.sshPortOpen = true; + ++server.openPortCount; + } else { + helpers.log(ctx, () => `SSH Port (22) already opened on '${server.hostname}'.`); + } + return true; + }, + ftpcrack: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + if (hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); + } + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return false; + } + if (!Player.hasProgram(Programs.FTPCrackProgram.name)) { + throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the FTPCrack.exe program!"); + } + if (!server.ftpPortOpen) { + helpers.log(ctx, () => `Executed FTPCrack.exe on '${server.hostname}' to open FTP port (21).`); + server.ftpPortOpen = true; + ++server.openPortCount; + } else { + helpers.log(ctx, () => `FTP Port (21) already opened on '${server.hostname}'.`); + } + return true; + }, + relaysmtp: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + if (hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); + } + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return false; + } + if (!Player.hasProgram(Programs.RelaySMTPProgram.name)) { + throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the relaySMTP.exe program!"); + } + if (!server.smtpPortOpen) { + helpers.log(ctx, () => `Executed relaySMTP.exe on '${server.hostname}' to open SMTP port (25).`); + server.smtpPortOpen = true; + ++server.openPortCount; + } else { + helpers.log(ctx, () => `SMTP Port (25) already opened on '${server.hostname}'.`); + } + return true; + }, + httpworm: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + if (hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument"); + } + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return false; + } + if (!Player.hasProgram(Programs.HTTPWormProgram.name)) { + throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the HTTPWorm.exe program!"); + } + if (!server.httpPortOpen) { + helpers.log(ctx, () => `Executed HTTPWorm.exe on '${server.hostname}' to open HTTP port (80).`); + server.httpPortOpen = true; + ++server.openPortCount; + } else { + helpers.log(ctx, () => `HTTP Port (80) already opened on '${server.hostname}'.`); + } + return true; + }, + sqlinject: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + if (hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Takes 1 argument."); + } + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return false; + } + if (!Player.hasProgram(Programs.SQLInjectProgram.name)) { + throw helpers.makeRuntimeErrorMsg(ctx, "You do not have the SQLInject.exe program!"); + } + if (!server.sqlPortOpen) { + helpers.log(ctx, () => `Executed SQLInject.exe on '${server.hostname}' to open SQL port (1433).`); + server.sqlPortOpen = true; + ++server.openPortCount; + } else { + helpers.log(ctx, () => `SQL Port (1433) already opened on '${server.hostname}'.`); + } + return true; + }, + run: + (ctx: NetscriptContext) => + (_scriptname: unknown, _threads: unknown = 1, ..._args: unknown[]): number => { + const scriptname = helpers.string(ctx, "scriptname", _scriptname); + const threads = helpers.number(ctx, "threads", _threads); + const args = helpers.scriptArgs(ctx, _args); + if (scriptname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Usage: run(scriptname, [numThreads], [arg1], [arg2]...)"); + } + if (isNaN(threads) || threads <= 0) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`); + } + const scriptServer = GetServer(ctx.workerScript.hostname); if (scriptServer == null) { throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev."); } - return scriptServer.hostname; + + return runScriptFromScript(Player, "run", scriptServer, scriptname, args, ctx.workerScript, threads); }, - getHackingLevel: (ctx: NetscriptContext) => (): number => { - Player.updateSkillLevels(); - helpers.log(ctx, () => `returned ${Player.skills.hacking}`); - return Player.skills.hacking; - }, - getHackingMultipliers: () => (): HackingMultipliers => { - return { - chance: Player.mults.hacking_chance, - speed: Player.mults.hacking_speed, - money: Player.mults.hacking_money, - growth: Player.mults.hacking_grow, - }; - }, - getHacknetMultipliers: () => (): HacknetMultipliers => { - return { - production: Player.mults.hacknet_node_money, - purchaseCost: Player.mults.hacknet_node_purchase_cost, - ramCost: Player.mults.hacknet_node_ram_cost, - coreCost: Player.mults.hacknet_node_core_cost, - levelCost: Player.mults.hacknet_node_level_cost, - }; - }, - getBitNodeMultipliers: (ctx: NetscriptContext) => (): IBNMults => { - if (Player.sourceFileLvl(5) <= 0 && Player.bitNodeN !== 5) { - throw helpers.makeRuntimeErrorMsg(ctx, "Requires Source-File 5 to run."); + exec: + (ctx: NetscriptContext) => + (_scriptname: unknown, _hostname: unknown, _threads: unknown = 1, ..._args: unknown[]): number => { + const scriptname = helpers.string(ctx, "scriptname", _scriptname); + const hostname = helpers.string(ctx, "hostname", _hostname); + const threads = helpers.number(ctx, "threads", _threads); + const args = helpers.scriptArgs(ctx, _args); + if (scriptname === undefined || hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Usage: exec(scriptname, server, [numThreads], [arg1], [arg2]...)"); } - const copy = Object.assign({}, BitNodeMultipliers); - return copy; + if (isNaN(threads) || threads <= 0) { + 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); }, - getServer: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname): IServerDef => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - const copy = Object.assign({}, server) as Server; - // These fields should be hidden. - copy.contracts = []; - copy.messages = []; - copy.runningScripts = []; - copy.scripts = []; - copy.textFiles = []; - copy.programs = []; - copy.serversOnNetwork = []; - if (!copy.baseDifficulty) copy.baseDifficulty = 0; - if (!copy.hackDifficulty) copy.hackDifficulty = 0; - if (!copy.minDifficulty) copy.minDifficulty = 0; - if (!copy.moneyAvailable) copy.moneyAvailable = 0; - if (!copy.moneyMax) copy.moneyMax = 0; - if (!copy.numOpenPortsRequired) copy.numOpenPortsRequired = 0; - if (!copy.openPortCount) copy.openPortCount = 0; - if (!copy.requiredHackingSkill) copy.requiredHackingSkill = 0; - if (!copy.serverGrowth) copy.serverGrowth = 0; - return copy; - }, - getServerMoneyAvailable: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 0; - } - if (failOnHacknetServer(server, "getServerMoneyAvailable")) { - return 0; - } - if (server.hostname == "home") { - // Return player's money - helpers.log(ctx, () => `returned player's money: ${numeralWrapper.formatMoney(Player.money)}`); - return Player.money; - } - helpers.log( - ctx, - () => `returned ${numeralWrapper.formatMoney(server.moneyAvailable)} for '${server.hostname}'`, - ); - return server.moneyAvailable; - }, - getServerSecurityLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerSecurityLevel")) { - return 1; - } - helpers.log( - ctx, - () => `returned ${numeralWrapper.formatServerSecurity(server.hackDifficulty)} for '${server.hostname}'`, - ); - return server.hackDifficulty; - }, - getServerBaseSecurityLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - helpers.log(ctx, () => `getServerBaseSecurityLevel is deprecated because it's not useful.`); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerBaseSecurityLevel")) { - return 1; - } - helpers.log( - ctx, - () => `returned ${numeralWrapper.formatServerSecurity(server.baseDifficulty)} for '${server.hostname}'`, - ); - return server.baseDifficulty; - }, - getServerMinSecurityLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerMinSecurityLevel")) { - return 1; - } - helpers.log( - ctx, - () => `returned ${numeralWrapper.formatServerSecurity(server.minDifficulty)} for ${server.hostname}`, - ); - return server.minDifficulty; - }, - getServerRequiredHackingLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerRequiredHackingLevel")) { - return 1; - } - helpers.log( - ctx, - () => `returned ${numeralWrapper.formatSkill(server.requiredHackingSkill)} for '${server.hostname}'`, - ); - return server.requiredHackingSkill; - }, - getServerMaxMoney: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 0; - } - if (failOnHacknetServer(server, "getServerMaxMoney")) { - return 0; - } - helpers.log(ctx, () => `returned ${numeralWrapper.formatMoney(server.moneyMax)} for '${server.hostname}'`); - return server.moneyMax; - }, - getServerGrowth: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerGrowth")) { - return 1; - } - helpers.log(ctx, () => `returned ${server.serverGrowth} for '${server.hostname}'`); - return server.serverGrowth; - }, - getServerNumPortsRequired: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "Cannot be executed on this server."); - return 5; - } - if (failOnHacknetServer(server, "getServerNumPortsRequired")) { - return 5; - } - helpers.log(ctx, () => `returned ${server.numOpenPortsRequired} for '${server.hostname}'`); - return server.numOpenPortsRequired; - }, - getServerRam: - (ctx: NetscriptContext) => - (_hostname: unknown): [number, number] => { - const hostname = helpers.string(ctx, "hostname", _hostname); - helpers.log(ctx, () => `getServerRam is deprecated in favor of getServerMaxRam / getServerUsedRam`); - const server = helpers.getServer(ctx, hostname); - helpers.log( - ctx, - () => `returned [${numeralWrapper.formatRAM(server.maxRam)}, ${numeralWrapper.formatRAM(server.ramUsed)}]`, - ); - return [server.maxRam, server.ramUsed]; - }, - getServerMaxRam: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - helpers.log(ctx, () => `returned ${numeralWrapper.formatRAM(server.maxRam)}`); - return server.maxRam; - }, - getServerUsedRam: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - helpers.log(ctx, () => `returned ${numeralWrapper.formatRAM(server.ramUsed)}`); - return server.ramUsed; - }, - serverExists: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = helpers.string(ctx, "hostname", _hostname); - return GetServer(hostname) !== null; - }, - fileExists: - (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): boolean => { - const filename = helpers.string(ctx, "filename", _filename); - const hostname = helpers.string(ctx, "hostname", _hostname); - if (filename === undefined) { - throw helpers.makeRuntimeErrorMsg(ctx, "Usage: fileExists(scriptname, [server])"); - } - const server = helpers.getServer(ctx, hostname); - for (let i = 0; i < server.scripts.length; ++i) { - if (filename == server.scripts[i].filename) { - return true; - } - } - for (let i = 0; i < server.programs.length; ++i) { - if (filename.toLowerCase() == server.programs[i].toLowerCase()) { - return true; - } - } - for (let i = 0; i < server.messages.length; ++i) { - if (filename.toLowerCase() === server.messages[i].toLowerCase()) { - return true; - } - } - const txtFile = getTextFile(filename, server); - return txtFile != null; - }, - isRunning: - (ctx: NetscriptContext) => - (fn: unknown, hostname: unknown, ...scriptArgs: unknown[]): boolean => { - const ident = helpers.scriptIdentifier(ctx, fn, hostname, scriptArgs); - return getRunningScript(ctx, ident) !== null; - }, - getPurchasedServerLimit: () => (): number => { - return getPurchaseServerLimit(); - }, - getPurchasedServerMaxRam: () => (): number => { - return getPurchaseServerMaxRam(); - }, - getPurchasedServerCost: - (ctx: NetscriptContext) => - (_ram: unknown): number => { - const ram = helpers.number(ctx, "ram", _ram); + spawn: + (ctx: NetscriptContext) => + (_scriptname: unknown, _threads: unknown = 1, ..._args: unknown[]): void => { + const scriptname = helpers.string(ctx, "scriptname", _scriptname); + const threads = helpers.number(ctx, "threads", _threads); + const args = helpers.scriptArgs(ctx, _args); + if (!scriptname || !threads) { + throw helpers.makeRuntimeErrorMsg(ctx, "Usage: spawn(scriptname, threads)"); + } - const cost = getPurchaseServerCost(ram); - if (cost === Infinity) { - helpers.log(ctx, () => `Invalid argument: ram='${ram}'`); - return Infinity; + const spawnDelay = 10; + setTimeout(() => { + if (isNaN(threads) || threads <= 0) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid thread count. Must be numeric and > 0, is ${threads}`); + } + const scriptServer = GetServer(ctx.workerScript.hostname); + if (scriptServer == null) { + throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev"); } - return cost; - }, - purchaseServer: - (ctx: NetscriptContext) => - (_name: unknown, _ram: unknown): string => { - const name = helpers.string(ctx, "name", _name); - const ram = helpers.number(ctx, "ram", _ram); - let hostnameStr = String(name); - hostnameStr = hostnameStr.replace(/\s+/g, ""); - if (hostnameStr == "") { - helpers.log(ctx, () => `Invalid argument: hostname='${hostnameStr}'`); - return ""; + return runScriptFromScript(Player, "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..."); + } + }, + kill: + (ctx: NetscriptContext) => + (scriptID: unknown, hostname: unknown, ...scriptArgs: unknown[]): boolean => { + const ident = helpers.scriptIdentifier(ctx, scriptID, hostname, scriptArgs); + let res; + const killByPid = typeof ident === "number"; + if (killByPid) { + // Kill by pid + res = killWorkerScript(ident); + } else { + // Kill by filename/hostname + if (scriptID === undefined || hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Usage: kill(scriptname, server, [arg1], [arg2]...)"); } - if (Player.purchasedServers.length >= getPurchaseServerLimit()) { + const server = helpers.getServer(ctx, ident.hostname); + const runningScriptObj = getRunningScriptByArgs(ctx, ident.scriptname, ident.hostname, ident.args); + if (runningScriptObj == null) { + helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); + return false; + } + + res = killWorkerScript({ runningScript: runningScriptObj, hostname: server.hostname }); + } + + if (res) { + if (killByPid) { + helpers.log(ctx, () => `Killing script with PID ${ident}`); + } else { + helpers.log(ctx, () => `Killing '${scriptID}' on '${hostname}' with args: ${arrayToString(scriptArgs)}.`); + } + return true; + } else { + if (killByPid) { + helpers.log(ctx, () => `No script with PID ${ident}`); + } else { helpers.log( ctx, - () => - `You have reached the maximum limit of ${getPurchaseServerLimit()} servers. You cannot purchase any more.`, + () => `No such script '${scriptID}' on '${hostname}' with args: ${arrayToString(scriptArgs)}`, ); - return ""; } + return false; + } + }, + killall: + (ctx: NetscriptContext) => + (_hostname: unknown = ctx.workerScript.hostname, _safetyguard: unknown = true): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const safetyguard = !!_safetyguard; + if (hostname === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Usage: killall(hostname, [safetyguard boolean])"); + } + const server = helpers.getServer(ctx, hostname); - const cost = getPurchaseServerCost(ram); - if (cost === Infinity) { - if (ram > getPurchaseServerMaxRam()) { - helpers.log(ctx, () => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`); - } else { - helpers.log(ctx, () => `Invalid argument: ram='${ram}' must be a positive power of 2`); - } + let scriptsKilled = 0; - return ""; + for (let i = server.runningScripts.length - 1; i >= 0; --i) { + if (safetyguard === true && server.runningScripts[i].pid == ctx.workerScript.pid) continue; + killWorkerScript({ runningScript: server.runningScripts[i], hostname: server.hostname }); + ++scriptsKilled; + } + WorkerScriptStartStopEventEmitter.emit(); + helpers.log( + ctx, + () => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`, + ); + + 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 { + helpers.log(ctx, () => "Failed. This is a bug. Report to dev."); + } + }, + scp: + (ctx: NetscriptContext) => + async ( + _scriptname: unknown, + _destination: unknown, + _source: unknown = ctx.workerScript.hostname, + ): Promise => { + const destination = helpers.string(ctx, "destination", _destination); + const source = helpers.string(ctx, "source", _source); + if (Array.isArray(_scriptname)) { + // Recursively call scp on all elements of array + const scripts: string[] = _scriptname; + if (scripts.length === 0) { + throw helpers.makeRuntimeErrorMsg(ctx, "No scripts to copy"); } - - if (Player.money < cost) { - helpers.log(ctx, () => `Not enough money to purchase server. Need ${numeralWrapper.formatMoney(cost)}`); - return ""; - } - const newServ = safetlyCreateUniqueServer({ - ip: createUniqueRandomIp(), - hostname: hostnameStr, - organizationName: "", - isConnectedTo: false, - adminRights: true, - purchasedByPlayer: true, - maxRam: ram, - }); - AddToAllServers(newServ); - - Player.purchasedServers.push(newServ.hostname); - const homeComputer = Player.getHomeComputer(); - homeComputer.serversOnNetwork.push(newServ.hostname); - newServ.serversOnNetwork.push(homeComputer.hostname); - Player.loseMoney(cost, "servers"); - helpers.log( - ctx, - () => `Purchased new server with hostname '${newServ.hostname}' for ${numeralWrapper.formatMoney(cost)}`, + let res = true; + await Promise.all( + scripts.map(async function (script) { + if (!(await NetscriptFunctions(ctx.workerScript).scp(script, destination, source))) { + res = false; + } + }), ); - return newServ.hostname; - }, - deleteServer: - (ctx: NetscriptContext) => - (_name: unknown): boolean => { - const name = helpers.string(ctx, "name", _name); - let hostnameStr = String(name); - hostnameStr = hostnameStr.replace(/\s\s+/g, ""); - const server = GetServer(hostnameStr); - if (!(server instanceof Server)) { - helpers.log(ctx, () => `Invalid argument: hostname='${hostnameStr}'`); - return false; - } + return Promise.resolve(res); + } - if (!server.purchasedByPlayer || server.hostname === "home") { - helpers.log(ctx, () => "Cannot delete non-purchased server."); - return false; - } + const scriptName = helpers.string(ctx, "scriptName", _scriptname); - const hostname = server.hostname; + // Invalid file type + if (!isValidFilePath(scriptName)) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${scriptName}'`); + } - // Can't delete server you're currently connected to - if (server.isConnectedTo) { - helpers.log(ctx, () => "You are currently connected to the server you are trying to delete."); - return false; - } + // Invalid file name + if (!scriptName.endsWith(".lit") && !isScriptFilename(scriptName) && !scriptName.endsWith("txt")) { + throw helpers.makeRuntimeErrorMsg(ctx, "Only works for scripts, .lit and .txt files"); + } - // A server cannot delete itself - if (hostname === workerScript.hostname) { - helpers.log(ctx, () => "Cannot delete the server this script is running on."); - return false; - } + const destServer = helpers.getServer(ctx, destination); + const currServ = helpers.getServer(ctx, source); - // Delete all scripts running on server - if (server.runningScripts.length > 0) { - helpers.log(ctx, () => `Cannot delete server '${hostname}' because it still has scripts running.`); - return false; - } - - // Delete from player's purchasedServers array + // Scp for lit files + if (scriptName.endsWith(".lit")) { let found = false; - for (let i = 0; i < Player.purchasedServers.length; ++i) { - if (hostname == Player.purchasedServers[i]) { + for (let i = 0; i < currServ.messages.length; ++i) { + if (currServ.messages[i] == scriptName) { found = true; - Player.purchasedServers.splice(i, 1); break; } } if (!found) { - helpers.log( - ctx, - () => `Could not identify server ${hostname} as a purchased server. This is a bug. Report to dev.`, - ); - return false; - } - - // Delete from all servers - DeleteServer(hostname); - - // Delete from home computer - found = false; - const homeComputer = Player.getHomeComputer(); - for (let i = 0; i < homeComputer.serversOnNetwork.length; ++i) { - if (hostname == homeComputer.serversOnNetwork[i]) { - homeComputer.serversOnNetwork.splice(i, 1); - helpers.log(ctx, () => `Deleted server '${hostnameStr}`); - return true; - } - } - // Wasn't found on home computer - helpers.log( - ctx, - () => `Could not find server ${hostname} as a purchased server. This is a bug. Report to dev.`, - ); - return false; - }, - getPurchasedServers: () => (): string[] => { - const res: string[] = []; - Player.purchasedServers.forEach(function (hostname) { - res.push(hostname); - }); - return res; - }, - writePort: - (ctx: NetscriptContext) => - (_port: unknown, data: unknown = ""): Promise => { - const port = helpers.number(ctx, "port", _port); - if (typeof data !== "string" && typeof data !== "number") { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Trying to write invalid data to a port: only strings and numbers are valid.`, - ); - } - const iport = helpers.getValidPort(ctx, port); - return Promise.resolve(iport.write(data)); - }, - write: - (ctx: NetscriptContext) => - (_port: unknown, data: unknown = "", _mode: unknown = "a"): Promise => { - const port = helpers.string(ctx, "port", _port); - const mode = helpers.string(ctx, "mode", _mode); - if (isString(port)) { - // Write to script or text file - let fn = port; - if (!isValidFilePath(fn)) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${fn}`); - } - - if (fn.lastIndexOf("/") === 0) { - fn = removeLeadingSlash(fn); - } - - // Coerce 'data' to be a string - try { - data = String(data); - } catch (e: unknown) { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Invalid data (${String(e)}). Data being written must be convertible to a string`, - ); - } - - const server = workerScript.getServer(); - if (server == null) { - throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev."); - } - if (isScriptFilename(fn)) { - // Write to script - let script = workerScript.getScriptOnServer(fn, server); - if (script == null) { - // Create a new script - script = new Script(Player, fn, String(data), server.hostname, server.scripts); - server.scripts.push(script); - return script.updateRamUsage(Player, server.scripts); - } - mode === "w" ? (script.code = String(data)) : (script.code += data); - return script.updateRamUsage(Player, server.scripts); - } else { - // Write to text file - const txtFile = getTextFile(fn, server); - if (txtFile == null) { - createTextFile(fn, String(data), server); - return Promise.resolve(); - } - if (mode === "w") { - txtFile.write(String(data)); - } else { - txtFile.append(String(data)); - } - } - return Promise.resolve(); - } else { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`); - } - }, - tryWritePort: - (ctx: NetscriptContext) => - (_port: unknown, data: unknown = ""): Promise => { - let port = helpers.number(ctx, "port", _port); - if (typeof data !== "string" && typeof data !== "number") { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Trying to write invalid data to a port: only strings and numbers are valid.`, - ); - } - if (!isNaN(port)) { - port = Math.round(port); - if (port < 1 || port > CONSTANTS.NumNetscriptPorts) { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Invalid port: ${port}. Only ports 1-${CONSTANTS.NumNetscriptPorts} are valid.`, - ); - } - const iport = NetscriptPorts[port - 1]; - if (iport == null || !(iport instanceof Object)) { - throw helpers.makeRuntimeErrorMsg(ctx, `Could not find port: ${port}. This is a bug. Report to dev.`); - } - return Promise.resolve(iport.tryWrite(data)); - } else { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`); - } - }, - readPort: - (ctx: NetscriptContext) => - (_port: unknown): unknown => { - const port = helpers.number(ctx, "port", _port); - // Read from port - const iport = helpers.getValidPort(ctx, port); - const x = iport.read(); - return x; - }, - read: - (ctx: NetscriptContext) => - (_port: unknown): string => { - const port = helpers.string(ctx, "port", _port); - if (isString(port)) { - // Read from script or text file - const fn = port; - const server = GetServer(workerScript.hostname); - if (server == null) { - throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev."); - } - if (isScriptFilename(fn)) { - // Read from script - const script = workerScript.getScriptOnServer(fn, server); - if (script == null) { - return ""; - } - return script.code; - } else { - // Read from text file - const txtFile = getTextFile(fn, server); - if (txtFile !== null) { - return txtFile.text; - } else { - return ""; - } - } - } else { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`); - } - }, - peek: - (ctx: NetscriptContext) => - (_port: unknown): unknown => { - const port = helpers.number(ctx, "port", _port); - const iport = helpers.getValidPort(ctx, port); - const x = iport.peek(); - return x; - }, - clear: - (ctx: NetscriptContext) => - (_file: unknown): void => { - const file = helpers.string(ctx, "file", _file); - if (isString(file)) { - // Clear text file - const fn = file; - const server = GetServer(workerScript.hostname); - if (server == null) { - throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev."); - } - const txtFile = getTextFile(fn, server); - if (txtFile != null) { - txtFile.write(""); - } - } else { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${file}`); - } - }, - clearPort: - (ctx: NetscriptContext) => - (_port: unknown): void => { - const port = helpers.number(ctx, "port", _port); - // Clear port - const iport = helpers.getValidPort(ctx, port); - iport.clear(); - }, - getPortHandle: - (ctx: NetscriptContext) => - (_port: unknown): IPort => { - const port = helpers.number(ctx, "port", _port); - const iport = helpers.getValidPort(ctx, port); - return iport; - }, - rm: - (ctx: NetscriptContext) => - (_fn: unknown, _hostname: unknown = workerScript.hostname): boolean => { - const fn = helpers.string(ctx, "fn", _fn); - const hostname = helpers.string(ctx, "hostname", _hostname); - const s = helpers.getServer(ctx, hostname); - - const status = s.removeFile(fn); - if (!status.res) { - helpers.log(ctx, () => status.msg + ""); - } - - return status.res; - }, - scriptRunning: - (ctx: NetscriptContext) => - (_scriptname: unknown, _hostname: unknown): boolean => { - const scriptname = helpers.string(ctx, "scriptname", _scriptname); - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - for (let i = 0; i < server.runningScripts.length; ++i) { - if (server.runningScripts[i].filename == scriptname) { - return true; - } - } - return false; - }, - scriptKill: - (ctx: NetscriptContext) => - (_scriptname: unknown, _hostname: unknown): boolean => { - const scriptname = helpers.string(ctx, "scriptname", _scriptname); - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - let suc = false; - for (let i = 0; i < server.runningScripts.length; i++) { - if (server.runningScripts[i].filename == scriptname) { - killWorkerScript({ runningScript: server.runningScripts[i], hostname: server.hostname }); - suc = true; - i--; - } - } - return suc; - }, - getScriptName: () => (): string => { - return workerScript.name; - }, - getScriptRam: - (ctx: NetscriptContext) => - (_scriptname: unknown, _hostname: unknown = workerScript.hostname): number => { - const scriptname = helpers.string(ctx, "scriptname", _scriptname); - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename == scriptname) { - return server.scripts[i].ramUsage; - } - } - return 0; - }, - getRunningScript: - (ctx: NetscriptContext) => - (fn: unknown, hostname: unknown, ...args: unknown[]): IRunningScriptDef | null => { - const ident = helpers.scriptIdentifier(ctx, fn, hostname, args); - const runningScript = getRunningScript(ctx, ident); - if (runningScript === null) return null; - return createPublicRunningScript(runningScript); - }, - getHackTime: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "invalid for this kind of server"); - return Infinity; - } - if (failOnHacknetServer(server, "getHackTime")) { - return Infinity; - } - - return calculateHackingTime(server, Player) * 1000; - }, - getGrowTime: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "invalid for this kind of server"); - return Infinity; - } - if (failOnHacknetServer(server, "getGrowTime")) { - return Infinity; - } - - return calculateGrowTime(server, Player) * 1000; - }, - getWeakenTime: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname): number => { - const hostname = helpers.string(ctx, "hostname", _hostname); - const server = helpers.getServer(ctx, hostname); - if (!(server instanceof Server)) { - helpers.log(ctx, () => "invalid for this kind of server"); - return Infinity; - } - if (failOnHacknetServer(server, "getWeakenTime")) { - return Infinity; - } - - return calculateWeakenTime(server, Player) * 1000; - }, - getTotalScriptIncome: () => (): [number, number] => { - // First element is total income of all currently running scripts - let total = 0; - for (const script of workerScripts.values()) { - total += script.scriptRef.onlineMoneyMade / script.scriptRef.onlineRunningTime; - } - - return [total, Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug / 1000)]; - }, - getScriptIncome: - (ctx: NetscriptContext) => - (fn: unknown, hostname: unknown, ...args: unknown[]): number => { - const ident = helpers.scriptIdentifier(ctx, fn, hostname, args); - const runningScript = getRunningScript(ctx, ident); - if (runningScript == null) { - helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); - return -1; - } - return runningScript.onlineMoneyMade / runningScript.onlineRunningTime; - }, - getTotalScriptExpGain: () => (): number => { - let total = 0; - for (const ws of workerScripts.values()) { - total += ws.scriptRef.onlineExpGained / ws.scriptRef.onlineRunningTime; - } - return total; - }, - getScriptExpGain: - (ctx: NetscriptContext) => - (fn: unknown, hostname: unknown, ...args: unknown[]): number => { - const ident = helpers.scriptIdentifier(ctx, fn, hostname, args); - const runningScript = getRunningScript(ctx, ident); - if (runningScript == null) { - helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); - return -1; - } - return runningScript.onlineExpGained / runningScript.onlineRunningTime; - }, - nFormat: - (ctx: NetscriptContext) => - (_n: unknown, _format: unknown): string => { - const n = helpers.number(ctx, "n", _n); - const format = helpers.string(ctx, "format", _format); - if (isNaN(n)) { - return ""; - } - - return numeralWrapper.format(n, format); - }, - tFormat: - (ctx: NetscriptContext) => - (_milliseconds: unknown, _milliPrecision: unknown = false): string => { - const milliseconds = helpers.number(ctx, "milliseconds", _milliseconds); - const milliPrecision = !!_milliPrecision; - return convertTimeMsToTimeElapsedString(milliseconds, milliPrecision); - }, - getTimeSinceLastAug: () => (): number => { - return Player.playtimeSinceLastAug; - }, - alert: - (ctx: NetscriptContext) => - (_message: unknown): void => { - const message = helpers.string(ctx, "message", _message); - dialogBoxCreate(message); - }, - toast: - (ctx: NetscriptContext) => - (_message: unknown, _variant: unknown = ToastVariant.SUCCESS, _duration: unknown = 2000): void => { - const message = helpers.string(ctx, "message", _message); - const variant = helpers.string(ctx, "variant", _variant); - const duration = helpers.number(ctx, "duration", _duration); - if (!checkEnum(ToastVariant, variant)) - throw new Error(`variant must be one of ${Object.values(ToastVariant).join(", ")}`); - SnackbarEvents.emit(message, variant, duration); - }, - prompt: - (ctx: NetscriptContext) => - (_txt: unknown, options?: { type?: string; options?: string[] }): Promise => { - const txt = helpers.string(ctx, "txt", _txt); - - return new Promise(function (resolve) { - PromptEvent.emit({ - txt: txt, - options, - resolve: resolve, - }); - }); - }, - wget: - (ctx: NetscriptContext) => - async (_url: unknown, _target: unknown, _hostname: unknown = workerScript.hostname): Promise => { - const url = helpers.string(ctx, "url", _url); - const target = helpers.string(ctx, "target", _target); - const hostname = helpers.string(ctx, "hostname", _hostname); - if (!isScriptFilename(target) && !target.endsWith(".txt")) { - helpers.log(ctx, () => `Invalid target file: '${target}'. Must be a script or text file.`); + helpers.log(ctx, () => `File '${scriptName}' does not exist.`); return Promise.resolve(false); } - const s = helpers.getServer(ctx, hostname); - return new Promise(function (resolve) { - $.get( - url, - function (data) { - let res; - if (isScriptFilename(target)) { - res = s.writeToScriptFile(Player, target, data); - } else { - res = s.writeToTextFile(target, data); - } - if (!res.success) { - helpers.log(ctx, () => "Failed."); - return resolve(false); - } - if (res.overwritten) { - helpers.log(ctx, () => `Successfully retrieved content and overwrote '${target}' on '${hostname}'`); - return resolve(true); - } - helpers.log(ctx, () => `Successfully retrieved content to new file '${target}' on '${hostname}'`); - return resolve(true); - }, - "text", - ).fail(function (e) { - helpers.log(ctx, () => JSON.stringify(e)); - return resolve(false); - }); - }); - }, - getFavorToDonate: () => (): number => { - return Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); - }, - getPlayer: () => (): INetscriptPlayer => { - const data = { - hp: Player.hp, - skills: Player.skills, - exp: Player.exp, - hacking_chance_mult: Player.mults.hacking_chance, - mults: JSON.parse(JSON.stringify(Player.mults)), - numPeopleKilled: Player.numPeopleKilled, - money: Player.money, - city: Player.city, - location: Player.location, - bitNodeN: Player.bitNodeN, - totalPlaytime: Player.totalPlaytime, - playtimeSinceLastAug: Player.playtimeSinceLastAug, - playtimeSinceLastBitnode: Player.playtimeSinceLastBitnode, - jobs: {}, - factions: Player.factions.slice(), - tor: Player.hasTorRouter(), - inBladeburner: Player.inBladeburner(), - hasCorporation: Player.hasCorporation(), - entropy: Player.entropy, - }; - Object.assign(data.jobs, Player.jobs); - return data; - }, - atExit: - (ctx: NetscriptContext) => - (f: unknown): void => { - if (typeof f !== "function") { - throw helpers.makeRuntimeErrorMsg(ctx, "argument should be function"); + + for (let i = 0; i < destServer.messages.length; ++i) { + if (destServer.messages[i] === scriptName) { + helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); + return Promise.resolve(true); // Already exists + } } - workerScript.atExit = () => { - f(); - }; // Wrap the user function to prevent WorkerScript leaking as 'this' - }, - mv: - (ctx: NetscriptContext) => - (_host: unknown, _source: unknown, _destination: unknown): void => { - const host = helpers.string(ctx, "host", _host); - const source = helpers.string(ctx, "source", _source); - const destination = helpers.string(ctx, "destination", _destination); + destServer.messages.push(scriptName); + helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); + return Promise.resolve(true); + } - if (!isValidFilePath(source)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${source}'`); - if (!isValidFilePath(destination)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${destination}'`); + // Scp for text files + if (scriptName.endsWith(".txt")) { + let txtFile; + for (let i = 0; i < currServ.textFiles.length; ++i) { + if (currServ.textFiles[i].fn === scriptName) { + txtFile = currServ.textFiles[i]; + break; + } + } + if (txtFile === undefined) { + helpers.log(ctx, () => `File '${scriptName}' does not exist.`); + return Promise.resolve(false); + } - const source_is_txt = source.endsWith(".txt"); - const dest_is_txt = destination.endsWith(".txt"); + for (let i = 0; i < destServer.textFiles.length; ++i) { + if (destServer.textFiles[i].fn === scriptName) { + // Overwrite + destServer.textFiles[i].text = txtFile.text; + helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); + return Promise.resolve(true); + } + } + const newFile = new TextFile(txtFile.fn, txtFile.text); + destServer.textFiles.push(newFile); + helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); + return Promise.resolve(true); + } - if (!isScriptFilename(source) && !source_is_txt) - throw helpers.makeRuntimeErrorMsg(ctx, `'mv' can only be used on scripts and text files (.txt)`); - if (source_is_txt != dest_is_txt) - throw helpers.makeRuntimeErrorMsg(ctx, `Source and destination files must have the same type`); + // Scp for script files + let sourceScript = null; + for (let i = 0; i < currServ.scripts.length; ++i) { + if (scriptName == currServ.scripts[i].filename) { + sourceScript = currServ.scripts[i]; + break; + } + } + if (sourceScript == null) { + helpers.log(ctx, () => `File '${scriptName}' does not exist.`); + return Promise.resolve(false); + } - if (source === destination) { + // Overwrite script if it already exists + for (let i = 0; i < destServer.scripts.length; ++i) { + if (scriptName == destServer.scripts[i].filename) { + helpers.log(ctx, () => `WARNING: File '${scriptName}' overwritten on '${destServer?.hostname}'`); + const oldScript = destServer.scripts[i]; + // If it's the exact same file don't actually perform the + // copy to avoid recompiling uselessly. Players tend to scp + // liberally. + if (oldScript.code === sourceScript.code) return Promise.resolve(true); + oldScript.code = sourceScript.code; + oldScript.ramUsage = sourceScript.ramUsage; + oldScript.markUpdated(); + return Promise.resolve(true); + } + } + + // Create new script if it does not already exist + const newScript = new Script(Player, scriptName); + newScript.code = sourceScript.code; + newScript.ramUsage = sourceScript.ramUsage; + newScript.server = destServer.hostname; + destServer.scripts.push(newScript); + helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); + return new Promise((resolve) => { + if (destServer === null) { + resolve(false); return; } + newScript.updateRamUsage(Player, destServer.scripts).then(() => resolve(true)); + }); + }, + ls: + (ctx: NetscriptContext) => + (_hostname: unknown, _grep: unknown = ""): string[] => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const grep = helpers.string(ctx, "grep", _grep); - const destServer = helpers.getServer(ctx, host); + const server = helpers.getServer(ctx, hostname); - if (!source_is_txt && destServer.isRunning(source)) - throw helpers.makeRuntimeErrorMsg(ctx, `Cannot use 'mv' on a script that is running`); - - interface File { - filename: string; - } - - const files = source_is_txt ? destServer.textFiles : destServer.scripts; - let source_file: File | null = null; - let dest_file: File | null = null; - - for (let i = 0; i < files.length; ++i) { - const file = files[i]; - if (file.filename === source) { - source_file = file; - } else if (file.filename === destination) { - dest_file = file; - } - } - - if (source_file == null) throw helpers.makeRuntimeErrorMsg(ctx, `Source file ${source} does not exist`); - - if (dest_file != null) { - if (dest_file instanceof TextFile && source_file instanceof TextFile) { - dest_file.text = source_file.text; - } else if (dest_file instanceof Script && source_file instanceof Script) { - dest_file.code = source_file.code; - dest_file.markUpdated(); - } - - destServer.removeFile(source); - } else { - source_file.filename = destination; - if (source_file instanceof Script) { - source_file.markUpdated(); - } - } - }, - flags: Flags(workerScript.args.map((a) => String(a))), - }; - - // add undocumented functions - const ns = { - ...base, - ...NetscriptExtra(Player), - }; - function getFunctionNames(obj: object, prefix: string): string[] { - const functionNames: string[] = []; - for (const [key, value] of Object.entries(obj)) { - if (key === "args") { - continue; - } else if (typeof value == "function") { - functionNames.push(prefix + key); - } else if (typeof value == "object") { - functionNames.push(...getFunctionNames(value, key + ".")); + // Get the grep filter, if one exists + let filter = ""; + if (_grep !== undefined) { + filter = grep.toString(); } + + const allFiles = []; + for (let i = 0; i < server.programs.length; i++) { + if (filter) { + if (server.programs[i].includes(filter)) { + allFiles.push(server.programs[i]); + } + } else { + allFiles.push(server.programs[i]); + } + } + for (let i = 0; i < server.scripts.length; i++) { + if (filter) { + if (server.scripts[i].filename.includes(filter)) { + allFiles.push(server.scripts[i].filename); + } + } else { + allFiles.push(server.scripts[i].filename); + } + } + for (let i = 0; i < server.messages.length; i++) { + if (filter) { + const msg = server.messages[i]; + if (msg.includes(filter)) { + allFiles.push(msg); + } + } else { + allFiles.push(server.messages[i]); + } + } + + for (let i = 0; i < server.textFiles.length; i++) { + if (filter) { + if (server.textFiles[i].fn.includes(filter)) { + allFiles.push(server.textFiles[i].fn); + } + } else { + allFiles.push(server.textFiles[i].fn); + } + } + + for (let i = 0; i < server.contracts.length; ++i) { + if (filter) { + if (server.contracts[i].fn.includes(filter)) { + allFiles.push(server.contracts[i].fn); + } + } else { + allFiles.push(server.contracts[i].fn); + } + } + + // Sort the files alphabetically then print each + allFiles.sort(); + return allFiles; + }, + getRecentScripts: () => (): IRecentScript[] => { + return recentScripts.map((rs) => ({ + timeOfDeath: rs.timeOfDeath, + ...createPublicRunningScript(rs.runningScript), + })); + }, + ps: + (ctx: NetscriptContext) => + (_hostname: unknown = ctx.workerScript.hostname): ProcessInfo[] => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + const processes = []; + for (const script of server.runningScripts) { + processes.push({ + filename: script.filename, + threads: script.threads, + args: script.args.slice(), + pid: script.pid, + }); + } + return processes; + }, + hasRootAccess: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + + const server = helpers.getServer(ctx, hostname); + return server.hasAdminRights; + }, + getHostname: (ctx: NetscriptContext) => (): string => { + const scriptServer = GetServer(ctx.workerScript.hostname); + if (scriptServer == null) { + throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev."); } - return functionNames; - } + return scriptServer.hostname; + }, + getHackingLevel: (ctx: NetscriptContext) => (): number => { + Player.updateSkillLevels(); + helpers.log(ctx, () => `returned ${Player.skills.hacking}`); + return Player.skills.hacking; + }, + getHackingMultipliers: () => (): HackingMultipliers => { + return { + chance: Player.mults.hacking_chance, + speed: Player.mults.hacking_speed, + money: Player.mults.hacking_money, + growth: Player.mults.hacking_grow, + }; + }, + getHacknetMultipliers: () => (): HacknetMultipliers => { + return { + production: Player.mults.hacknet_node_money, + purchaseCost: Player.mults.hacknet_node_purchase_cost, + ramCost: Player.mults.hacknet_node_ram_cost, + coreCost: Player.mults.hacknet_node_core_cost, + levelCost: Player.mults.hacknet_node_level_cost, + }; + }, + getBitNodeMultipliers: (ctx: NetscriptContext) => (): IBNMults => { + if (Player.sourceFileLvl(5) <= 0 && Player.bitNodeN !== 5) { + throw helpers.makeRuntimeErrorMsg(ctx, "Requires Source-File 5 to run."); + } + const copy = Object.assign({}, BitNodeMultipliers); + return copy; + }, + getServer: + (ctx: NetscriptContext) => + (_hostname: unknown = ctx.workerScript.hostname): IServerDef => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + const copy = Object.assign({}, server) as Server; + // These fields should be hidden. + copy.contracts = []; + copy.messages = []; + copy.runningScripts = []; + copy.scripts = []; + copy.textFiles = []; + copy.programs = []; + copy.serversOnNetwork = []; + if (!copy.baseDifficulty) copy.baseDifficulty = 0; + if (!copy.hackDifficulty) copy.hackDifficulty = 0; + if (!copy.minDifficulty) copy.minDifficulty = 0; + if (!copy.moneyAvailable) copy.moneyAvailable = 0; + if (!copy.moneyMax) copy.moneyMax = 0; + if (!copy.numOpenPortsRequired) copy.numOpenPortsRequired = 0; + if (!copy.openPortCount) copy.openPortCount = 0; + if (!copy.requiredHackingSkill) copy.requiredHackingSkill = 0; + if (!copy.serverGrowth) copy.serverGrowth = 0; + return copy; + }, + getServerMoneyAvailable: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 0; + } + if (failOnHacknetServer(ctx, server, "getServerMoneyAvailable")) { + return 0; + } + if (server.hostname == "home") { + // Return player's money + helpers.log(ctx, () => `returned player's money: ${numeralWrapper.formatMoney(Player.money)}`); + return Player.money; + } + helpers.log(ctx, () => `returned ${numeralWrapper.formatMoney(server.moneyAvailable)} for '${server.hostname}'`); + return server.moneyAvailable; + }, + getServerSecurityLevel: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 1; + } + if (failOnHacknetServer(ctx, server, "getServerSecurityLevel")) { + return 1; + } + helpers.log( + ctx, + () => `returned ${numeralWrapper.formatServerSecurity(server.hackDifficulty)} for '${server.hostname}'`, + ); + return server.hackDifficulty; + }, + getServerBaseSecurityLevel: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + helpers.log(ctx, () => `getServerBaseSecurityLevel is deprecated because it's not useful.`); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 1; + } + if (failOnHacknetServer(ctx, server, "getServerBaseSecurityLevel")) { + return 1; + } + helpers.log( + ctx, + () => `returned ${numeralWrapper.formatServerSecurity(server.baseDifficulty)} for '${server.hostname}'`, + ); + return server.baseDifficulty; + }, + getServerMinSecurityLevel: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 1; + } + if (failOnHacknetServer(ctx, server, "getServerMinSecurityLevel")) { + return 1; + } + helpers.log( + ctx, + () => `returned ${numeralWrapper.formatServerSecurity(server.minDifficulty)} for ${server.hostname}`, + ); + return server.minDifficulty; + }, + getServerRequiredHackingLevel: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 1; + } + if (failOnHacknetServer(ctx, server, "getServerRequiredHackingLevel")) { + return 1; + } + helpers.log( + ctx, + () => `returned ${numeralWrapper.formatSkill(server.requiredHackingSkill)} for '${server.hostname}'`, + ); + return server.requiredHackingSkill; + }, + getServerMaxMoney: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 0; + } + if (failOnHacknetServer(ctx, server, "getServerMaxMoney")) { + return 0; + } + helpers.log(ctx, () => `returned ${numeralWrapper.formatMoney(server.moneyMax)} for '${server.hostname}'`); + return server.moneyMax; + }, + getServerGrowth: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 1; + } + if (failOnHacknetServer(ctx, server, "getServerGrowth")) { + return 1; + } + helpers.log(ctx, () => `returned ${server.serverGrowth} for '${server.hostname}'`); + return server.serverGrowth; + }, + getServerNumPortsRequired: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "Cannot be executed on this server."); + return 5; + } + if (failOnHacknetServer(ctx, server, "getServerNumPortsRequired")) { + return 5; + } + helpers.log(ctx, () => `returned ${server.numOpenPortsRequired} for '${server.hostname}'`); + return server.numOpenPortsRequired; + }, + getServerRam: + (ctx: NetscriptContext) => + (_hostname: unknown): [number, number] => { + const hostname = helpers.string(ctx, "hostname", _hostname); + helpers.log(ctx, () => `getServerRam is deprecated in favor of getServerMaxRam / getServerUsedRam`); + const server = helpers.getServer(ctx, hostname); + helpers.log( + ctx, + () => `returned [${numeralWrapper.formatRAM(server.maxRam)}, ${numeralWrapper.formatRAM(server.ramUsed)}]`, + ); + return [server.maxRam, server.ramUsed]; + }, + getServerMaxRam: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + helpers.log(ctx, () => `returned ${numeralWrapper.formatRAM(server.maxRam)}`); + return server.maxRam; + }, + getServerUsedRam: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + helpers.log(ctx, () => `returned ${numeralWrapper.formatRAM(server.ramUsed)}`); + return server.ramUsed; + }, + serverExists: + (ctx: NetscriptContext) => + (_hostname: unknown): boolean => { + const hostname = helpers.string(ctx, "hostname", _hostname); + return GetServer(hostname) !== null; + }, + fileExists: + (ctx: NetscriptContext) => + (_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): boolean => { + const filename = helpers.string(ctx, "filename", _filename); + const hostname = helpers.string(ctx, "hostname", _hostname); + if (filename === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, "Usage: fileExists(scriptname, [server])"); + } + const server = helpers.getServer(ctx, hostname); + for (let i = 0; i < server.scripts.length; ++i) { + if (filename == server.scripts[i].filename) { + return true; + } + } + for (let i = 0; i < server.programs.length; ++i) { + if (filename.toLowerCase() == server.programs[i].toLowerCase()) { + return true; + } + } + for (let i = 0; i < server.messages.length; ++i) { + if (filename.toLowerCase() === server.messages[i].toLowerCase()) { + return true; + } + } + const txtFile = getTextFile(filename, server); + return txtFile != null; + }, + isRunning: + (ctx: NetscriptContext) => + (fn: unknown, hostname: unknown, ...scriptArgs: unknown[]): boolean => { + const ident = helpers.scriptIdentifier(ctx, fn, hostname, scriptArgs); + return getRunningScript(ctx, ident) !== null; + }, + getPurchasedServerLimit: () => (): number => { + return getPurchaseServerLimit(); + }, + getPurchasedServerMaxRam: () => (): number => { + return getPurchaseServerMaxRam(); + }, + getPurchasedServerCost: + (ctx: NetscriptContext) => + (_ram: unknown): number => { + const ram = helpers.number(ctx, "ram", _ram); - const possibleLogs = Object.fromEntries([...getFunctionNames(ns, "")].map((a) => [a, true])); + const cost = getPurchaseServerCost(ram); + if (cost === Infinity) { + helpers.log(ctx, () => `Invalid argument: ram='${ram}'`); + return Infinity; + } - const wrappedNS = wrapAPI({}, workerScript, ns) as unknown as INS; - (wrappedNS as any).args = ns.args; - (wrappedNS as any).enums = ns.enums; - return wrappedNS; -} + return cost; + }, + purchaseServer: + (ctx: NetscriptContext) => + (_name: unknown, _ram: unknown): string => { + const name = helpers.string(ctx, "name", _name); + const ram = helpers.number(ctx, "ram", _ram); + let hostnameStr = String(name); + hostnameStr = hostnameStr.replace(/\s+/g, ""); + if (hostnameStr == "") { + helpers.log(ctx, () => `Invalid argument: hostname='${hostnameStr}'`); + return ""; + } + + if (Player.purchasedServers.length >= getPurchaseServerLimit()) { + helpers.log( + ctx, + () => + `You have reached the maximum limit of ${getPurchaseServerLimit()} servers. You cannot purchase any more.`, + ); + return ""; + } + + const cost = getPurchaseServerCost(ram); + if (cost === Infinity) { + if (ram > getPurchaseServerMaxRam()) { + helpers.log(ctx, () => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`); + } else { + helpers.log(ctx, () => `Invalid argument: ram='${ram}' must be a positive power of 2`); + } + + return ""; + } + + if (Player.money < cost) { + helpers.log(ctx, () => `Not enough money to purchase server. Need ${numeralWrapper.formatMoney(cost)}`); + return ""; + } + const newServ = safetlyCreateUniqueServer({ + ip: createUniqueRandomIp(), + hostname: hostnameStr, + organizationName: "", + isConnectedTo: false, + adminRights: true, + purchasedByPlayer: true, + maxRam: ram, + }); + AddToAllServers(newServ); + + Player.purchasedServers.push(newServ.hostname); + const homeComputer = Player.getHomeComputer(); + homeComputer.serversOnNetwork.push(newServ.hostname); + newServ.serversOnNetwork.push(homeComputer.hostname); + Player.loseMoney(cost, "servers"); + helpers.log( + ctx, + () => `Purchased new server with hostname '${newServ.hostname}' for ${numeralWrapper.formatMoney(cost)}`, + ); + return newServ.hostname; + }, + deleteServer: + (ctx: NetscriptContext) => + (_name: unknown): boolean => { + const name = helpers.string(ctx, "name", _name); + let hostnameStr = String(name); + hostnameStr = hostnameStr.replace(/\s\s+/g, ""); + const server = GetServer(hostnameStr); + if (!(server instanceof Server)) { + helpers.log(ctx, () => `Invalid argument: hostname='${hostnameStr}'`); + return false; + } + + if (!server.purchasedByPlayer || server.hostname === "home") { + helpers.log(ctx, () => "Cannot delete non-purchased server."); + return false; + } + + const hostname = server.hostname; + + // Can't delete server you're currently connected to + if (server.isConnectedTo) { + helpers.log(ctx, () => "You are currently connected to the server you are trying to delete."); + return false; + } + + // A server cannot delete itself + if (hostname === ctx.workerScript.hostname) { + helpers.log(ctx, () => "Cannot delete the server this script is running on."); + return false; + } + + // Delete all scripts running on server + if (server.runningScripts.length > 0) { + helpers.log(ctx, () => `Cannot delete server '${hostname}' because it still has scripts running.`); + return false; + } + + // Delete from player's purchasedServers array + let found = false; + for (let i = 0; i < Player.purchasedServers.length; ++i) { + if (hostname == Player.purchasedServers[i]) { + found = true; + Player.purchasedServers.splice(i, 1); + break; + } + } + + if (!found) { + helpers.log( + ctx, + () => `Could not identify server ${hostname} as a purchased server. This is a bug. Report to dev.`, + ); + return false; + } + + // Delete from all servers + DeleteServer(hostname); + + // Delete from home computer + found = false; + const homeComputer = Player.getHomeComputer(); + for (let i = 0; i < homeComputer.serversOnNetwork.length; ++i) { + if (hostname == homeComputer.serversOnNetwork[i]) { + homeComputer.serversOnNetwork.splice(i, 1); + helpers.log(ctx, () => `Deleted server '${hostnameStr}`); + return true; + } + } + // Wasn't found on home computer + helpers.log(ctx, () => `Could not find server ${hostname} as a purchased server. This is a bug. Report to dev.`); + return false; + }, + getPurchasedServers: () => (): string[] => { + const res: string[] = []; + Player.purchasedServers.forEach(function (hostname) { + res.push(hostname); + }); + return res; + }, + writePort: + (ctx: NetscriptContext) => + (_port: unknown, data: unknown = ""): Promise => { + const port = helpers.number(ctx, "port", _port); + if (typeof data !== "string" && typeof data !== "number") { + throw helpers.makeRuntimeErrorMsg( + ctx, + `Trying to write invalid data to a port: only strings and numbers are valid.`, + ); + } + const iport = helpers.getValidPort(ctx, port); + return Promise.resolve(iport.write(data)); + }, + write: + (ctx: NetscriptContext) => + (_port: unknown, data: unknown = "", _mode: unknown = "a"): Promise => { + const port = helpers.string(ctx, "port", _port); + const mode = helpers.string(ctx, "mode", _mode); + if (isString(port)) { + // Write to script or text file + let fn = port; + if (!isValidFilePath(fn)) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${fn}`); + } + + if (fn.lastIndexOf("/") === 0) { + fn = removeLeadingSlash(fn); + } + + // Coerce 'data' to be a string + try { + data = String(data); + } catch (e: unknown) { + throw helpers.makeRuntimeErrorMsg( + ctx, + `Invalid data (${String(e)}). Data being written must be convertible to a string`, + ); + } + + const server = ctx.workerScript.getServer(); + if (server == null) { + throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev."); + } + if (isScriptFilename(fn)) { + // Write to script + let script = ctx.workerScript.getScriptOnServer(fn, server); + if (script == null) { + // Create a new script + script = new Script(Player, fn, String(data), server.hostname, server.scripts); + server.scripts.push(script); + return script.updateRamUsage(Player, server.scripts); + } + mode === "w" ? (script.code = String(data)) : (script.code += data); + return script.updateRamUsage(Player, server.scripts); + } else { + // Write to text file + const txtFile = getTextFile(fn, server); + if (txtFile == null) { + createTextFile(fn, String(data), server); + return Promise.resolve(); + } + if (mode === "w") { + txtFile.write(String(data)); + } else { + txtFile.append(String(data)); + } + } + return Promise.resolve(); + } else { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`); + } + }, + tryWritePort: + (ctx: NetscriptContext) => + (_port: unknown, data: unknown = ""): Promise => { + let port = helpers.number(ctx, "port", _port); + if (typeof data !== "string" && typeof data !== "number") { + throw helpers.makeRuntimeErrorMsg( + ctx, + `Trying to write invalid data to a port: only strings and numbers are valid.`, + ); + } + if (!isNaN(port)) { + port = Math.round(port); + if (port < 1 || port > CONSTANTS.NumNetscriptPorts) { + throw helpers.makeRuntimeErrorMsg( + ctx, + `Invalid port: ${port}. Only ports 1-${CONSTANTS.NumNetscriptPorts} are valid.`, + ); + } + const iport = NetscriptPorts[port - 1]; + if (iport == null || !(iport instanceof Object)) { + throw helpers.makeRuntimeErrorMsg(ctx, `Could not find port: ${port}. This is a bug. Report to dev.`); + } + return Promise.resolve(iport.tryWrite(data)); + } else { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`); + } + }, + readPort: + (ctx: NetscriptContext) => + (_port: unknown): unknown => { + const port = helpers.number(ctx, "port", _port); + // Read from port + const iport = helpers.getValidPort(ctx, port); + const x = iport.read(); + return x; + }, + read: + (ctx: NetscriptContext) => + (_port: unknown): string => { + const port = helpers.string(ctx, "port", _port); + if (isString(port)) { + // Read from script or text file + const fn = port; + const server = GetServer(ctx.workerScript.hostname); + if (server == null) { + throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev."); + } + if (isScriptFilename(fn)) { + // Read from script + const script = ctx.workerScript.getScriptOnServer(fn, server); + if (script == null) { + return ""; + } + return script.code; + } else { + // Read from text file + const txtFile = getTextFile(fn, server); + if (txtFile !== null) { + return txtFile.text; + } else { + return ""; + } + } + } else { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`); + } + }, + peek: + (ctx: NetscriptContext) => + (_port: unknown): unknown => { + const port = helpers.number(ctx, "port", _port); + const iport = helpers.getValidPort(ctx, port); + const x = iport.peek(); + return x; + }, + clear: + (ctx: NetscriptContext) => + (_file: unknown): void => { + const file = helpers.string(ctx, "file", _file); + if (isString(file)) { + // Clear text file + const fn = file; + const server = GetServer(ctx.workerScript.hostname); + if (server == null) { + throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev."); + } + const txtFile = getTextFile(fn, server); + if (txtFile != null) { + txtFile.write(""); + } + } else { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${file}`); + } + }, + clearPort: + (ctx: NetscriptContext) => + (_port: unknown): void => { + const port = helpers.number(ctx, "port", _port); + // Clear port + const iport = helpers.getValidPort(ctx, port); + iport.clear(); + }, + getPortHandle: + (ctx: NetscriptContext) => + (_port: unknown): IPort => { + const port = helpers.number(ctx, "port", _port); + const iport = helpers.getValidPort(ctx, port); + return iport; + }, + rm: + (ctx: NetscriptContext) => + (_fn: unknown, _hostname: unknown = ctx.workerScript.hostname): boolean => { + const fn = helpers.string(ctx, "fn", _fn); + const hostname = helpers.string(ctx, "hostname", _hostname); + const s = helpers.getServer(ctx, hostname); + + const status = s.removeFile(fn); + if (!status.res) { + helpers.log(ctx, () => status.msg + ""); + } + + return status.res; + }, + scriptRunning: + (ctx: NetscriptContext) => + (_scriptname: unknown, _hostname: unknown): boolean => { + const scriptname = helpers.string(ctx, "scriptname", _scriptname); + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + for (let i = 0; i < server.runningScripts.length; ++i) { + if (server.runningScripts[i].filename == scriptname) { + return true; + } + } + return false; + }, + scriptKill: + (ctx: NetscriptContext) => + (_scriptname: unknown, _hostname: unknown): boolean => { + const scriptname = helpers.string(ctx, "scriptname", _scriptname); + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + let suc = false; + for (let i = 0; i < server.runningScripts.length; i++) { + if (server.runningScripts[i].filename == scriptname) { + killWorkerScript({ runningScript: server.runningScripts[i], hostname: server.hostname }); + suc = true; + i--; + } + } + return suc; + }, + getScriptName: (ctx: NetscriptContext) => (): string => { + return ctx.workerScript.name; + }, + getScriptRam: + (ctx: NetscriptContext) => + (_scriptname: unknown, _hostname: unknown = ctx.workerScript.hostname): number => { + const scriptname = helpers.string(ctx, "scriptname", _scriptname); + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + for (let i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename == scriptname) { + return server.scripts[i].ramUsage; + } + } + return 0; + }, + getRunningScript: + (ctx: NetscriptContext) => + (fn: unknown, hostname: unknown, ...args: unknown[]): IRunningScriptDef | null => { + const ident = helpers.scriptIdentifier(ctx, fn, hostname, args); + const runningScript = getRunningScript(ctx, ident); + if (runningScript === null) return null; + return createPublicRunningScript(runningScript); + }, + getHackTime: + (ctx: NetscriptContext) => + (_hostname: unknown = ctx.workerScript.hostname): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "invalid for this kind of server"); + return Infinity; + } + if (failOnHacknetServer(ctx, server, "getHackTime")) { + return Infinity; + } + + return calculateHackingTime(server, Player) * 1000; + }, + getGrowTime: + (ctx: NetscriptContext) => + (_hostname: unknown = ctx.workerScript.hostname): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "invalid for this kind of server"); + return Infinity; + } + if (failOnHacknetServer(ctx, server, "getGrowTime")) { + return Infinity; + } + + return calculateGrowTime(server, Player) * 1000; + }, + getWeakenTime: + (ctx: NetscriptContext) => + (_hostname: unknown = ctx.workerScript.hostname): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); + const server = helpers.getServer(ctx, hostname); + if (!(server instanceof Server)) { + helpers.log(ctx, () => "invalid for this kind of server"); + return Infinity; + } + if (failOnHacknetServer(ctx, server, "getWeakenTime")) { + return Infinity; + } + + return calculateWeakenTime(server, Player) * 1000; + }, + getTotalScriptIncome: () => (): [number, number] => { + // First element is total income of all currently running scripts + let total = 0; + for (const script of workerScripts.values()) { + total += script.scriptRef.onlineMoneyMade / script.scriptRef.onlineRunningTime; + } + + return [total, Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug / 1000)]; + }, + getScriptIncome: + (ctx: NetscriptContext) => + (fn: unknown, hostname: unknown, ...args: unknown[]): number => { + const ident = helpers.scriptIdentifier(ctx, fn, hostname, args); + const runningScript = getRunningScript(ctx, ident); + if (runningScript == null) { + helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); + return -1; + } + return runningScript.onlineMoneyMade / runningScript.onlineRunningTime; + }, + getTotalScriptExpGain: () => (): number => { + let total = 0; + for (const ws of workerScripts.values()) { + total += ws.scriptRef.onlineExpGained / ws.scriptRef.onlineRunningTime; + } + return total; + }, + getScriptExpGain: + (ctx: NetscriptContext) => + (fn: unknown, hostname: unknown, ...args: unknown[]): number => { + const ident = helpers.scriptIdentifier(ctx, fn, hostname, args); + const runningScript = getRunningScript(ctx, ident); + if (runningScript == null) { + helpers.log(ctx, () => getCannotFindRunningScriptErrorMessage(ident)); + return -1; + } + return runningScript.onlineExpGained / runningScript.onlineRunningTime; + }, + nFormat: + (ctx: NetscriptContext) => + (_n: unknown, _format: unknown): string => { + const n = helpers.number(ctx, "n", _n); + const format = helpers.string(ctx, "format", _format); + if (isNaN(n)) { + return ""; + } + + return numeralWrapper.format(n, format); + }, + tFormat: + (ctx: NetscriptContext) => + (_milliseconds: unknown, _milliPrecision: unknown = false): string => { + const milliseconds = helpers.number(ctx, "milliseconds", _milliseconds); + const milliPrecision = !!_milliPrecision; + return convertTimeMsToTimeElapsedString(milliseconds, milliPrecision); + }, + getTimeSinceLastAug: () => (): number => { + return Player.playtimeSinceLastAug; + }, + alert: + (ctx: NetscriptContext) => + (_message: unknown): void => { + const message = helpers.string(ctx, "message", _message); + dialogBoxCreate(message); + }, + toast: + (ctx: NetscriptContext) => + (_message: unknown, _variant: unknown = ToastVariant.SUCCESS, _duration: unknown = 2000): void => { + const message = helpers.string(ctx, "message", _message); + const variant = helpers.string(ctx, "variant", _variant); + const duration = helpers.number(ctx, "duration", _duration); + if (!checkEnum(ToastVariant, variant)) + throw new Error(`variant must be one of ${Object.values(ToastVariant).join(", ")}`); + SnackbarEvents.emit(message, variant, duration); + }, + prompt: + (ctx: NetscriptContext) => + (_txt: unknown, options?: { type?: string; options?: string[] }): Promise => { + const txt = helpers.string(ctx, "txt", _txt); + + return new Promise(function (resolve) { + PromptEvent.emit({ + txt: txt, + options, + resolve: resolve, + }); + }); + }, + wget: + (ctx: NetscriptContext) => + async (_url: unknown, _target: unknown, _hostname: unknown = ctx.workerScript.hostname): Promise => { + const url = helpers.string(ctx, "url", _url); + const target = helpers.string(ctx, "target", _target); + const hostname = helpers.string(ctx, "hostname", _hostname); + if (!isScriptFilename(target) && !target.endsWith(".txt")) { + helpers.log(ctx, () => `Invalid target file: '${target}'. Must be a script or text file.`); + return Promise.resolve(false); + } + const s = helpers.getServer(ctx, hostname); + return new Promise(function (resolve) { + $.get( + url, + function (data) { + let res; + if (isScriptFilename(target)) { + res = s.writeToScriptFile(Player, target, data); + } else { + res = s.writeToTextFile(target, data); + } + if (!res.success) { + helpers.log(ctx, () => "Failed."); + return resolve(false); + } + if (res.overwritten) { + helpers.log(ctx, () => `Successfully retrieved content and overwrote '${target}' on '${hostname}'`); + return resolve(true); + } + helpers.log(ctx, () => `Successfully retrieved content to new file '${target}' on '${hostname}'`); + return resolve(true); + }, + "text", + ).fail(function (e) { + helpers.log(ctx, () => JSON.stringify(e)); + return resolve(false); + }); + }); + }, + getFavorToDonate: () => (): number => { + return Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); + }, + getPlayer: () => (): INetscriptPlayer => { + const data = { + hp: Player.hp, + skills: Player.skills, + exp: Player.exp, + hacking_chance_mult: Player.mults.hacking_chance, + mults: JSON.parse(JSON.stringify(Player.mults)), + numPeopleKilled: Player.numPeopleKilled, + money: Player.money, + city: Player.city, + location: Player.location, + bitNodeN: Player.bitNodeN, + totalPlaytime: Player.totalPlaytime, + playtimeSinceLastAug: Player.playtimeSinceLastAug, + playtimeSinceLastBitnode: Player.playtimeSinceLastBitnode, + jobs: {}, + factions: Player.factions.slice(), + tor: Player.hasTorRouter(), + inBladeburner: Player.inBladeburner(), + hasCorporation: Player.hasCorporation(), + entropy: Player.entropy, + }; + Object.assign(data.jobs, Player.jobs); + return data; + }, + atExit: + (ctx: NetscriptContext) => + (f: unknown): void => { + if (typeof f !== "function") { + throw helpers.makeRuntimeErrorMsg(ctx, "argument should be function"); + } + ctx.workerScript.atExit = () => { + f(); + }; // Wrap the user function to prevent WorkerScript leaking as 'this' + }, + mv: + (ctx: NetscriptContext) => + (_host: unknown, _source: unknown, _destination: unknown): void => { + const host = helpers.string(ctx, "host", _host); + const source = helpers.string(ctx, "source", _source); + const destination = helpers.string(ctx, "destination", _destination); + + if (!isValidFilePath(source)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${source}'`); + if (!isValidFilePath(destination)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${destination}'`); + + const source_is_txt = source.endsWith(".txt"); + const dest_is_txt = destination.endsWith(".txt"); + + if (!isScriptFilename(source) && !source_is_txt) + throw helpers.makeRuntimeErrorMsg(ctx, `'mv' can only be used on scripts and text files (.txt)`); + if (source_is_txt != dest_is_txt) + throw helpers.makeRuntimeErrorMsg(ctx, `Source and destination files must have the same type`); + + if (source === destination) { + return; + } + + const destServer = helpers.getServer(ctx, host); + + if (!source_is_txt && destServer.isRunning(source)) + throw helpers.makeRuntimeErrorMsg(ctx, `Cannot use 'mv' on a script that is running`); + + interface File { + filename: string; + } + + const files = source_is_txt ? destServer.textFiles : destServer.scripts; + let source_file: File | null = null; + let dest_file: File | null = null; + + for (let i = 0; i < files.length; ++i) { + const file = files[i]; + if (file.filename === source) { + source_file = file; + } else if (file.filename === destination) { + dest_file = file; + } + } + + if (source_file == null) throw helpers.makeRuntimeErrorMsg(ctx, `Source file ${source} does not exist`); + + if (dest_file != null) { + if (dest_file instanceof TextFile && source_file instanceof TextFile) { + dest_file.text = source_file.text; + } else if (dest_file instanceof Script && source_file instanceof Script) { + dest_file.code = source_file.code; + dest_file.markUpdated(); + } + + destServer.removeFile(source); + } else { + source_file.filename = destination; + if (source_file instanceof Script) { + source_file.markUpdated(); + } + } + }, + flags: Flags, +}; + +// add undocumented functions +const ns = { + ...base, + ...NetscriptExtra(), +}; + +const possibleLogs = Object.fromEntries([...getFunctionNames(ns, "")].map((a) => [a, true])); diff --git a/src/NetscriptFunctions/Bladeburner.ts b/src/NetscriptFunctions/Bladeburner.ts index b7ab0ece8..efc5eaf23 100644 --- a/src/NetscriptFunctions/Bladeburner.ts +++ b/src/NetscriptFunctions/Bladeburner.ts @@ -1,5 +1,4 @@ -import { WorkerScript } from "../Netscript/WorkerScript"; -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { Bladeburner } from "../Bladeburner/Bladeburner"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { Bladeburner as INetscriptBladeburner, BladeburnerCurAction } from "../ScriptEditor/NetscriptDefinitions"; @@ -8,7 +7,7 @@ import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper"; import { BlackOperation } from "../Bladeburner/BlackOperation"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript): InternalAPI { +export function NetscriptBladeburner(): InternalAPI { const checkBladeburnerAccess = function (ctx: NetscriptContext, skipjoined = false): void { const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Must have joined bladeburner"); @@ -102,7 +101,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.startActionNetscriptFn(player, type, name, workerScript); + return bladeburner.startActionNetscriptFn(player, type, name, ctx.workerScript); } catch (e: unknown) { throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } @@ -200,7 +199,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript); + return bladeburner.getActionCountRemainingNetscriptFn(type, name, ctx.workerScript); } catch (e: unknown) { throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } @@ -275,7 +274,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript); + return bladeburner.getSkillLevelNetscriptFn(skillName, ctx.workerScript); } catch (e: unknown) { throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } @@ -289,7 +288,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, count, workerScript); + return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, count, ctx.workerScript); } catch (e: unknown) { throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } @@ -303,7 +302,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.upgradeSkillNetscriptFn(skillName, count, workerScript); + return bladeburner.upgradeSkillNetscriptFn(skillName, count, ctx.workerScript); } catch (e: unknown) { throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } @@ -317,7 +316,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript); + return bladeburner.getTeamSizeNetscriptFn(type, name, ctx.workerScript); } catch (e: unknown) { throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } @@ -332,7 +331,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript); + return bladeburner.setTeamSizeNetscriptFn(type, name, size, ctx.workerScript); } catch (e: unknown) { throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } @@ -394,7 +393,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript checkBladeburnerAccess(ctx, true); const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); - return bladeburner.joinBladeburnerFactionNetscriptFn(workerScript); + return bladeburner.joinBladeburnerFactionNetscriptFn(ctx.workerScript); }, joinBladeburnerDivision: (ctx: NetscriptContext) => (): boolean => { if (player.bitNodeN === 7 || player.sourceFileLvl(7) > 0) { diff --git a/src/NetscriptFunctions/CodingContract.ts b/src/NetscriptFunctions/CodingContract.ts index e7f653789..785f0e9f8 100644 --- a/src/NetscriptFunctions/CodingContract.ts +++ b/src/NetscriptFunctions/CodingContract.ts @@ -1,12 +1,11 @@ -import { WorkerScript } from "../Netscript/WorkerScript"; -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { is2DArray } from "../utils/helpers/is2DArray"; import { CodingContract } from "../CodingContracts"; import { CodingAttemptOptions, CodingContract as ICodingContract } from "../ScriptEditor/NetscriptDefinitions"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScript): InternalAPI { +export function NetscriptCodingContract(): InternalAPI { const getCodingContract = function ( ctx: NetscriptContext, func: string, @@ -28,7 +27,7 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr ( answer: unknown, _filename: unknown, - _hostname: unknown = workerScript.hostname, + _hostname: unknown = ctx.workerScript.hostname, { returnReward }: CodingAttemptOptions = { returnReward: false }, ): boolean | string => { const filename = helpers.string(ctx, "filename", _filename); @@ -78,7 +77,7 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr }, getContractType: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): string => { + (_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): string => { const filename = helpers.string(ctx, "filename", _filename); const hostname = helpers.string(ctx, "hostname", _hostname); const contract = getCodingContract(ctx, "getContractType", hostname, filename); @@ -86,7 +85,7 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr }, getData: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): unknown => { + (_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): unknown => { const filename = helpers.string(ctx, "filename", _filename); const hostname = helpers.string(ctx, "hostname", _hostname); const contract = getCodingContract(ctx, "getData", hostname, filename); @@ -109,7 +108,7 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr }, getDescription: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): string => { + (_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): string => { const filename = helpers.string(ctx, "filename", _filename); const hostname = helpers.string(ctx, "hostname", _hostname); const contract = getCodingContract(ctx, "getDescription", hostname, filename); @@ -117,7 +116,7 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr }, getNumTriesRemaining: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): number => { + (_filename: unknown, _hostname: unknown = ctx.workerScript.hostname): number => { const filename = helpers.string(ctx, "filename", _filename); const hostname = helpers.string(ctx, "hostname", _hostname); const contract = getCodingContract(ctx, "getNumTriesRemaining", hostname, filename); diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index d4200fb5a..3df04bf8b 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -1,4 +1,4 @@ -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { OfficeSpace } from "../Corporation/OfficeSpace"; import { Employee } from "../Corporation/Employee"; @@ -68,7 +68,7 @@ import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptCorporation(player: IPlayer): InternalAPI { +export function NetscriptCorporation(): InternalAPI { function createCorporation(corporationName: string, selfFund = true): boolean { if (!player.canAccessCorporation() || player.hasCorporation()) return false; if (!corporationName) return false; diff --git a/src/NetscriptFunctions/Extra.ts b/src/NetscriptFunctions/Extra.ts index b872c7991..427a1ec63 100644 --- a/src/NetscriptFunctions/Extra.ts +++ b/src/NetscriptFunctions/Extra.ts @@ -1,4 +1,4 @@ -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { Exploit } from "../Exploits/Exploit"; import * as bcrypt from "bcryptjs"; import { Apr1Events as devMenu } from "../ui/Apr1"; @@ -16,7 +16,7 @@ export interface INetscriptExtra { rainbow(guess: string): void; } -export function NetscriptExtra(player: IPlayer): InternalAPI { +export function NetscriptExtra(): InternalAPI { return { heart: { // Easter egg function diff --git a/src/NetscriptFunctions/Flags.ts b/src/NetscriptFunctions/Flags.ts index d67158dc0..8afa51fa9 100644 --- a/src/NetscriptFunctions/Flags.ts +++ b/src/NetscriptFunctions/Flags.ts @@ -1,41 +1,42 @@ import { toNative } from "./toNative"; import libarg from "arg"; import { ScriptArg } from "../Netscript/ScriptArg"; +import { NetscriptContext } from "../Netscript/APIWrapper"; type FlagType = StringConstructor | NumberConstructor | BooleanConstructor | StringConstructor[]; type FlagsRet = { [key: string]: ScriptArg }; -export function Flags(vargs: string[]): () => (data: unknown) => FlagsRet { - return (/* ctx: NetscriptContext */) => - (schema: unknown): FlagsRet => { - schema = toNative(schema); - if (!Array.isArray(schema)) throw new Error("flags schema passed in is invalid."); - const args: { - [key: string]: FlagType; - } = {}; +export function Flags(ctx: NetscriptContext | string[]): (data: unknown) => FlagsRet { + const vargs = Array.isArray(ctx) ? ctx : ctx.workerScript.args; + return (schema: unknown): FlagsRet => { + schema = toNative(schema); + if (!Array.isArray(schema)) throw new Error("flags schema passed in is invalid."); + const args: { + [key: string]: FlagType; + } = {}; - for (const d of schema) { - let t: FlagType = String; - if (typeof d[1] === "number") { - t = Number; - } else if (typeof d[1] === "boolean") { - t = Boolean; - } else if (Array.isArray(d[1])) { - t = [String]; - } - const numDashes = d[0].length > 1 ? 2 : 1; - args["-".repeat(numDashes) + d[0]] = t; + for (const d of schema) { + let t: FlagType = String; + if (typeof d[1] === "number") { + t = Number; + } else if (typeof d[1] === "boolean") { + t = Boolean; + } else if (Array.isArray(d[1])) { + t = [String]; } - const ret: FlagsRet = libarg(args, { argv: vargs }); - for (const d of schema) { - if (!ret.hasOwnProperty("--" + d[0]) || !ret.hasOwnProperty("-" + d[0])) ret[d[0]] = d[1]; - } - for (const key of Object.keys(ret)) { - if (!key.startsWith("-")) continue; - const value = ret[key]; - delete ret[key]; - const numDashes = key.length === 2 ? 1 : 2; - ret[key.slice(numDashes)] = value; - } - return ret; - }; + const numDashes = d[0].length > 1 ? 2 : 1; + args["-".repeat(numDashes) + d[0]] = t; + } + const ret: FlagsRet = libarg(args, { argv: vargs }); + for (const d of schema) { + if (!ret.hasOwnProperty("--" + d[0]) || !ret.hasOwnProperty("-" + d[0])) ret[d[0]] = d[1]; + } + for (const key of Object.keys(ret)) { + if (!key.startsWith("-")) continue; + const value = ret[key]; + delete ret[key]; + const numDashes = key.length === 2 ? 1 : 2; + ret[key.slice(numDashes)] = value; + } + return ret; + }; } diff --git a/src/NetscriptFunctions/Formulas.ts b/src/NetscriptFunctions/Formulas.ts index 0c6d84da4..3cc659d2e 100644 --- a/src/NetscriptFunctions/Formulas.ts +++ b/src/NetscriptFunctions/Formulas.ts @@ -1,4 +1,4 @@ -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { calculateServerGrowth } from "../Server/formulas/grow"; import { calculateMoneyGainRate, @@ -44,7 +44,7 @@ import { repFromDonation } from "../Faction/formulas/donation"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptFormulas(player: IPlayer): InternalAPI { +export function NetscriptFormulas(): InternalAPI { const checkFormulasAccess = function (ctx: NetscriptContext): void { if (!player.hasProgram(Programs.Formulas.name)) { throw helpers.makeRuntimeErrorMsg(ctx, `Requires Formulas.exe to run.`); diff --git a/src/NetscriptFunctions/Gang.ts b/src/NetscriptFunctions/Gang.ts index b7c3690d7..b51553d31 100644 --- a/src/NetscriptFunctions/Gang.ts +++ b/src/NetscriptFunctions/Gang.ts @@ -1,11 +1,10 @@ import { FactionNames } from "../Faction/data/FactionNames"; import { GangConstants } from "../Gang/data/Constants"; -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { Gang } from "../Gang/Gang"; import { AllGangs } from "../Gang/AllGangs"; import { GangMemberTasks } from "../Gang/GangMemberTasks"; import { GangMemberUpgrades } from "../Gang/GangMemberUpgrades"; -import { WorkerScript } from "../Netscript/WorkerScript"; import { GangMember } from "../Gang/GangMember"; import { GangMemberTask } from "../Gang/GangMemberTask"; import { helpers } from "../Netscript/NetscriptHelpers"; @@ -22,7 +21,7 @@ import { } from "../ScriptEditor/NetscriptDefinitions"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; -export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): InternalAPI { +export function NetscriptGang(): InternalAPI { const checkGangApiAccess = function (ctx: NetscriptContext): void { const gang = player.gang; if (gang === null) throw new Error("Must have joined gang"); @@ -170,9 +169,9 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte if (gang === null) throw new Error("Should not be called without Gang"); const recruited = gang.recruitMember(memberName); if (recruited) { - workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`); + ctx.workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`); } else { - workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`); + ctx.workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`); } return recruited; @@ -195,7 +194,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte const gang = player.gang; if (gang === null) throw new Error("Should not be called without Gang"); if (!gang.getAllTaskNames().includes(taskName)) { - workerScript.log( + ctx.workerScript.log( "gang.setMemberTask", () => `Failed to assign Gang Member '${memberName}' to Invalid task '${taskName}'. '${memberName}' is now Unassigned`, @@ -204,12 +203,12 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte } const success = member.assignToTask(taskName); if (success) { - workerScript.log( + ctx.workerScript.log( "gang.setMemberTask", () => `Successfully assigned Gang Member '${memberName}' to '${taskName}' task`, ); } else { - workerScript.log( + ctx.workerScript.log( "gang.setMemberTask", () => `Failed to assign Gang Member '${memberName}' to '${taskName}' task. '${memberName}' is now Unassigned`, @@ -277,9 +276,9 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte if (!equipment) return false; const res = member.buyUpgrade(equipment, player, gang); if (res) { - workerScript.log("gang.purchaseEquipment", () => `Purchased '${equipName}' for Gang member '${memberName}'`); + ctx.workerScript.log("gang.purchaseEquipment", () => `Purchased '${equipName}' for Gang member '${memberName}'`); } else { - workerScript.log( + ctx.workerScript.log( "gang.purchaseEquipment", () => `Failed to purchase '${equipName}' for Gang member '${memberName}'`, ); @@ -296,7 +295,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte if (gang === null) throw new Error("Should not be called without Gang"); const member = getGangMember(ctx, memberName); if (!member.canAscend()) return; - return gang.ascendMember(member, workerScript); + return gang.ascendMember(member, ctx.workerScript); }, getAscensionResult: (ctx: NetscriptContext) => @@ -321,10 +320,10 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte if (gang === null) throw new Error("Should not be called without Gang"); if (engage) { gang.territoryWarfareEngaged = true; - workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare"); + ctx.workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare"); } else { gang.territoryWarfareEngaged = false; - workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare"); + ctx.workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare"); } }, getChanceToWinClash: diff --git a/src/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts index 5f0df0fe8..1b1623b52 100644 --- a/src/NetscriptFunctions/Grafting.ts +++ b/src/NetscriptFunctions/Grafting.ts @@ -4,13 +4,13 @@ import { hasAugmentationPrereqs } from "../Faction/FactionHelpers"; import { CityName } from "../Locations/data/CityNames"; import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation"; import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../PersonObjects/Grafting/GraftingHelpers"; -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions"; import { Router } from "../ui/GameRoot"; import { GraftingWork } from "../Work/GraftingWork"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptGrafting(player: IPlayer): InternalAPI { +export function NetscriptGrafting(): InternalAPI { const checkGraftingAPIAccess = (ctx: NetscriptContext): void => { if (!player.canAccessGrafting()) { throw helpers.makeRuntimeErrorMsg( diff --git a/src/NetscriptFunctions/Hacknet.ts b/src/NetscriptFunctions/Hacknet.ts index d698c89ca..7e173ae71 100644 --- a/src/NetscriptFunctions/Hacknet.ts +++ b/src/NetscriptFunctions/Hacknet.ts @@ -1,5 +1,4 @@ -import { IPlayer } from "../PersonObjects/IPlayer"; -import { WorkerScript } from "../Netscript/WorkerScript"; +import { Player as player } from "../Player"; import { HacknetServerConstants } from "../Hacknet/data/Constants"; import { getCostOfNextHacknetNode, @@ -23,7 +22,7 @@ import { Hacknet as IHacknet, NodeStats } from "../ScriptEditor/NetscriptDefinit import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): InternalAPI { +export function NetscriptHacknet(): InternalAPI { // Utility function to get Hacknet Node object const getHacknetNode = function (ctx: NetscriptContext, i: number): HacknetNode | HacknetServer { if (i < 0 || i >= player.hacknetNodes.length) { @@ -128,7 +127,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I } const node = getHacknetNode(ctx, i); if (!(node instanceof HacknetServer)) { - workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers"); + helpers.log(ctx, () => "Can only be called on hacknet servers"); return false; } const res = purchaseCacheUpgrade(player, node, n); @@ -171,7 +170,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I } const node = getHacknetNode(ctx, i); if (!(node instanceof HacknetServer)) { - workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers"); + helpers.log(ctx, () => "Can only be called on hacknet servers"); return -1; } return node.calculateCacheUpgradeCost(n); diff --git a/src/NetscriptFunctions/Infiltration.ts b/src/NetscriptFunctions/Infiltration.ts index 921a8dfee..63f2703a5 100644 --- a/src/NetscriptFunctions/Infiltration.ts +++ b/src/NetscriptFunctions/Infiltration.ts @@ -1,4 +1,4 @@ -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { Infiltration as IInfiltration, @@ -20,7 +20,7 @@ import { checkEnum } from "../utils/helpers/checkEnum"; import { LocationName } from "../Locations/data/LocationNames"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptInfiltration(player: IPlayer): InternalAPI { +export function NetscriptInfiltration(): InternalAPI { const getLocationsWithInfiltrations = Object.values(Locations).filter( (location: Location) => location.infiltrationData, ); diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts index 2cf9ef29c..14771ea8a 100644 --- a/src/NetscriptFunctions/Singularity.ts +++ b/src/NetscriptFunctions/Singularity.ts @@ -1,5 +1,4 @@ -import { WorkerScript } from "../Netscript/WorkerScript"; -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player} from "../Player"; import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers"; import { startWorkerScript } from "../NetscriptWorker"; import { Augmentation } from "../Augmentation/Augmentation"; @@ -54,7 +53,7 @@ import { FactionWork } from "../Work/FactionWork"; import { FactionWorkType } from "../Work/data/FactionWorkType"; import { CompanyWork } from "../Work/CompanyWork"; -export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI { +export function NetscriptSingularity(): InternalAPI { const getAugmentation = function (ctx: NetscriptContext, name: string): Augmentation { if (!augmentationExists(name)) { throw helpers.makeRuntimeErrorMsg(ctx, `Invalid augmentation: '${name}'`); @@ -235,9 +234,9 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript runAfterReset(cbScript); }, 0); - // Prevent workerScript from "finishing execution naturally" - workerScript.running = false; - killWorkerScript(workerScript); + // Prevent ctx.workerScript from "finishing execution naturally" + ctx.workerScript.running = false; + killWorkerScript(ctx.workerScript); }, installAugmentations: (ctx: NetscriptContext) => function (_cbScript: unknown = ""): boolean { @@ -255,8 +254,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript runAfterReset(cbScript); }, 0); - workerScript.running = false; // Prevent workerScript from "finishing execution naturally" - killWorkerScript(workerScript); + ctx.workerScript.running = false; // Prevent ctx.workerScript from "finishing execution naturally" + killWorkerScript(ctx.workerScript); return true; }, @@ -1179,7 +1178,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeRoughName}'`); } helpers.log(ctx, () => `Attempting to commit ${crime.name}...`); - const crimeTime = crime.commit(player, 1, workerScript); + const crimeTime = crime.commit(player, 1, ctx.workerScript); if (focus) { player.startFocusing(); Router.toWork(); diff --git a/src/NetscriptFunctions/Sleeve.ts b/src/NetscriptFunctions/Sleeve.ts index d23c78f54..16b076926 100644 --- a/src/NetscriptFunctions/Sleeve.ts +++ b/src/NetscriptFunctions/Sleeve.ts @@ -1,4 +1,4 @@ -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers"; import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { CityName } from "../Locations/data/CityNames"; @@ -19,7 +19,7 @@ import { isSleeveFactionWork } from "../PersonObjects/Sleeve/Work/SleeveFactionW import { isSleeveCompanyWork } from "../PersonObjects/Sleeve/Work/SleeveCompanyWork"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptSleeve(player: IPlayer): InternalAPI { +export function NetscriptSleeve(): InternalAPI { const checkSleeveAPIAccess = function (ctx: NetscriptContext): void { if (player.bitNodeN !== 10 && !player.sourceFileLvl(10)) { throw helpers.makeRuntimeErrorMsg( diff --git a/src/NetscriptFunctions/Stanek.ts b/src/NetscriptFunctions/Stanek.ts index 1b6f3012a..c2e936f1b 100644 --- a/src/NetscriptFunctions/Stanek.ts +++ b/src/NetscriptFunctions/Stanek.ts @@ -1,5 +1,4 @@ -import { IPlayer } from "../PersonObjects/IPlayer"; -import { WorkerScript } from "../Netscript/WorkerScript"; +import { Player as player } from "../Player"; import { staneksGift } from "../CotMG/Helper"; import { Fragments, FragmentById } from "../CotMG/Fragment"; @@ -18,7 +17,7 @@ import { joinFaction } from "../Faction/FactionHelpers"; import { Factions } from "../Faction/Factions"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript): InternalAPI { +export function NetscriptStanek(): InternalAPI { function checkStanekAPIAccess(ctx: NetscriptContext): void { if (!player.hasAugmentation(AugmentationNames.StaneksGift1, true)) { throw helpers.makeRuntimeErrorMsg(ctx, "Stanek's Gift is not installed"); @@ -54,7 +53,7 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript): In //Charge the fragment const time = staneksGift.inBonus() ? 200 : 1000; return helpers.netscriptDelay(ctx, time).then(function () { - staneksGift.charge(player, fragment, workerScript.scriptRef.threads); + staneksGift.charge(player, fragment, ctx.workerScript.scriptRef.threads); helpers.log(ctx, () => `Charged fragment with ${ctx.workerScript.scriptRef.threads} threads.`); return Promise.resolve(); }); diff --git a/src/NetscriptFunctions/StockMarket.ts b/src/NetscriptFunctions/StockMarket.ts index 5d47e1bb0..5436680b4 100644 --- a/src/NetscriptFunctions/StockMarket.ts +++ b/src/NetscriptFunctions/StockMarket.ts @@ -1,5 +1,4 @@ -import { WorkerScript } from "../Netscript/WorkerScript"; -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling"; import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder, initStockMarketFn } from "../StockMarket/StockMarket"; import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers"; @@ -17,7 +16,7 @@ import { StockOrder, TIX } from "../ScriptEditor/NetscriptDefinitions"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript): InternalAPI { +export function NetscriptStockMarket(): InternalAPI { /** * Checks if the player has TIX API access. Throws an error if the player does not */ @@ -164,7 +163,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript const shares = helpers.number(ctx, "shares", _shares); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); - const res = buyStock(stock, shares, workerScript, {}); + const res = buyStock(stock, shares, ctx.workerScript, {}); return res ? stock.getAskPrice() : 0; }, sellStock: @@ -174,7 +173,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript const shares = helpers.number(ctx, "shares", _shares); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); - const res = sellStock(stock, shares, workerScript, {}); + const res = sellStock(stock, shares, ctx.workerScript, {}); return res ? stock.getBidPrice() : 0; }, @@ -193,7 +192,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript } } const stock = getStockFromSymbol(ctx, symbol); - const res = shortStock(stock, shares, workerScript, {}); + const res = shortStock(stock, shares, ctx.workerScript, {}); return res ? stock.getBidPrice() : 0; }, @@ -212,7 +211,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript } } const stock = getStockFromSymbol(ctx, symbol); - const res = sellShort(stock, shares, workerScript, {}); + const res = sellShort(stock, shares, ctx.workerScript, {}); return res ? stock.getAskPrice() : 0; }, @@ -259,7 +258,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`); } - return placeOrder(stock, shares, price, orderType, orderPos, workerScript); + return placeOrder(stock, shares, price, orderType, orderPos, ctx.workerScript); }, cancelOrder: (ctx: NetscriptContext) => @@ -315,7 +314,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript type: orderType, pos: orderPos, }; - return cancelOrder(params, workerScript); + return cancelOrder(params, ctx.workerScript); }, getOrders: (ctx: NetscriptContext) => (): StockOrder => { checkTixApiAccess(ctx); diff --git a/src/Terminal/determineAllPossibilitiesForTabCompletion.ts b/src/Terminal/determineAllPossibilitiesForTabCompletion.ts index 321eb6983..81a94390f 100644 --- a/src/Terminal/determineAllPossibilitiesForTabCompletion.ts +++ b/src/Terminal/determineAllPossibilitiesForTabCompletion.ts @@ -309,7 +309,7 @@ export async function determineAllPossibilitiesForTabCompletion( return "--" + f[0]; }); try { - return flagFunc()(schema); + return flagFunc(schema); } catch (err) { return {}; }