diff --git a/src/Infiltration/ui/BribeGame.tsx b/src/Infiltration/ui/BribeGame.tsx index b1633050d..2a550aeef 100644 --- a/src/Infiltration/ui/BribeGame.tsx +++ b/src/Infiltration/ui/BribeGame.tsx @@ -151,7 +151,7 @@ const positive = [ "patient", "dynamic", "loyal", - "straightforward" + "straightforward", ]; const negative = [ diff --git a/src/Netscript/APIWrapper.ts b/src/Netscript/APIWrapper.ts index 6844f45f5..3e1522e90 100644 --- a/src/Netscript/APIWrapper.ts +++ b/src/Netscript/APIWrapper.ts @@ -1,22 +1,15 @@ import { getRamCost } from "./RamCostGenerator"; -import type { IPort } from "../NetscriptPort"; -import type { BaseServer } from "../Server/BaseServer"; import type { WorkerScript } from "./WorkerScript"; -import { makeRuntimeRejectMsg } from "../NetscriptEvaluator"; import { Player } from "../Player"; -import { CityName } from "../Locations/data/CityNames"; -import { BasicHGWOptions } from "../ScriptEditor/NetscriptDefinitions"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { Server } from "../Server/Server"; -import { FormulaGang } from "../Gang/formulas/formulas"; -import { INetscriptHelper, ScriptIdentifier } from "../NetscriptFunctions/INetscriptHelper"; -import { GangMember } from "../Gang/GangMember"; -import { GangMemberTask } from "../Gang/GangMemberTask"; +import { helpers } from "./NetscriptHelpers"; import { ScriptArg } from "./ScriptArg"; +import { NSEnums } from "src/ScriptEditor/NetscriptDefinitions"; +import { NSFull } from "src/NetscriptFunctions"; type ExternalFunction = (...args: unknown[]) => unknown; -export type ExternalAPI = { - [string: string]: ExternalAPI | ExternalFunction; + +export type ExternalAPILayer = { + [key: string]: ExternalAPILayer | ExternalFunction | ScriptArg[]; }; type InternalFunction unknown> = (ctx: NetscriptContext) => F; @@ -24,48 +17,23 @@ 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; }; -type WrappedNetscriptFunction = (...args: unknown[]) => unknown; -type WrappedNetscriptAPI = { - readonly [string: string]: WrappedNetscriptAPI | WrappedNetscriptFunction; -}; - export type NetscriptContext = { - makeRuntimeErrorMsg: (message: string) => string; - log: (message: () => string) => void; workerScript: WorkerScript; function: string; - helper: WrappedNetscriptHelpers; -}; - -type WrappedNetscriptHelpers = { - makeRuntimeErrorMsg: (msg: string) => string; - string: (argName: string, v: unknown) => string; - number: (argName: string, v: unknown) => number; - ustring: (argName: string, v: unknown) => string | undefined; - unumber: (argName: string, v: unknown) => number | undefined; - scriptArgs(args: unknown): ScriptArg[]; - scriptIdentifier: (fn: unknown, hostname: unknown, args: unknown) => ScriptIdentifier; - city: (argName: string, v: unknown) => CityName; - boolean: (v: unknown) => boolean; - getServer: (hostname: string) => BaseServer; - checkSingularityAccess: () => void; - hack: (hostname: string, manual: boolean, { threads: requestedThreads, stock }?: BasicHGWOptions) => Promise; - getValidPort: (port: number) => IPort; - player(p: unknown): IPlayer; - server(s: unknown): Server; - gang(g: unknown): FormulaGang; - gangMember: (m: unknown) => GangMember; - gangTask: (t: unknown) => GangMemberTask; + functionPath: string; }; function wrapFunction( - helpers: INetscriptHelper, - wrappedAPI: ExternalAPI, + wrappedAPI: ExternalAPILayer, workerScript: WorkerScript, func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown, ...tree: string[] @@ -73,44 +41,19 @@ function wrapFunction( const functionPath = tree.join("."); const functionName = tree.pop(); if (typeof functionName !== "string") { - throw makeRuntimeRejectMsg(workerScript, "Failure occured while wrapping netscript api"); + throw helpers.makeRuntimeRejectMsg(workerScript, "Failure occured while wrapping netscript api"); } const ctx = { - makeRuntimeErrorMsg: (message: string) => { - return helpers.makeRuntimeErrorMsg(functionPath, message); - }, - log: (message: () => string) => { - workerScript.log(functionPath, message); - }, workerScript, function: functionName, - helper: { - makeRuntimeErrorMsg: (msg: string) => helpers.makeRuntimeErrorMsg(functionPath, msg), - string: (argName: string, v: unknown) => helpers.string(functionPath, argName, v), - number: (argName: string, v: unknown) => helpers.number(functionPath, argName, v), - ustring: (argName: string, v: unknown) => helpers.ustring(functionPath, argName, v), - unumber: (argName: string, v: unknown) => helpers.unumber(functionPath, argName, v), - scriptArgs: (v: unknown) => helpers.scriptArgs(functionPath, v), - scriptIdentifier: (fn: unknown, hostname: unknown, args: unknown) => - helpers.scriptIdentifier(functionPath, fn, hostname, args), - city: (argName: string, v: unknown) => helpers.city(functionPath, argName, v), - boolean: helpers.boolean, - getServer: (hostname: string) => helpers.getServer(hostname, ctx), - checkSingularityAccess: () => helpers.checkSingularityAccess(functionName), - hack: (hostname: string, manual: boolean, extra?: BasicHGWOptions) => helpers.hack(ctx, hostname, manual, extra), - getValidPort: (port: number) => helpers.getValidPort(functionPath, port), - player: (p: unknown) => helpers.player(functionPath, p), - server: (s: unknown) => helpers.server(functionPath, s), - gang: (g: unknown) => helpers.gang(functionPath, g), - gangMember: (m: unknown) => helpers.gangMember(functionPath, m), - gangTask: (t: unknown) => helpers.gangTask(functionPath, t), - }, + functionPath, }; function wrappedFunction(...args: unknown[]): unknown { - helpers.updateDynamicRam(ctx.function, getRamCost(Player, ...tree, ctx.function)); + helpers.checkEnvFlags(ctx); + helpers.updateDynamicRam(ctx, getRamCost(Player, ...tree, ctx.function)); return func(ctx)(...args); } - const parent = getNestedProperty(wrappedAPI, ...tree); + const parent = getNestedProperty(wrappedAPI, tree); Object.defineProperty(parent, functionName, { value: wrappedFunction, writable: true, @@ -118,20 +61,25 @@ function wrapFunction( }); } -export function wrapAPI( - helpers: INetscriptHelper, - wrappedAPI: ExternalAPI, +export function wrapAPI(workerScript: WorkerScript, namespace: object, args: ScriptArg[]): NSFull { + const wrappedAPI = wrapAPILayer({}, workerScript, namespace); + wrappedAPI.args = args; + return wrappedAPI as unknown as NSFull; +} + +export function wrapAPILayer( + wrappedAPI: ExternalAPILayer, workerScript: WorkerScript, namespace: object, ...tree: string[] -): WrappedNetscriptAPI { +) { for (const [key, value] of Object.entries(namespace)) { if (typeof value === "function") { - wrapFunction(helpers, wrappedAPI, workerScript, value, ...tree, key); + wrapFunction(wrappedAPI, workerScript, value, ...tree, key); } else if (Array.isArray(value)) { - setNestedProperty(wrappedAPI, value, key); + setNestedProperty(wrappedAPI, value.slice(), key); } else if (typeof value === "object") { - wrapAPI(helpers, wrappedAPI, workerScript, value, ...tree, key); + wrapAPILayer(wrappedAPI, workerScript, value, ...tree, key); } else { setNestedProperty(wrappedAPI, value, ...tree, key); } @@ -139,28 +87,21 @@ export function wrapAPI( return wrappedAPI; } -// TODO: This doesn't even work properly. -function setNestedProperty(root: object, value: unknown, ...tree: string[]): void { +function setNestedProperty(root: any, value: unknown, ...tree: string[]): void { let target = root; const key = tree.pop(); - if (!key) { - throw new Error("Failure occured while wrapping netscript api (setNestedProperty)"); + if (!key) throw new Error("Failure occured while wrapping netscript api (setNestedProperty)"); + for (const branch of tree) { + target[branch] ??= {}; + target = target[branch]; } - for (let branch of Object.values(target)) { - if (branch === undefined) { - branch = {}; - } - target = branch; - } - Object.assign(target, { [key]: value }); + target[key] = value; } -function getNestedProperty(root: any, ...tree: string[]): unknown { +function getNestedProperty(root: any, tree: string[]): unknown { let target = root; for (const branch of tree) { - if (target[branch] === undefined) { - target[branch] = {}; - } + target[branch] ??= {}; target = target[branch]; } return target; diff --git a/src/Netscript/Environment.ts b/src/Netscript/Environment.ts index 919d593ab..6985ec043 100644 --- a/src/Netscript/Environment.ts +++ b/src/Netscript/Environment.ts @@ -10,6 +10,12 @@ export class Environment { */ stopFlag = false; + /** + * The currently running function + */ + + runningFn = ""; + /** * Environment variables (currently only Netscript functions) */ diff --git a/src/Netscript/NetscriptHelpers.ts b/src/Netscript/NetscriptHelpers.ts new file mode 100644 index 000000000..7752c6eac --- /dev/null +++ b/src/Netscript/NetscriptHelpers.ts @@ -0,0 +1,665 @@ +import { NetscriptContext } from "./APIWrapper"; +import { WorkerScript } from "./WorkerScript"; +import { GetAllServers, GetServer } from "../Server/AllServers"; +import { Player } from "../Player"; +import { ScriptDeath } from "./ScriptDeath"; +import { isString } from "../utils/helpers/isString"; +import { numeralWrapper } from "../ui/numeralFormat"; +import { ScriptArg } from "./ScriptArg"; +import { CityName } from "../Locations/data/CityNames"; +import { BasicHGWOptions } from "src/ScriptEditor/NetscriptDefinitions"; +import { Server } from "../Server/Server"; +import { + calculateHackingChance, + calculateHackingExpGain, + calculateHackingTime, + calculatePercentMoneyHacked, +} from "../Hacking"; +import { netscriptCanHack } from "../Hacking/netscriptCanHack"; +import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; +import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; +import { CONSTANTS } from "../Constants"; +import { influenceStockThroughServerHack } from "../StockMarket/PlayerInfluencing"; +import { IPort } from "../NetscriptPort"; +import { NetscriptPorts } from "../NetscriptWorker"; +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"; +import { ScriptIdentifier } from "./ScriptIdentifier"; +import { findRunningScript, findRunningScriptByPid } from "../Script/ScriptHelpers"; +import { RunningScript as IRunningScript } from "../ScriptEditor/NetscriptDefinitions"; +import { arrayToString } from "../utils/helpers/arrayToString"; +import { HacknetServer } from "../Hacknet/HacknetServer"; +import { BaseServer } from "../Server/BaseServer"; + +export const helpers = { + string, + number, + scriptArgs, + argsToString, + isScriptErrorMessage, + makeRuntimeRejectMsg, + makeRuntimeErrorMsg, + resolveNetscriptRequestedThreads, + checkEnvFlags, + checkSingularityAccess, + netscriptDelay, + updateDynamicRam, + city, + getServer, + scriptIdentifier, + hack, + getValidPort, + player, + server, + gang, + gangMember, + gangTask, + log, + getFunctionNames, + getRunningScript, + getRunningScriptByArgs, + getCannotFindRunningScriptErrorMessage, + createPublicRunningScript, + failOnHacknetServer, +}; + +/** Convert a provided value v for argument argName to string. If it wasn't originally a string or number, throw. */ +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.`); +} + +/** Convert provided value v for argument argName to number. Throw if could not convert to a non-NaN number. */ +function number(ctx: NetscriptContext, argName: string, v: unknown): number { + if (typeof v === "string") { + const x = parseFloat(v); + if (!isNaN(x)) return x; // otherwise it wasn't even a string representing a number. + } else if (typeof v === "number") { + if (isNaN(v)) throw makeRuntimeErrorMsg(ctx, `'${argName}' is NaN.`); + return v; + } + throw makeRuntimeErrorMsg(ctx, `'${argName}' should be a number.`); +} + +/** Returns args back if it is 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; + } + const splitMsg = msg.split("|DELIMITER|"); + return splitMsg.length == 4; +} + +/** 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); + } + + 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?"); + const stack = errstack.split("\n").slice(1); + const workerScript = ctx.workerScript; + const caller = ctx.function; + const scripts = workerScript.getServer().scripts; + const userstack = []; + for (const stackline of stack) { + let filename; + for (const script of scripts) { + if (script.url && stackline.includes(script.url)) { + filename = script.filename; + } + for (const dependency of script.dependencies) { + if (stackline.includes(dependency.url)) { + filename = dependency.filename; + } + } + } + if (!filename) continue; + + interface ILine { + line: string; + func: string; + } + + function parseChromeStackline(line: string): ILine | null { + const lineRe = /.*:(\d+):\d+.*/; + const funcRe = /.*at (.+) \(.*/; + + const lineMatch = line.match(lineRe); + const funcMatch = line.match(funcRe); + if (lineMatch && funcMatch) { + return { line: lineMatch[1], func: funcMatch[1] }; + } + return null; + } + let call = { line: "-1", func: "unknown" }; + const chromeCall = parseChromeStackline(stackline); + if (chromeCall) { + call = chromeCall; + } + + function parseFirefoxStackline(line: string): ILine | null { + const lineRe = /.*:(\d+):\d+$/; + const lineMatch = line.match(lineRe); + + const lio = line.lastIndexOf("@"); + + if (lineMatch && lio !== -1) { + return { line: lineMatch[1], func: line.slice(0, lio) }; + } + return null; + } + + const firefoxCall = parseFirefoxStackline(stackline); + if (firefoxCall) { + call = firefoxCall; + } + + userstack.push(`${filename}:L${call.line}@${call.func}`); + } + + workerScript.log(caller, () => msg); + let rejectMsg = `${caller}: ${msg}`; + if (userstack.length !== 0) rejectMsg += `

Stack:
${userstack.join("
")}`; + 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) { + return isNaN(threads) || threads < 1 ? 1 : threads; + } + const requestedThreadsAsInt = requestedThreads | 0; + if (isNaN(requestedThreads) || requestedThreadsAsInt < 1) { + throw makeRuntimeRejectMsg( + ctx.workerScript, + `Invalid thread count passed to ${ctx.function}: ${requestedThreads}. Threads must be a positive number.`, + ); + } + if (requestedThreadsAsInt > threads) { + throw makeRuntimeRejectMsg( + ctx.workerScript, + `Too many threads requested by ${ctx.function}. Requested: ${requestedThreads}. Has: ${threads}.`, + ); + } + 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( + ctx, + `This singularity function requires Source-File 4 to run. A power up you obtain later in the game. + It will be very obvious when and how you can obtain it.`, + ); + } +} + +/** 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); + if (ws.env.runningFn && ctx.function !== "asleep") { + ws.errorMessage = makeRuntimeRejectMsg( + ws, + `Concurrent calls to Netscript functions are not allowed! + Did you forget to await hack(), grow(), or some other + promise-returning function? + Currently running: ${ws.env.runningFn} tried to run: ${ctx.function}`, + ); + if (ws.delayReject) ws.delayReject(new ScriptDeath(ws)); + throw new ScriptDeath(ws); //No idea if this is the right thing to throw + } +} + +/** Set a timeout for performing a task, mark the script as busy in the meantime. */ +function netscriptDelay(ctx: NetscriptContext, time: number): Promise { + const ws = ctx.workerScript; + return new Promise(function (resolve, reject) { + ws.delay = window.setTimeout(() => { + ws.delay = null; + ws.delayReject = undefined; + ws.env.runningFn = ""; + if (ws.env.stopFlag) reject(new ScriptDeath(ws)); + else resolve(); + }, time); + ws.delayReject = reject; + ws.env.runningFn = ctx.function; + }); +} + +/** Adds to dynamic ram cost when calling new ns functions from a script */ +function updateDynamicRam(ctx: NetscriptContext, ramCost: number): void { + const ws = ctx.workerScript; + const fnName = ctx.function; + if (ws.dynamicLoadedFns[fnName]) return; + ws.dynamicLoadedFns[fnName] = true; + + let threads = ws.scriptRef.threads; + if (typeof threads !== "number") { + console.warn(`WorkerScript detected NaN for threadcount for ${ws.name} on ${ws.hostname}`); + threads = 1; + } + ws.dynamicRamUsage += ramCost; + if (ws.dynamicRamUsage > 1.01 * ws.ramUsage) { + ws.errorMessage = makeRuntimeRejectMsg( + ws, + `Dynamic RAM usage calculated to be greater than initial RAM usage on fn: ${fnName}. + This is probably because you somehow circumvented the static RAM calculation. + + Threads: ${threads} + Dynamic RAM Usage: ${numeralWrapper.formatRAM(ws.dynamicRamUsage)} + Static RAM Usage: ${numeralWrapper.formatRAM(ws.ramUsage)} + + One of these could be the reason: + * Using eval() to get a reference to a ns function +   const myScan = eval('ns.scan'); + + * Using map access to do the same +   const myScan = ns['scan']; + + Sorry :(`, + ); + } +} + +/** Validates the input v as being a CityName. Throws an error if it is not. */ +function city(ctx: NetscriptContext, argName: string, v: unknown): CityName { + if (typeof v !== "string") throw makeRuntimeErrorMsg(ctx, `${argName} should be a city name.`); + const s = v as CityName; + if (!Object.values(CityName).includes(s)) throw makeRuntimeErrorMsg(ctx, `${argName} should be a city name.`); + return s; +} + +function scriptIdentifier( + ctx: NetscriptContext, + scriptID: unknown, + _hostname: unknown, + _args: unknown, +): ScriptIdentifier { + const ws = ctx.workerScript; + // Provide the pid for the current script if no identifier provided + if (scriptID === undefined) return ws.pid; + if (typeof scriptID === "number") return scriptID; + if (typeof scriptID === "string") { + const hostname = _hostname === undefined ? ctx.workerScript.hostname : string(ctx, "hostname", _hostname); + const args = _args === undefined ? [] : scriptArgs(ctx, _args); + return { + scriptname: scriptID, + hostname, + args, + }; + } + throw new Error("not implemented"); +} + +/** + * Gets the Server for a specific hostname/ip, throwing an error + * if the server doesn't exist. + * @param {NetscriptContext} ctx - Context from which getServer is being called. For logging purposes. + * @param {string} hostname - Hostname of the server + * @returns {BaseServer} The specified server as a BaseServer + */ +function getServer(ctx: NetscriptContext, hostname: string) { + const server = GetServer(hostname); + if (server == null) { + const str = hostname === "" ? "'' (empty string)" : "'" + hostname + "'"; + throw makeRuntimeErrorMsg(ctx, `Invalid hostname: ${str}`); + } + return server; +} + +function isScriptArgs(args: unknown): args is ScriptArg[] { + const isScriptArg = (arg: unknown) => typeof arg === "string" || typeof arg === "number" || typeof arg === "boolean"; + return Array.isArray(args) && args.every(isScriptArg); +} + +async function hack( + ctx: NetscriptContext, + hostname: string, + manual: boolean, + { threads: requestedThreads, stock }: BasicHGWOptions = {}, +): Promise { + const ws = ctx.workerScript; + const threads = helpers.resolveNetscriptRequestedThreads(ctx, requestedThreads); + const server = getServer(ctx, hostname); + if (!(server instanceof Server)) { + throw makeRuntimeErrorMsg(ctx, "Cannot be executed on this server."); + } + + // Calculate the hacking time + const hackingTime = calculateHackingTime(server, Player); // This is in seconds + + // No root access or skill level too low + const canHack = netscriptCanHack(server, Player); + if (!canHack.res) { + throw makeRuntimeErrorMsg(ctx, canHack.msg || ""); + } + + log( + ctx, + () => + `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( + hackingTime * 1000, + true, + )} (t=${numeralWrapper.formatThreads(threads)})`, + ); + + return helpers.netscriptDelay(ctx, hackingTime * 1000).then(function () { + const hackChance = calculateHackingChance(server, Player); + const rand = Math.random(); + let expGainedOnSuccess = calculateHackingExpGain(server, Player) * threads; + const expGainedOnFailure = expGainedOnSuccess / 4; + if (rand < hackChance) { + // Success! + const percentHacked = calculatePercentMoneyHacked(server, Player); + let maxThreadNeeded = Math.ceil(1 / percentHacked); + if (isNaN(maxThreadNeeded)) { + // Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value + maxThreadNeeded = 1e6; + } + + let moneyDrained = Math.floor(server.moneyAvailable * percentHacked) * threads; + + // Over-the-top safety checks + if (moneyDrained <= 0) { + moneyDrained = 0; + expGainedOnSuccess = expGainedOnFailure; + } + if (moneyDrained > server.moneyAvailable) { + moneyDrained = server.moneyAvailable; + } + server.moneyAvailable -= moneyDrained; + if (server.moneyAvailable < 0) { + server.moneyAvailable = 0; + } + + let moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain; + if (manual) { + moneyGained = moneyDrained * BitNodeMultipliers.ManualHackMoney; + } + + Player.gainMoney(moneyGained, "hacking"); + ws.scriptRef.onlineMoneyMade += moneyGained; + Player.scriptProdSinceLastAug += moneyGained; + ws.scriptRef.recordHack(server.hostname, moneyGained, threads); + Player.gainHackingExp(expGainedOnSuccess); + if (manual) Player.gainIntelligenceExp(0.005); + ws.scriptRef.onlineExpGained += expGainedOnSuccess; + log( + ctx, + () => + `Successfully hacked '${server.hostname}' for ${numeralWrapper.formatMoney( + moneyGained, + )} and ${numeralWrapper.formatExp(expGainedOnSuccess)} exp (t=${numeralWrapper.formatThreads(threads)})`, + ); + server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded)); + if (stock) { + influenceStockThroughServerHack(server, moneyDrained); + } + if (manual) { + server.backdoorInstalled = true; + } + return Promise.resolve(moneyGained); + } else { + // Player only gains 25% exp for failure? + Player.gainHackingExp(expGainedOnFailure); + ws.scriptRef.onlineExpGained += expGainedOnFailure; + log( + ctx, + () => + `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.formatExp( + expGainedOnFailure, + )} exp (t=${numeralWrapper.formatThreads(threads)})`, + ); + return Promise.resolve(0); + } + }); +} + +function getValidPort(ctx: NetscriptContext, port: number): IPort { + if (isNaN(port)) { + throw makeRuntimeErrorMsg( + ctx, + `Invalid argument. Must be a port number between 1 and ${CONSTANTS.NumNetscriptPorts}, is ${port}`, + ); + } + port = Math.round(port); + if (port < 1 || port > CONSTANTS.NumNetscriptPorts) { + throw makeRuntimeErrorMsg( + ctx, + `Trying to use an invalid port: ${port}. Only ports 1-${CONSTANTS.NumNetscriptPorts} are valid.`, + ); + } + const iport = NetscriptPorts[port - 1]; + if (iport == null || !(iport instanceof Object)) { + throw makeRuntimeErrorMsg(ctx, `Could not find port: ${port}. This is a bug. Report to dev.`); + } + return iport; +} + +function player(ctx: NetscriptContext, p: unknown): IPlayer { + const fakePlayer = { + hp: undefined, + mults: undefined, + numPeopleKilled: undefined, + money: undefined, + city: undefined, + location: undefined, + bitNodeN: undefined, + totalPlaytime: undefined, + playtimeSinceLastAug: undefined, + playtimeSinceLastBitnode: undefined, + jobs: undefined, + factions: undefined, + tor: undefined, + inBladeburner: undefined, + hasCorporation: undefined, + entropy: undefined, + }; + if (!roughlyIs(fakePlayer, p)) throw makeRuntimeErrorMsg(ctx, `player should be a Player.`); + return p as IPlayer; +} + +function server(ctx: NetscriptContext, s: unknown): Server { + if (!roughlyIs(new Server(), s)) throw makeRuntimeErrorMsg(ctx, `server should be a Server.`); + return s as Server; +} + +function roughlyIs(expect: object, actual: unknown): boolean { + if (typeof actual !== "object" || actual == null) return false; + const expects = Object.keys(expect); + const actuals = Object.keys(actual); + for (const expect of expects) + if (!actuals.includes(expect)) { + return false; + } + return true; +} + +function gang(ctx: NetscriptContext, g: unknown): FormulaGang { + if (!roughlyIs({ respect: 0, territory: 0, wantedLevel: 0 }, g)) + throw makeRuntimeErrorMsg(ctx, `gang should be a Gang.`); + return g as FormulaGang; +} + +function gangMember(ctx: NetscriptContext, m: unknown): GangMember { + if (!roughlyIs(new GangMember(), m)) throw makeRuntimeErrorMsg(ctx, `member should be a GangMember.`); + return m as GangMember; +} + +function gangTask(ctx: NetscriptContext, t: unknown): GangMemberTask { + if (!roughlyIs(new GangMemberTask("", "", false, false, {}), t)) + throw makeRuntimeErrorMsg(ctx, `task should be a GangMemberTask.`); + return t as 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; +} + +/** Provides an array of all function names on a nested object */ +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; +} + +function getRunningScriptByPid(pid: number): RunningScript | null { + for (const server of GetAllServers()) { + const runningScript = findRunningScriptByPid(pid, server); + if (runningScript) return runningScript; + } + return null; +} + +function getRunningScript(ctx: NetscriptContext, ident: ScriptIdentifier): RunningScript | null { + if (typeof ident === "number") { + return getRunningScriptByPid(ident); + } else { + return getRunningScriptByArgs(ctx, ident.scriptname, ident.hostname, ident.args); + } +} + +/** + * 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 + */ +function getCannotFindRunningScriptErrorMessage(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 + */ +function createPublicRunningScript(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 + */ +function failOnHacknetServer(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; + } +} diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index 46a1226ae..43c1b9498 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -346,6 +346,11 @@ const grafting = { }; const corporation = { + getMaterialNames: 0, + getIndustryTypes: 0, + getUnlockables: 0, + getUpgradeNames: 0, + getResearchNames: 0, createCorporation: 0, hasUnlockUpgrade: 0, getUnlockUpgradeCost: 0, diff --git a/src/Netscript/ScriptIdentifier.ts b/src/Netscript/ScriptIdentifier.ts new file mode 100644 index 000000000..aad4e24c4 --- /dev/null +++ b/src/Netscript/ScriptIdentifier.ts @@ -0,0 +1,9 @@ +import { ScriptArg } from "./ScriptArg"; + +export type ScriptIdentifier = //This was previously in INetscriptHelper.ts, may move to its own file or a generic types file. + | number + | { + scriptname: string; + hostname: string; + args: ScriptArg[]; + }; diff --git a/src/NetscriptEvaluator.ts b/src/NetscriptEvaluator.ts deleted file mode 100644 index bde595160..000000000 --- a/src/NetscriptEvaluator.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { isString } from "./utils/helpers/isString"; -import { GetServer } from "./Server/AllServers"; -import { ScriptDeath } from "./Netscript/ScriptDeath"; -import { WorkerScript } from "./Netscript/WorkerScript"; -import { NetscriptContext } from "./Netscript/APIWrapper"; - -export function netscriptDelay(time: number, workerScript: WorkerScript): Promise { - // Cancel any pre-existing netscriptDelay'ed function call - // TODO: the rejection almost certainly ends up in the uncaught rejection handler. - // Maybe reject with a stack-trace'd error message? - if (workerScript.delayReject) workerScript.delayReject(); - - return new Promise(function (resolve, reject) { - workerScript.delay = window.setTimeout(() => { - workerScript.delay = null; - workerScript.delayReject = undefined; - if (workerScript.env.stopFlag) reject(new ScriptDeath(workerScript)); - else resolve(); - }, time); - workerScript.delayReject = reject; - }); -} - -export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string { - const server = GetServer(workerScript.hostname); - if (server == null) { - throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.hostname}`); - } - - for (const scriptUrl of workerScript.scriptRef.dependencies) { - // Return just the original msg if it's nullish so that we don't get a workerscript error - msg = msg?.replace(new RegExp(scriptUrl.url, "g"), scriptUrl.filename) ?? msg; - } - - return "|DELIMITER|" + server.hostname + "|DELIMITER|" + workerScript.name + "|DELIMITER|" + msg; -} - -export function resolveNetscriptRequestedThreads(ctx: NetscriptContext, requestedThreads?: number): number { - const threads = ctx.workerScript.scriptRef.threads; - if (!requestedThreads) { - return isNaN(threads) || threads < 1 ? 1 : threads; - } - const requestedThreadsAsInt = requestedThreads | 0; - if (isNaN(requestedThreads) || requestedThreadsAsInt < 1) { - throw makeRuntimeRejectMsg( - ctx.workerScript, - `Invalid thread count passed to ${ctx.function}: ${requestedThreads}. Threads must be a positive number.`, - ); - } - if (requestedThreadsAsInt > threads) { - throw makeRuntimeRejectMsg( - ctx.workerScript, - `Too many threads requested by ${ctx.function}. Requested: ${requestedThreads}. Has: ${threads}.`, - ); - } - return requestedThreadsAsInt; -} - -export function isScriptErrorMessage(msg: string): boolean { - if (!isString(msg)) { - return false; - } - const splitMsg = msg.split("|DELIMITER|"); - return splitMsg.length == 4; -} diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index eefec8927..2c617b4b6 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -1,12 +1,8 @@ import $ from "jquery"; import { vsprintf, sprintf } from "sprintf-js"; - import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter"; - import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; - import { CONSTANTS } from "./Constants"; - import { calculateHackingChance, calculateHackingExpGain, @@ -15,22 +11,14 @@ import { calculateGrowTime, calculateWeakenTime, } from "./Hacking"; - -import { netscriptCanGrow, netscriptCanHack, netscriptCanWeaken } from "./Hacking/netscriptCanHack"; - -import { HacknetServer } from "./Hacknet/HacknetServer"; - +import { netscriptCanGrow, netscriptCanWeaken } from "./Hacking/netscriptCanHack"; import { Terminal } from "./Terminal"; - import { Player } from "./Player"; import { Programs } from "./Programs/Programs"; import { Script } from "./Script/Script"; -import { findRunningScript, findRunningScriptByPid } from "./Script/ScriptHelpers"; import { isScriptFilename } from "./Script/isScriptFilename"; import { PromptEvent } from "./ui/React/PromptManager"; - -import { GetServer, GetAllServers, DeleteServer, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers"; -import { RunningScript } from "./Script/RunningScript"; +import { GetServer, DeleteServer, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers"; import { getServerOnNetwork, numCycleForGrowth, @@ -40,26 +28,19 @@ import { } from "./Server/ServerHelpers"; import { getPurchaseServerCost, getPurchaseServerLimit, getPurchaseServerMaxRam } from "./Server/ServerPurchases"; import { Server } from "./Server/Server"; -import { influenceStockThroughServerHack, influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing"; - +import { influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing"; import { isValidFilePath, removeLeadingSlash } from "./Terminal/DirectoryHelpers"; import { TextFile, getTextFile, createTextFile } from "./TextFile"; - import { NetscriptPorts, runScriptFromScript } from "./NetscriptWorker"; import { killWorkerScript } from "./Netscript/killWorkerScript"; import { workerScripts } from "./Netscript/WorkerScripts"; import { WorkerScript } from "./Netscript/WorkerScript"; -import { makeRuntimeRejectMsg, netscriptDelay, resolveNetscriptRequestedThreads } from "./NetscriptEvaluator"; - +import { helpers } from "./Netscript/NetscriptHelpers"; import { numeralWrapper } from "./ui/numeralFormat"; import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions"; - import { LogBoxEvents, LogBoxCloserEvents } from "./ui/React/LogBoxManager"; import { arrayToString } from "./utils/helpers/arrayToString"; import { isString } from "./utils/helpers/isString"; -import { FormulaGang as FormulaGang } from "./Gang/formulas/formulas"; - -import { BaseServer } from "./Server/BaseServer"; import { NetscriptGang } from "./NetscriptFunctions/Gang"; import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve"; import { NetscriptExtra } from "./NetscriptFunctions/Extra"; @@ -74,15 +55,9 @@ import { NetscriptFormulas } from "./NetscriptFunctions/Formulas"; import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket"; import { NetscriptGrafting } from "./NetscriptFunctions/Grafting"; import { IPort } from "./NetscriptPort"; - import { - NS as INS, + NS, Player as INetscriptPlayer, - Gang as IGang, - Bladeburner as IBladeburner, - Stanek as IStanek, - Infiltration as IInfiltration, - RunningScript as IRunningScript, RecentScript as IRecentScript, BasicHGWOptions, ProcessInfo, @@ -91,12 +66,9 @@ import { BitNodeMultipliers as IBNMults, Server as IServerDef, RunningScript as IRunningScriptDef, - // ToastVariant, } from "./ScriptEditor/NetscriptDefinitions"; import { NetscriptSingularity } from "./NetscriptFunctions/Singularity"; -import { toNative } from "./NetscriptFunctions/toNative"; - import { dialogBoxCreate } from "./ui/React/DialogBox"; import { SnackbarEvents, ToastVariant } from "./ui/React/Snackbar"; import { checkEnum } from "./utils/helpers/checkEnum"; @@ -105,2441 +77,1942 @@ import { Flags } from "./NetscriptFunctions/Flags"; import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence"; import { CalculateShareMult, StartSharing } from "./NetworkShare/Share"; import { recentScripts } from "./Netscript/RecentScripts"; -import { CityName } from "./Locations/data/CityNames"; import { InternalAPI, NetscriptContext, wrapAPI } from "./Netscript/APIWrapper"; -import { INetscriptHelper, ScriptIdentifier } from "./NetscriptFunctions/INetscriptHelper"; -import { IPlayer } from "./PersonObjects/IPlayer"; -import { GangMember } from "./Gang/GangMember"; -import { GangMemberTask } from "./Gang/GangMemberTask"; -import { ScriptArg } from "./Netscript/ScriptArg"; +import { INetscriptExtra } from "./NetscriptFunctions/Extra"; -interface NS extends INS { - [key: string]: any; - gang: IGang; - bladeburner: IBladeburner; - stanek: IStanek; - infiltration: IInfiltration; +export type NSFull = NS & INetscriptExtra; + +export function NetscriptFunctions(workerScript: WorkerScript): NSFull { + return wrapAPI(workerScript, ns, workerScript.args.slice()); } -export function NetscriptFunctions(workerScript: WorkerScript): NS { - const updateDynamicRam = function (fnName: string, ramCost: number): void { - if (workerScript.dynamicLoadedFns[fnName]) { - return; - } - workerScript.dynamicLoadedFns[fnName] = true; +const base: InternalAPI = { + args: [], + enums: { + toast: ToastVariant, + }, - let threads = workerScript.scriptRef.threads; - if (typeof threads !== "number") { - console.warn(`WorkerScript detected NaN for threadcount for ${workerScript.name} on ${workerScript.hostname}`); - threads = 1; - } - workerScript.dynamicRamUsage += ramCost; - if (workerScript.dynamicRamUsage > 1.01 * workerScript.ramUsage) { - throw makeRuntimeRejectMsg( - workerScript, - `Dynamic RAM usage calculated to be greater than initial RAM usage on fn: ${fnName}. - This is probably because you somehow circumvented the static RAM calculation. - - Threads: ${threads} - Dynamic RAM Usage: ${numeralWrapper.formatRAM(workerScript.dynamicRamUsage)} - Static RAM Usage: ${numeralWrapper.formatRAM(workerScript.ramUsage)} - - One of these could be the reason: - * Using eval() to get a reference to a ns function -   const myScan = eval('ns.scan'); - - * Using map access to do the same -   const myScan = ns['scan']; - - Sorry :(`, - ); - } - }; - - /** - * Gets the Server for a specific hostname/ip, throwing an error - * if the server doesn't exist. - * @param {string} hostname - Hostname or IP of the server - * @param {string} callingFnName - Name of calling function. For logging purposes - * @returns {BaseServer} The specified server as a BaseServer - */ - const safeGetServer = function (hostname: string, ctx: NetscriptContext): BaseServer { - const server = GetServer(hostname); - if (server == null) { - const str = hostname === "" ? "'' (empty string)" : "'" + hostname + "'"; - throw ctx.makeRuntimeErrorMsg(`Invalid hostname: ${str}`); - } - return server; - }; - - /** - * 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 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; + 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); + 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); } - const server = safeGetServer(hostname, ctx); + 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); - 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.fn, 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.fn} 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 makeRuntimeErrorMsg = function (caller: string, msg: string): string { - const errstack = new Error().stack; - if (errstack === undefined) throw new Error("how did we not throw an error?"); - const stack = errstack.split("\n").slice(1); - const scripts = workerScript.getServer().scripts; - const userstack = []; - for (const stackline of stack) { - let filename; - for (const script of scripts) { - if (script.url && stackline.includes(script.url)) { - filename = script.filename; - } - for (const dependency of script.dependencies) { - if (stackline.includes(dependency.url)) { - filename = dependency.filename; - } - } + // 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 (!filename) continue; - - interface ILine { - line: string; - func: string; - } - - function parseChromeStackline(line: string): ILine | null { - const lineRe = /.*:(\d+):\d+.*/; - const funcRe = /.*at (.+) \(.*/; - - const lineMatch = line.match(lineRe); - const funcMatch = line.match(funcRe); - if (lineMatch && funcMatch) { - return { line: lineMatch[1], func: funcMatch[1] }; - } - return null; - } - let call = { line: "-1", func: "unknown" }; - const chromeCall = parseChromeStackline(stackline); - if (chromeCall) { - call = chromeCall; - } - - function parseFirefoxStackline(line: string): ILine | null { - const lineRe = /.*:(\d+):\d+$/; - const lineMatch = line.match(lineRe); - - const lio = line.lastIndexOf("@"); - - if (lineMatch && lio !== -1) { - return { line: lineMatch[1], func: line.slice(0, lio) }; - } - return null; - } - - const firefoxCall = parseFirefoxStackline(stackline); - if (firefoxCall) { - call = firefoxCall; - } - - userstack.push(`${filename}:L${call.line}@${call.func}`); - } - - workerScript.log(caller, () => msg); - let rejectMsg = `${caller}: ${msg}`; - if (userstack.length !== 0) rejectMsg += `

Stack:
${userstack.join("
")}`; - return makeRuntimeRejectMsg(workerScript, rejectMsg); - }; - - const checkSingularityAccess = function (func: string): void { - if (Player.bitNodeN !== 4) { - if (Player.sourceFileLvl(4) === 0) { - throw makeRuntimeErrorMsg( - func, - `This singularity function requires Source-File 4 to run. A power up you obtain later in the game. It will be very obvious when and how you can obtain it.`, + if (isNaN(hackAmount)) { + throw helpers.makeRuntimeErrorMsg( + ctx, + `Invalid hackAmount argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric.`, ); } - } - }; - const hack = async function ( - ctx: NetscriptContext, - hostname: string, - manual: boolean, - { threads: requestedThreads, stock }: BasicHGWOptions = {}, - ): Promise { - if (hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Takes 1 argument."); - } - const threads = resolveNetscriptRequestedThreads(ctx, requestedThreads); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - throw ctx.makeRuntimeErrorMsg("Cannot be executed on this server."); - } - - // Calculate the hacking time - const hackingTime = calculateHackingTime(server, Player); // This is in seconds - - // No root access or skill level too low - const canHack = netscriptCanHack(server, Player); - if (!canHack.res) { - throw ctx.makeRuntimeErrorMsg(canHack.msg || ""); - } - - ctx.log( - () => - `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( - hackingTime * 1000, - true, - )} (t=${numeralWrapper.formatThreads(threads)})`, - ); - - return netscriptDelay(hackingTime * 1000, workerScript).then(function () { - const hackChance = calculateHackingChance(server, Player); - const rand = Math.random(); - let expGainedOnSuccess = calculateHackingExpGain(server, Player) * threads; - const expGainedOnFailure = expGainedOnSuccess / 4; - if (rand < hackChance) { - // Success! - const percentHacked = calculatePercentMoneyHacked(server, Player); - let maxThreadNeeded = Math.ceil(1 / percentHacked); - if (isNaN(maxThreadNeeded)) { - // Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value - maxThreadNeeded = 1e6; - } - - let moneyDrained = Math.floor(server.moneyAvailable * percentHacked) * threads; - - // Over-the-top safety checks - if (moneyDrained <= 0) { - moneyDrained = 0; - expGainedOnSuccess = expGainedOnFailure; - } - if (moneyDrained > server.moneyAvailable) { - moneyDrained = server.moneyAvailable; - } - server.moneyAvailable -= moneyDrained; - if (server.moneyAvailable < 0) { - server.moneyAvailable = 0; - } - - let moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain; - if (manual) { - moneyGained = moneyDrained * BitNodeMultipliers.ManualHackMoney; - } - - Player.gainMoney(moneyGained, "hacking"); - workerScript.scriptRef.onlineMoneyMade += moneyGained; - Player.scriptProdSinceLastAug += moneyGained; - workerScript.scriptRef.recordHack(server.hostname, moneyGained, threads); - Player.gainHackingExp(expGainedOnSuccess); - if (manual) Player.gainIntelligenceExp(0.005); - workerScript.scriptRef.onlineExpGained += expGainedOnSuccess; - ctx.log( - () => - `Successfully hacked '${server.hostname}' for ${numeralWrapper.formatMoney( - moneyGained, - )} and ${numeralWrapper.formatExp(expGainedOnSuccess)} exp (t=${numeralWrapper.formatThreads(threads)})`, - ); - server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded)); - if (stock) { - influenceStockThroughServerHack(server, moneyDrained); - } - if (manual) { - server.backdoorInstalled = true; - } - return Promise.resolve(moneyGained); - } else { - // Player only gains 25% exp for failure? - Player.gainHackingExp(expGainedOnFailure); - workerScript.scriptRef.onlineExpGained += expGainedOnFailure; - ctx.log( - () => - `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.formatExp( - expGainedOnFailure, - )} exp (t=${numeralWrapper.formatThreads(threads)})`, - ); - return Promise.resolve(0); + if (hackAmount < 0 || hackAmount > server.moneyAvailable) { + return -1; + } else if (hackAmount === 0) { + return 0; } - }); - }; - const argsToString = function (args: unknown[]): string { - let out = ""; - for (let arg of args) { - if (arg === null) { - out += "null"; - continue; + const percentHacked = calculatePercentMoneyHacked(server, Player); + + if (percentHacked === 0 || server.moneyAvailable === 0) { + return 0; // To prevent returning infinity below } - if (arg === undefined) { - out += "undefined"; - continue; + + 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; } - arg = toNative(arg); - out += typeof arg === "object" ? JSON.stringify(arg) : `${arg}`; - } - return out; - }; - - const roughlyIs = (expect: object, actual: unknown): boolean => { - console.log(expect); - console.log(actual); - - if (typeof actual !== "object" || actual == null) return false; - const expects = Object.keys(expect); - const actuals = Object.keys(actual); - for (const expect of expects) - if (!actuals.includes(expect)) { - console.log(expect); - return false; - } - return true; - }; - - const helperString = (funcName: string, argName: string, v: unknown): string => { - if (typeof v === "string") return v; - if (typeof v === "number") return v + ""; // cast to string; - throw makeRuntimeErrorMsg(funcName, `'${argName}' should be a string.`); - }; - const helperNumber = (funcName: string, argName: string, v: unknown): number => { - if (typeof v === "string") { - const x = parseFloat(v); - if (!isNaN(x)) return x; // otherwise it wasn't even a string representing a number. - } else if (typeof v === "number") { - if (isNaN(v)) throw makeRuntimeErrorMsg(funcName, `'${argName}' is NaN.`); - return v; - } - throw makeRuntimeErrorMsg(funcName, `'${argName}' should be a number.`); - }; - - const isScriptArgs = (_args: unknown): _args is ScriptArg[] => - Array.isArray(_args) && - _args.every((a) => typeof a === "string" || typeof a === "number" || typeof a === "boolean"); - - const helperScriptArgs = (funcName: string, args: unknown): ScriptArg[] => { - if (!isScriptArgs(args)) throw makeRuntimeErrorMsg(funcName, "'args' is not an array of script args"); - return args as ScriptArg[]; - }; - - const helper: INetscriptHelper = { - updateDynamicRam: updateDynamicRam, - makeRuntimeErrorMsg: makeRuntimeErrorMsg, - string: helperString, - number: helperNumber, - ustring: (funcName: string, argName: string, v: unknown): string | undefined => { - if (v === undefined) return undefined; - return helperString(funcName, argName, v); + return calculatePercentMoneyHacked(server, Player); }, - unumber: (funcName: string, argName: string, v: unknown): number | undefined => { - if (v === undefined) return undefined; - return helperNumber(funcName, argName, v); - }, - scriptArgs: helperScriptArgs, - scriptIdentifier: (funcName: string, _fn: unknown, _hostname: unknown, _args: unknown): ScriptIdentifier => { - if (_fn === undefined) return workerScript.pid; - if (typeof _fn === "number") return _fn; - if (typeof _fn === "string") { - const hostname = _hostname != undefined ? helperString(funcName, "hostname", _hostname) : workerScript.hostname; - if (!isScriptArgs(_args)) throw makeRuntimeErrorMsg(funcName, "'args' is not an array of script args"); - return { - fn: _fn, - hostname: hostname, - args: _args, - }; - } - throw new Error("not implemented"); - }, - boolean: (v: unknown): boolean => { - return !!v; // Just convert it to boolean. - }, - city: (funcName: string, argName: string, v: unknown): CityName => { - if (typeof v !== "string") throw makeRuntimeErrorMsg(funcName, `${argName} should be a city name.`); - const s = v as CityName; - if (!Object.values(CityName).includes(s)) - throw makeRuntimeErrorMsg(funcName, `${argName} should be a city name.`); - return s; - }, - getServer: safeGetServer, - checkSingularityAccess: checkSingularityAccess, - hack: hack, - getValidPort: (funcName: string, port: number): IPort => { - if (isNaN(port)) { - throw makeRuntimeErrorMsg( - funcName, - `Invalid argument. Must be a port number between 1 and ${CONSTANTS.NumNetscriptPorts}, is ${port}`, - ); - } - port = Math.round(port); - if (port < 1 || port > CONSTANTS.NumNetscriptPorts) { - throw makeRuntimeErrorMsg( - funcName, - `Trying to use an invalid port: ${port}. Only ports 1-${CONSTANTS.NumNetscriptPorts} are valid.`, - ); - } - const iport = NetscriptPorts[port - 1]; - if (iport == null || !(iport instanceof Object)) { - throw makeRuntimeErrorMsg(funcName, `Could not find port: ${port}. This is a bug. Report to dev.`); - } - return iport; - }, - player(funcName: string, p: unknown): IPlayer { - const fakePlayer = { - hp: undefined, - mults: undefined, - numPeopleKilled: undefined, - money: undefined, - city: undefined, - location: undefined, - bitNodeN: undefined, - totalPlaytime: undefined, - playtimeSinceLastAug: undefined, - playtimeSinceLastBitnode: undefined, - jobs: undefined, - factions: undefined, - tor: undefined, - inBladeburner: undefined, - hasCorporation: undefined, - entropy: undefined, - }; - if (!roughlyIs(fakePlayer, p)) throw makeRuntimeErrorMsg(funcName, `player should be a Player.`); - return p as IPlayer; - }, - server(funcName: string, s: unknown): Server { - if (!roughlyIs(new Server(), s)) throw makeRuntimeErrorMsg(funcName, `server should be a Server.`); - return s as Server; - }, - gang(funcName: string, g: unknown): FormulaGang { - if (!roughlyIs({ respect: 0, territory: 0, wantedLevel: 0 }, g)) - throw makeRuntimeErrorMsg(funcName, `gang should be a Gang.`); - return g as FormulaGang; - }, - gangMember(funcName: string, m: unknown): GangMember { - if (!roughlyIs(new GangMember(), m)) throw makeRuntimeErrorMsg(funcName, `member should be a GangMember.`); - return m as GangMember; - }, - gangTask(funcName: string, t: unknown): GangMemberTask { - if (!roughlyIs(new GangMemberTask("", "", false, false, {}), t)) - throw makeRuntimeErrorMsg(funcName, `task should be a GangMemberTask.`); - return t as GangMemberTask; - }, - }; - - const singularity = NetscriptSingularity(Player, workerScript); - const base: InternalAPI = { - args: workerScript.args as unknown as any, - enums: { - toast: ToastVariant, - } as unknown as any, - - singularity: singularity, - gang: NetscriptGang(Player, workerScript), - bladeburner: NetscriptBladeburner(Player, workerScript), - codingcontract: NetscriptCodingContract(Player, workerScript), - sleeve: NetscriptSleeve(Player), - corporation: NetscriptCorporation(Player), - stanek: NetscriptStanek(Player, workerScript, helper), - infiltration: NetscriptInfiltration(Player), - ui: NetscriptUserInterface(), - formulas: NetscriptFormulas(Player, helper), - stock: NetscriptStockMarket(Player, workerScript), - grafting: NetscriptGrafting(Player), - hacknet: NetscriptHacknet(Player, workerScript), - sprintf: () => sprintf, - vsprintf: () => vsprintf, - scan: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname): string[] => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - 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); - } - ctx.log(() => `returned ${server.serversOnNetwork.length} connections for ${server.hostname}`); - return out; - }, - hack: - (ctx: NetscriptContext) => - (_hostname: unknown, { threads: requestedThreads, stock }: BasicHGWOptions = {}): Promise => { - const hostname = ctx.helper.string("hostname", _hostname); - return hack(ctx, hostname, false, { threads: requestedThreads, stock: stock }); - }, - hackAnalyzeThreads: - (ctx: NetscriptContext) => - (_hostname: unknown, _hackAmount: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const hackAmount = ctx.helper.number("hackAmount", _hackAmount); - - // Check argument validity - const server = safeGetServer(hostname, ctx); + 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)) { - ctx.log(() => "Cannot be executed on this server."); - return -1; - } - if (isNaN(hackAmount)) { - throw ctx.makeRuntimeErrorMsg( - `Invalid hackAmount argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric.`, - ); - } - - if (hackAmount < 0 || hackAmount > server.moneyAvailable) { - return -1; - } else if (hackAmount === 0) { + helpers.log(ctx, () => "Cannot be executed on this server."); 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 = ctx.helper.string("hostname", _hostname); + return CONSTANTS.ServerFortifyAmount * threads; + }, + hackAnalyzeChance: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = helpers.string(ctx, "hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "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 = ctx.helper.number("threads", _threads); - if (_hostname) { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "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 = ctx.helper.string("hostname", _hostname); - - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 0; - } - - return calculateHackingChance(server, Player); - }, - sleep: - (ctx: NetscriptContext) => - async (_time: unknown = 0): Promise => { - const time = ctx.helper.number("time", _time); - if (time === undefined) { - throw ctx.makeRuntimeErrorMsg("Takes 1 argument."); - } - ctx.log(() => `Sleeping for ${time} milliseconds`); - return netscriptDelay(time, workerScript).then(function () { - return Promise.resolve(true); - }); - }, - asleep: (ctx: NetscriptContext) => - function (_time: unknown = 0): Promise { - const time = ctx.helper.number("time", _time); - ctx.log(() => `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 = ctx.helper.string("hostname", _hostname); - const threads = resolveNetscriptRequestedThreads(ctx, requestedThreads ?? workerScript.scriptRef.threads); - - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "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 ctx.makeRuntimeErrorMsg(canHack.msg || ""); - } - - const growTime = calculateGrowTime(server, Player); - ctx.log( - () => - `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( - growTime * 1000, - true, - )} (t=${numeralWrapper.formatThreads(threads)}).`, - ); - return netscriptDelay(growTime * 1000, workerScript).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; - ctx.log( - () => - `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 = ctx.helper.string("hostname", _hostname); - const growth = ctx.helper.number("growth", _growth); - const cores = ctx.helper.number("cores", _cores); - - // Check argument validity - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 0; - } - if (typeof growth !== "number" || isNaN(growth) || growth < 1 || !isFinite(growth)) { - throw ctx.makeRuntimeErrorMsg(`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 = ctx.helper.number("threads", _threads); - if (_hostname) { - const cores = ctx.helper.number("cores", _cores) || 1; - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - - if (!(server instanceof Server)) { - ctx.log(() => "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 = ctx.helper.string("hostname", _hostname); - const threads = resolveNetscriptRequestedThreads(ctx, requestedThreads ?? workerScript.scriptRef.threads); - if (hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Takes 1 argument."); - } - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "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 ctx.makeRuntimeErrorMsg(canHack.msg || ""); - } - - const weakenTime = calculateWeakenTime(server, Player); - ctx.log( - () => - `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( - weakenTime * 1000, - true, - )} (t=${numeralWrapper.formatThreads(threads)})`, - ); - return netscriptDelay(weakenTime * 1000, workerScript).then(function () { - const host = GetServer(workerScript.hostname); - if (host === null) { - ctx.log(() => "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; - ctx.log( - () => - `'${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 = ctx.helper.number("threads", _threads); - const cores = ctx.helper.number("cores", _cores); - const coreBonus = 1 + (cores - 1) / 16; - return CONSTANTS.ServerWeakenAmount * threads * coreBonus * BitNodeMultipliers.ServerWeakenRate; - }, - share: (ctx: NetscriptContext) => async (): Promise => { - ctx.log(() => "Sharing this computer."); - const end = StartSharing( - workerScript.scriptRef.threads * calculateIntelligenceBonus(Player.skills.intelligence, 2), - ); - return netscriptDelay(10000, workerScript).finally(function () { - ctx.log(() => "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 ctx.makeRuntimeErrorMsg("Takes at least 1 argument."); - } - workerScript.print(argsToString(args)); - }, - printf: - (ctx: NetscriptContext) => - (_format: unknown, ...args: unknown[]): void => { - const format = ctx.helper.string("format", _format); - if (typeof format !== "string") { - throw ctx.makeRuntimeErrorMsg("First argument must be string for the format."); - } - workerScript.print(vsprintf(format, args)); - }, - tprint: - (ctx: NetscriptContext) => - (...args: unknown[]): void => { - if (args.length === 0) { - throw ctx.makeRuntimeErrorMsg("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 = ctx.helper.string("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; + 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(ctx.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; + ctx.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)}).`, + ); + ctx.workerScript.scriptRef.onlineExpGained += expGain; + Player.gainHackingExp(expGain); + if (stock) { + influenceStockThroughServerGrow(server, moneyAfter - moneyBefore); } - 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(); + return Promise.resolve(moneyAfter / moneyBefore); + }); }, - disableLog: - (ctx: NetscriptContext) => - (_fn: unknown): void => { - const fn = ctx.helper.string("fn", _fn); - if (fn === "ALL") { - for (const fn of Object.keys(possibleLogs)) { - workerScript.disableLogs[fn] = true; - } - ctx.log(() => `Disabled logging for all functions`); - } else if (possibleLogs[fn] === undefined) { - throw ctx.makeRuntimeErrorMsg(`Invalid argument: ${fn}.`); - } else { - workerScript.disableLogs[fn] = true; - ctx.log(() => `Disabled logging for ${fn}`); - } - }, - enableLog: - (ctx: NetscriptContext) => - (_fn: unknown): void => { - const fn = ctx.helper.string("fn", _fn); - if (fn === "ALL") { - for (const fn of Object.keys(possibleLogs)) { - delete workerScript.disableLogs[fn]; - } - ctx.log(() => `Enabled logging for all functions`); - } else if (possibleLogs[fn] === undefined) { - throw ctx.makeRuntimeErrorMsg(`Invalid argument: ${fn}.`); - } - delete workerScript.disableLogs[fn]; - ctx.log(() => `Enabled logging for ${fn}`); - }, - isLogEnabled: - (ctx: NetscriptContext) => - (_fn: unknown): boolean => { - const fn = ctx.helper.string("fn", _fn); - if (possibleLogs[fn] === undefined) { - throw ctx.makeRuntimeErrorMsg(`Invalid argument: ${fn}.`); - } - return !workerScript.disableLogs[fn]; - }, - getScriptLogs: - (ctx: NetscriptContext) => - (fn: unknown, hostname: unknown, ...scriptArgs: unknown[]): string[] => { - const ident = ctx.helper.scriptIdentifier(fn, hostname, scriptArgs); - const runningScriptObj = getRunningScript(ctx, ident); - if (runningScriptObj == null) { - ctx.log(() => getCannotFindRunningScriptErrorMessage(ident)); - return []; - } + 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); - return runningScriptObj.logs.slice(); - }, - tail: - (ctx: NetscriptContext) => - (fn: unknown, hostname: unknown, ...scriptArgs: unknown[]): void => { - const ident = ctx.helper.scriptIdentifier(fn, hostname, scriptArgs); - const runningScriptObj = getRunningScript(ctx, ident); - if (runningScriptObj == null) { - ctx.log(() => getCannotFindRunningScriptErrorMessage(ident)); - return; - } + // 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}.`); + } - LogBoxEvents.emit(runningScriptObj); - }, + 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); - closeTail: - (ctx: NetscriptContext) => - (_pid: unknown = workerScript.scriptRef.pid): void => { - const pid = ctx.helper.number("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 = ctx.helper.string("hostname", _hostname); - - const server = safeGetServer(hostname, ctx); if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return false; - } - if (server.hasAdminRights) { - ctx.log(() => `Already have root access to '${server.hostname}'.`); - return true; - } - if (!Player.hasProgram(Programs.NukeProgram.name)) { - throw ctx.makeRuntimeErrorMsg("You do not have the NUKE.exe virus!"); - } - if (server.openPortCount < server.numOpenPortsRequired) { - throw ctx.makeRuntimeErrorMsg("Not enough ports opened to use NUKE.exe virus."); - } - server.hasAdminRights = true; - ctx.log(() => `Executed NUKE.exe virus on '${server.hostname}' to gain root access.`); - return true; - }, - brutessh: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.BruteSSHProgram.name)) { - throw ctx.makeRuntimeErrorMsg("You do not have the BruteSSH.exe program!"); - } - if (!server.sshPortOpen) { - ctx.log(() => `Executed BruteSSH.exe on '${server.hostname}' to open SSH port (22).`); - server.sshPortOpen = true; - ++server.openPortCount; - } else { - ctx.log(() => `SSH Port (22) already opened on '${server.hostname}'.`); - } - return true; - }, - ftpcrack: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = ctx.helper.string("hostname", _hostname); - if (hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Takes 1 argument."); - } - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.FTPCrackProgram.name)) { - throw ctx.makeRuntimeErrorMsg("You do not have the FTPCrack.exe program!"); - } - if (!server.ftpPortOpen) { - ctx.log(() => `Executed FTPCrack.exe on '${server.hostname}' to open FTP port (21).`); - server.ftpPortOpen = true; - ++server.openPortCount; - } else { - ctx.log(() => `FTP Port (21) already opened on '${server.hostname}'.`); - } - return true; - }, - relaysmtp: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = ctx.helper.string("hostname", _hostname); - if (hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Takes 1 argument."); - } - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.RelaySMTPProgram.name)) { - throw ctx.makeRuntimeErrorMsg("You do not have the relaySMTP.exe program!"); - } - if (!server.smtpPortOpen) { - ctx.log(() => `Executed relaySMTP.exe on '${server.hostname}' to open SMTP port (25).`); - server.smtpPortOpen = true; - ++server.openPortCount; - } else { - ctx.log(() => `SMTP Port (25) already opened on '${server.hostname}'.`); - } - return true; - }, - httpworm: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = ctx.helper.string("hostname", _hostname); - if (hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Takes 1 argument"); - } - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.HTTPWormProgram.name)) { - throw ctx.makeRuntimeErrorMsg("You do not have the HTTPWorm.exe program!"); - } - if (!server.httpPortOpen) { - ctx.log(() => `Executed HTTPWorm.exe on '${server.hostname}' to open HTTP port (80).`); - server.httpPortOpen = true; - ++server.openPortCount; - } else { - ctx.log(() => `HTTP Port (80) already opened on '${server.hostname}'.`); - } - return true; - }, - sqlinject: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = ctx.helper.string("hostname", _hostname); - if (hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Takes 1 argument."); - } - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.SQLInjectProgram.name)) { - throw ctx.makeRuntimeErrorMsg("You do not have the SQLInject.exe program!"); - } - if (!server.sqlPortOpen) { - ctx.log(() => `Executed SQLInject.exe on '${server.hostname}' to open SQL port (1433).`); - server.sqlPortOpen = true; - ++server.openPortCount; - } else { - ctx.log(() => `SQL Port (1433) already opened on '${server.hostname}'.`); - } - return true; - }, - run: - (ctx: NetscriptContext) => - (_scriptname: unknown, _threads: unknown = 1, ..._args: unknown[]): number => { - const scriptname = ctx.helper.string("scriptname", _scriptname); - const threads = ctx.helper.number("threads", _threads); - const args = ctx.helper.scriptArgs(_args); - if (scriptname === undefined) { - throw ctx.makeRuntimeErrorMsg("Usage: run(scriptname, [numThreads], [arg1], [arg2]...)"); - } - if (isNaN(threads) || threads <= 0) { - throw ctx.makeRuntimeErrorMsg(`Invalid thread count. Must be numeric and > 0, is ${threads}`); - } - const scriptServer = GetServer(workerScript.hostname); - if (scriptServer == null) { - throw ctx.makeRuntimeErrorMsg("Could not find server. This is a bug. Report to dev."); + helpers.log(ctx, () => "Cannot be executed on this server."); + return 0; } - return runScriptFromScript(Player, "run", scriptServer, scriptname, args, workerScript, threads); - }, - exec: - (ctx: NetscriptContext) => - (_scriptname: unknown, _hostname: unknown, _threads: unknown = 1, ..._args: unknown[]): number => { - const scriptname = ctx.helper.string("scriptname", _scriptname); - const hostname = ctx.helper.string("hostname", _hostname); - const threads = ctx.helper.number("threads", _threads); - const args = ctx.helper.scriptArgs(_args); - if (scriptname === undefined || hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Usage: exec(scriptname, server, [numThreads], [arg1], [arg2]...)"); + 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 ?? 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); } - if (isNaN(threads) || threads <= 0) { - throw ctx.makeRuntimeErrorMsg(`Invalid thread count. Must be numeric and > 0, is ${threads}`); + 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(helpers.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 = helpers.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; } - const server = safeGetServer(hostname, ctx); - return runScriptFromScript(Player, "exec", server, scriptname, args, workerScript, threads); - }, - spawn: - (ctx: NetscriptContext) => - (_scriptname: unknown, _threads: unknown = 1, ..._args: unknown[]): void => { - const scriptname = ctx.helper.string("scriptname", _scriptname); - const threads = ctx.helper.number("threads", _threads); - const args = ctx.helper.scriptArgs(_args); - if (!scriptname || !threads) { - throw ctx.makeRuntimeErrorMsg("Usage: spawn(scriptname, threads)"); - } - - const spawnDelay = 10; - setTimeout(() => { - if (isNaN(threads) || threads <= 0) { - throw ctx.makeRuntimeErrorMsg(`Invalid thread count. Must be numeric and > 0, is ${threads}`); - } - const scriptServer = GetServer(workerScript.hostname); - if (scriptServer == null) { - throw ctx.makeRuntimeErrorMsg("Could not find server. This is a bug. Report to dev"); - } - - return runScriptFromScript(Player, "spawn", scriptServer, scriptname, args, workerScript, threads); - }, spawnDelay * 1e3); - - ctx.log(() => `Will execute '${scriptname}' in ${spawnDelay} seconds`); - - workerScript.running = false; // Prevent workerScript from "finishing execution naturally" - if (killWorkerScript(workerScript)) { - ctx.log(() => "Exiting..."); - } - }, - kill: - (ctx: NetscriptContext) => - (filename: unknown, hostname: unknown, ...scriptArgs: unknown[]): boolean => { - const ident = ctx.helper.scriptIdentifier(filename, hostname, scriptArgs); - let res; - const killByPid = typeof ident === "number"; - if (killByPid) { - // Kill by pid - res = killWorkerScript(ident); - } else { - // Kill by filename/hostname - if (filename === undefined || hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Usage: kill(scriptname, server, [arg1], [arg2]...)"); - } - - const server = safeGetServer(ident.hostname, ctx); - const runningScriptObj = getRunningScriptByArgs(ctx, ident.fn, ident.hostname, ident.args); - if (runningScriptObj == null) { - ctx.log(() => getCannotFindRunningScriptErrorMessage(ident)); - return false; - } - - res = killWorkerScript({ runningScript: runningScriptObj, hostname: server.hostname }); - } - - if (res) { - if (killByPid) { - ctx.log(() => `Killing script with PID ${ident}`); - } else { - ctx.log(() => `Killing '${filename}' on '${hostname}' with args: ${arrayToString(scriptArgs)}.`); - } - return true; - } else { - if (killByPid) { - ctx.log(() => `No script with PID ${ident}`); - } else { - ctx.log(() => `No such script '${filename}' on '${hostname}' with args: ${arrayToString(scriptArgs)}`); - } - return false; - } - }, - killall: - (ctx: NetscriptContext) => - (_hostname: unknown = workerScript.hostname, _safetyguard: unknown = true): boolean => { - const hostname = ctx.helper.string("hostname", _hostname); - const safetyguard = helper.boolean(_safetyguard); - if (hostname === undefined) { - throw ctx.makeRuntimeErrorMsg("Usage: killall(hostname, [safetyguard boolean])"); - } - const server = safeGetServer(hostname, ctx); - - 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(); - ctx.log(() => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`); - - return scriptsKilled > 0; - }, - exit: (ctx: NetscriptContext) => (): void => { - workerScript.running = false; // Prevent workerScript from "finishing execution naturally" - if (killWorkerScript(workerScript)) { - ctx.log(() => "Exiting..."); + helpers.log(ctx, () => `Disabled logging for all functions`); + } else if (possibleLogs[fn] === undefined) { + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${fn}.`); } else { - ctx.log(() => "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 = ctx.helper.string("destination", _destination); - const source = ctx.helper.string("source", _source); - if (Array.isArray(_scriptname)) { - // Recursively call scp on all elements of array - const scripts: string[] = _scriptname; - if (scripts.length === 0) { - throw ctx.makeRuntimeErrorMsg("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 = ctx.helper.string("scriptName", _scriptname); - - // Invalid file type - if (!isValidFilePath(scriptName)) { - throw ctx.makeRuntimeErrorMsg(`Invalid filename: '${scriptName}'`); - } - - // Invalid file name - if (!scriptName.endsWith(".lit") && !isScriptFilename(scriptName) && !scriptName.endsWith("txt")) { - throw ctx.makeRuntimeErrorMsg("Only works for scripts, .lit and .txt files"); - } - - const destServer = safeGetServer(destination, ctx); - const currServ = safeGetServer(source, ctx); - - // 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) { - ctx.log(() => `File '${scriptName}' does not exist.`); - return Promise.resolve(false); - } - - for (let i = 0; i < destServer.messages.length; ++i) { - if (destServer.messages[i] === scriptName) { - ctx.log(() => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); // Already exists - } - } - destServer.messages.push(scriptName); - ctx.log(() => `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) { - ctx.log(() => `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; - ctx.log(() => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); - } - } - const newFile = new TextFile(txtFile.fn, txtFile.text); - destServer.textFiles.push(newFile); - ctx.log(() => `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) { - ctx.log(() => `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) { - ctx.log(() => `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); - ctx.log(() => `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 = ctx.helper.string("hostname", _hostname); - const grep = ctx.helper.string("grep", _grep); - - const server = safeGetServer(hostname, ctx); - - // 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 = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - 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 = ctx.helper.string("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 = helpers.getRunningScript(ctx, ident); + if (runningScriptObj == null) { + helpers.log(ctx, () => helpers.getCannotFindRunningScriptErrorMessage(ident)); + return []; + } - const server = safeGetServer(hostname, ctx); - 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 = helpers.getRunningScript(ctx, ident); + if (runningScriptObj == null) { + helpers.log(ctx, () => helpers.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 ctx.makeRuntimeErrorMsg("Could not find server. This is a bug. Report to dev."); + throw helpers.makeRuntimeErrorMsg(ctx, "Could not find server. This is a bug. Report to dev."); } - return scriptServer.hostname; + + return runScriptFromScript(Player, "run", scriptServer, scriptname, args, ctx.workerScript, threads); }, - getHackingLevel: (ctx: NetscriptContext) => (): number => { - Player.updateSkillLevels(); - ctx.log(() => `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 ctx.makeRuntimeErrorMsg("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 = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - 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 = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 0; - } - if (failOnHacknetServer(server, "getServerMoneyAvailable")) { - return 0; - } - if (server.hostname == "home") { - // Return player's money - ctx.log(() => `returned player's money: ${numeralWrapper.formatMoney(Player.money)}`); - return Player.money; - } - ctx.log(() => `returned ${numeralWrapper.formatMoney(server.moneyAvailable)} for '${server.hostname}'`); - return server.moneyAvailable; - }, - getServerSecurityLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerSecurityLevel")) { - return 1; - } - ctx.log( - () => `returned ${numeralWrapper.formatServerSecurity(server.hackDifficulty)} for '${server.hostname}'`, - ); - return server.hackDifficulty; - }, - getServerBaseSecurityLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - ctx.log(() => `getServerBaseSecurityLevel is deprecated because it's not useful.`); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerBaseSecurityLevel")) { - return 1; - } - ctx.log( - () => `returned ${numeralWrapper.formatServerSecurity(server.baseDifficulty)} for '${server.hostname}'`, - ); - return server.baseDifficulty; - }, - getServerMinSecurityLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerMinSecurityLevel")) { - return 1; - } - ctx.log(() => `returned ${numeralWrapper.formatServerSecurity(server.minDifficulty)} for ${server.hostname}`); - return server.minDifficulty; - }, - getServerRequiredHackingLevel: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerRequiredHackingLevel")) { - return 1; - } - ctx.log(() => `returned ${numeralWrapper.formatSkill(server.requiredHackingSkill)} for '${server.hostname}'`); - return server.requiredHackingSkill; - }, - getServerMaxMoney: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 0; - } - if (failOnHacknetServer(server, "getServerMaxMoney")) { - return 0; - } - ctx.log(() => `returned ${numeralWrapper.formatMoney(server.moneyMax)} for '${server.hostname}'`); - return server.moneyMax; - }, - getServerGrowth: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerGrowth")) { - return 1; - } - ctx.log(() => `returned ${server.serverGrowth} for '${server.hostname}'`); - return server.serverGrowth; - }, - getServerNumPortsRequired: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "Cannot be executed on this server."); - return 5; - } - if (failOnHacknetServer(server, "getServerNumPortsRequired")) { - return 5; - } - ctx.log(() => `returned ${server.numOpenPortsRequired} for '${server.hostname}'`); - return server.numOpenPortsRequired; - }, - getServerRam: - (ctx: NetscriptContext) => - (_hostname: unknown): [number, number] => { - const hostname = ctx.helper.string("hostname", _hostname); - ctx.log(() => `getServerRam is deprecated in favor of getServerMaxRam / getServerUsedRam`); - const server = safeGetServer(hostname, ctx); - ctx.log( - () => `returned [${numeralWrapper.formatRAM(server.maxRam)}, ${numeralWrapper.formatRAM(server.ramUsed)}]`, - ); - return [server.maxRam, server.ramUsed]; - }, - getServerMaxRam: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - ctx.log(() => `returned ${numeralWrapper.formatRAM(server.maxRam)}`); - return server.maxRam; - }, - getServerUsedRam: - (ctx: NetscriptContext) => - (_hostname: unknown): number => { - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - ctx.log(() => `returned ${numeralWrapper.formatRAM(server.ramUsed)}`); - return server.ramUsed; - }, - serverExists: - (ctx: NetscriptContext) => - (_hostname: unknown): boolean => { - const hostname = ctx.helper.string("hostname", _hostname); - return GetServer(hostname) !== null; - }, - fileExists: - (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): boolean => { - const filename = ctx.helper.string("filename", _filename); - const hostname = ctx.helper.string("hostname", _hostname); - if (filename === undefined) { - throw ctx.makeRuntimeErrorMsg("Usage: fileExists(scriptname, [server])"); - } - const server = safeGetServer(hostname, ctx); - 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 = ctx.helper.scriptIdentifier(fn, hostname, scriptArgs); - return getRunningScript(ctx, ident) !== null; - }, - getPurchasedServerLimit: () => (): number => { - return getPurchaseServerLimit(); - }, - getPurchasedServerMaxRam: () => (): number => { - return getPurchaseServerMaxRam(); - }, - getPurchasedServerCost: - (ctx: NetscriptContext) => - (_ram: unknown): number => { - const ram = ctx.helper.number("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) { - ctx.log(() => `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 = ctx.helper.string("name", _name); - const ram = ctx.helper.number("ram", _ram); - let hostnameStr = String(name); - hostnameStr = hostnameStr.replace(/\s+/g, ""); - if (hostnameStr == "") { - ctx.log(() => `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()) { - ctx.log( - () => - `You have reached the maximum limit of ${getPurchaseServerLimit()} servers. You cannot purchase any more.`, + const server = helpers.getServer(ctx, ident.hostname); + const runningScriptObj = helpers.getRunningScriptByArgs(ctx, ident.scriptname, ident.hostname, ident.args); + if (runningScriptObj == null) { + helpers.log(ctx, () => helpers.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 ""; } + 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()) { - ctx.log(() => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`); - } else { - ctx.log(() => `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) { - ctx.log(() => `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"); - ctx.log( - () => `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 = ctx.helper.string("name", _name); - let hostnameStr = String(name); - hostnameStr = hostnameStr.replace(/\s\s+/g, ""); - const server = GetServer(hostnameStr); - if (!(server instanceof Server)) { - ctx.log(() => `Invalid argument: hostname='${hostnameStr}'`); - return false; - } + return Promise.resolve(res); + } - if (!server.purchasedByPlayer || server.hostname === "home") { - ctx.log(() => "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) { - ctx.log(() => "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) { - ctx.log(() => "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) { - ctx.log(() => `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) { - ctx.log(() => `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); - ctx.log(() => `Deleted server '${hostnameStr}`); - return true; - } - } - // Wasn't found on home computer - ctx.log(() => `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 = ctx.helper.number("port", _port); - if (typeof data !== "string" && typeof data !== "number") { - throw ctx.makeRuntimeErrorMsg(`Trying to write invalid data to a port: only strings and numbers are valid.`); - } - const iport = helper.getValidPort("writePort", port); - return Promise.resolve(iport.write(data)); - }, - write: - (ctx: NetscriptContext) => - (_port: unknown, data: unknown = "", _mode: unknown = "a"): Promise => { - const port = ctx.helper.string("port", _port); - const mode = ctx.helper.string("mode", _mode); - if (isString(port)) { - // Write to script or text file - let fn = port; - if (!isValidFilePath(fn)) { - throw ctx.makeRuntimeErrorMsg(`Invalid filepath: ${fn}`); - } - - if (fn.lastIndexOf("/") === 0) { - fn = removeLeadingSlash(fn); - } - - // Coerce 'data' to be a string - try { - data = String(data); - } catch (e: unknown) { - throw ctx.makeRuntimeErrorMsg( - `Invalid data (${String(e)}). Data being written must be convertible to a string`, - ); - } - - const server = workerScript.getServer(); - if (server == null) { - throw ctx.makeRuntimeErrorMsg("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 ctx.makeRuntimeErrorMsg(`Invalid argument: ${port}`); - } - }, - tryWritePort: - (ctx: NetscriptContext) => - (_port: unknown, data: unknown = ""): Promise => { - let port = ctx.helper.number("port", _port); - if (typeof data !== "string" && typeof data !== "number") { - throw makeRuntimeErrorMsg( - "tryWritePort", - `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 makeRuntimeErrorMsg( - "tryWritePort", - `Invalid port: ${port}. Only ports 1-${CONSTANTS.NumNetscriptPorts} are valid.`, - ); - } - const iport = NetscriptPorts[port - 1]; - if (iport == null || !(iport instanceof Object)) { - throw ctx.makeRuntimeErrorMsg(`Could not find port: ${port}. This is a bug. Report to dev.`); - } - return Promise.resolve(iport.tryWrite(data)); - } else { - throw ctx.makeRuntimeErrorMsg(`Invalid argument: ${port}`); - } - }, - readPort: - (ctx: NetscriptContext) => - (_port: unknown): unknown => { - const port = ctx.helper.number("port", _port); - // Read from port - const iport = helper.getValidPort("readPort", port); - const x = iport.read(); - return x; - }, - read: - (ctx: NetscriptContext) => - (_port: unknown): string => { - const port = ctx.helper.string("port", _port); - if (isString(port)) { - // Read from script or text file - const fn = port; - const server = GetServer(workerScript.hostname); - if (server == null) { - throw ctx.makeRuntimeErrorMsg("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 ctx.makeRuntimeErrorMsg(`Invalid argument: ${port}`); - } - }, - peek: - (ctx: NetscriptContext) => - (_port: unknown): unknown => { - const port = ctx.helper.number("port", _port); - const iport = helper.getValidPort("peek", port); - const x = iport.peek(); - return x; - }, - clear: - (ctx: NetscriptContext) => - (_file: unknown): void => { - const file = ctx.helper.string("file", _file); - if (isString(file)) { - // Clear text file - const fn = file; - const server = GetServer(workerScript.hostname); - if (server == null) { - throw ctx.makeRuntimeErrorMsg("Error getting Server. This is a bug. Report to dev."); - } - const txtFile = getTextFile(fn, server); - if (txtFile != null) { - txtFile.write(""); - } - } else { - throw ctx.makeRuntimeErrorMsg(`Invalid argument: ${file}`); - } - }, - clearPort: - (ctx: NetscriptContext) => - (_port: unknown): void => { - const port = ctx.helper.number("port", _port); - // Clear port - const iport = helper.getValidPort("clearPort", port); - iport.clear(); - }, - getPortHandle: - (ctx: NetscriptContext) => - (_port: unknown): IPort => { - const port = ctx.helper.number("port", _port); - const iport = helper.getValidPort("getPortHandle", port); - return iport; - }, - rm: - (ctx: NetscriptContext) => - (_fn: unknown, _hostname: unknown = workerScript.hostname): boolean => { - const fn = ctx.helper.string("fn", _fn); - const hostname = ctx.helper.string("hostname", _hostname); - const s = safeGetServer(hostname, ctx); - - const status = s.removeFile(fn); - if (!status.res) { - ctx.log(() => status.msg + ""); - } - - return status.res; - }, - scriptRunning: - (ctx: NetscriptContext) => - (_scriptname: unknown, _hostname: unknown): boolean => { - const scriptname = ctx.helper.string("scriptname", _scriptname); - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - 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 = ctx.helper.string("scriptname", _scriptname); - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - 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 = ctx.helper.string("scriptname", _scriptname); - const hostname = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - 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 = ctx.helper.scriptIdentifier(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 = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "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 = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "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 = ctx.helper.string("hostname", _hostname); - const server = safeGetServer(hostname, ctx); - if (!(server instanceof Server)) { - ctx.log(() => "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 = ctx.helper.scriptIdentifier(fn, hostname, args); - const runningScript = getRunningScript(ctx, ident); - if (runningScript == null) { - ctx.log(() => 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 = ctx.helper.scriptIdentifier(fn, hostname, args); - const runningScript = getRunningScript(ctx, ident); - if (runningScript == null) { - ctx.log(() => getCannotFindRunningScriptErrorMessage(ident)); - return -1; - } - return runningScript.onlineExpGained / runningScript.onlineRunningTime; - }, - nFormat: - (ctx: NetscriptContext) => - (_n: unknown, _format: unknown): string => { - const n = ctx.helper.number("n", _n); - const format = ctx.helper.string("format", _format); - if (isNaN(n)) { - return ""; - } - - return numeralWrapper.format(n, format); - }, - tFormat: - (ctx: NetscriptContext) => - (_milliseconds: unknown, _milliPrecision: unknown = false): string => { - const milliseconds = ctx.helper.number("milliseconds", _milliseconds); - const milliPrecision = helper.boolean(_milliPrecision); - return convertTimeMsToTimeElapsedString(milliseconds, milliPrecision); - }, - getTimeSinceLastAug: () => (): number => { - return Player.playtimeSinceLastAug; - }, - alert: - (ctx: NetscriptContext) => - (_message: unknown): void => { - const message = ctx.helper.string("message", _message); - dialogBoxCreate(message); - }, - toast: - (ctx: NetscriptContext) => - (_message: unknown, _variant: unknown = ToastVariant.SUCCESS, _duration: unknown = 2000): void => { - const message = ctx.helper.string("message", _message); - const variant = ctx.helper.string("variant", _variant); - const duration = ctx.helper.number("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 = ctx.helper.string("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 = ctx.helper.string("url", _url); - const target = ctx.helper.string("target", _target); - const hostname = ctx.helper.string("hostname", _hostname); - if (!isScriptFilename(target) && !target.endsWith(".txt")) { - ctx.log(() => `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 = safeGetServer(hostname, ctx); - 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) { - ctx.log(() => "Failed."); - return resolve(false); - } - if (res.overwritten) { - ctx.log(() => `Successfully retrieved content and overwrote '${target}' on '${hostname}'`); - return resolve(true); - } - ctx.log(() => `Successfully retrieved content to new file '${target}' on '${hostname}'`); - return resolve(true); - }, - "text", - ).fail(function (e) { - ctx.log(() => 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 ctx.makeRuntimeErrorMsg("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 = ctx.helper.string("host", _host); - const source = ctx.helper.string("source", _source); - const destination = ctx.helper.string("destination", _destination); + destServer.messages.push(scriptName); + helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); + return Promise.resolve(true); + } - if (!isValidFilePath(source)) throw ctx.makeRuntimeErrorMsg(`Invalid filename: '${source}'`); - if (!isValidFilePath(destination)) throw ctx.makeRuntimeErrorMsg(`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 ctx.makeRuntimeErrorMsg(`'mv' can only be used on scripts and text files (.txt)`); - if (source_is_txt != dest_is_txt) - throw ctx.makeRuntimeErrorMsg(`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); - // This will throw if the server is not found, we do not need to validate result. - const destServer: BaseServer | null = safeGetServer(host, ctx); + const server = helpers.getServer(ctx, hostname); - if (!source_is_txt && destServer.isRunning(source)) - throw ctx.makeRuntimeErrorMsg(`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 ctx.makeRuntimeErrorMsg(`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, + ...helpers.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 (helpers.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 (helpers.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 (helpers.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 (helpers.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 (helpers.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 (helpers.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 (helpers.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 (helpers.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 helpers.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(helper, {}, 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 = helpers.getRunningScript(ctx, ident); + if (runningScript === null) return null; + return helpers.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 (helpers.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 (helpers.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 (helpers.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 = helpers.getRunningScript(ctx, ident); + if (runningScript == null) { + helpers.log(ctx, () => helpers.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 = helpers.getRunningScript(ctx, ident); + if (runningScript == null) { + helpers.log(ctx, () => helpers.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([...helpers.getFunctionNames(ns, "")].map((a) => [a, true])); diff --git a/src/NetscriptFunctions/Bladeburner.ts b/src/NetscriptFunctions/Bladeburner.ts index d0ddc1677..efc5eaf23 100644 --- a/src/NetscriptFunctions/Bladeburner.ts +++ b/src/NetscriptFunctions/Bladeburner.ts @@ -1,13 +1,13 @@ -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"; import { IAction } from "src/Bladeburner/IAction"; 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"); @@ -18,13 +18,13 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript }); if (!apiAccess) { const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`; - throw ctx.makeRuntimeErrorMsg(apiDenied); + throw helpers.makeRuntimeErrorMsg(ctx, apiDenied); } if (!skipjoined) { const bladeburnerAccess = bladeburner instanceof Bladeburner; if (!bladeburnerAccess) { const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`; - throw ctx.makeRuntimeErrorMsg(bladeburnerDenied); + throw helpers.makeRuntimeErrorMsg(ctx, bladeburnerDenied); } } }; @@ -33,7 +33,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Must have joined bladeburner"); if (!bladeburner.cities.hasOwnProperty(city)) { - throw ctx.makeRuntimeErrorMsg(`Invalid city: ${city}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city: ${city}`); } }; @@ -42,11 +42,11 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript if (bladeburner === null) throw new Error("Must have joined bladeburner"); const actionId = bladeburner.getActionIdFromTypeAndName(type, name); if (!actionId) { - throw ctx.makeRuntimeErrorMsg(`Invalid action type='${type}', name='${name}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid action type='${type}', name='${name}'`); } const actionObj = bladeburner.getActionObject(actionId); if (!actionObj) { - throw ctx.makeRuntimeErrorMsg(`Invalid action type='${type}', name='${name}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid action type='${type}', name='${name}'`); } return actionObj; @@ -74,7 +74,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getBlackOpRank: (ctx: NetscriptContext) => (_blackOpName: unknown): number => { - const blackOpName = ctx.helper.string("blackOpName", _blackOpName); + const blackOpName = helpers.string(ctx, "blackOpName", _blackOpName); checkBladeburnerAccess(ctx); const action = getBladeburnerActionObject(ctx, "blackops", blackOpName); if (!(action instanceof BlackOperation)) throw new Error("action was not a black operation"); @@ -95,15 +95,15 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript startAction: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): boolean => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); 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 ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, stopBladeburnerAction: (ctx: NetscriptContext) => (): void => { @@ -121,8 +121,8 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getActionTime: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): number => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); @@ -130,13 +130,13 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const time = bladeburner.getActionTimeNetscriptFn(player, type, name); if (typeof time === "string") { const errorLogText = `Invalid action: type='${type}' name='${name}'`; - ctx.log(() => errorLogText); + helpers.log(ctx, () => errorLogText); return -1; } else { return time; } } catch (e: unknown) { - throw ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, getActionCurrentTime: (ctx: NetscriptContext) => (): number => { @@ -149,14 +149,14 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript 1000; return timecomputed; } catch (e: unknown) { - throw ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, getActionEstimatedSuccessChance: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): [number, number] => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); @@ -164,21 +164,21 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript const chance = bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name); if (typeof chance === "string") { const errorLogText = `Invalid action: type='${type}' name='${name}'`; - ctx.log(() => errorLogText); + helpers.log(ctx, () => errorLogText); return [-1, -1]; } else { return chance; } } catch (e: unknown) { - throw ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, getActionRepGain: (ctx: NetscriptContext) => (_type: unknown, _name: unknown, _level: unknown): number => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); - const level = ctx.helper.number("level", _level); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); + const level = helpers.number(ctx, "level", _level); checkBladeburnerAccess(ctx); const action = getBladeburnerActionObject(ctx, type, name); let rewardMultiplier; @@ -193,22 +193,22 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getActionCountRemaining: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): number => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); 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 ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, getActionMaxLevel: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): number => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); const action = getBladeburnerActionObject(ctx, type, name); return action.maxLevel; @@ -216,8 +216,8 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getActionCurrentLevel: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): number => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); const action = getBladeburnerActionObject(ctx, type, name); return action.level; @@ -225,8 +225,8 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getActionAutolevel: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): boolean => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); const action = getBladeburnerActionObject(ctx, type, name); return action.autoLevel; @@ -234,9 +234,9 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript setActionAutolevel: (ctx: NetscriptContext) => (_type: unknown, _name: unknown, _autoLevel: unknown = true): void => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); - const autoLevel = ctx.helper.boolean(_autoLevel); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); + const autoLevel = !!_autoLevel; checkBladeburnerAccess(ctx); const action = getBladeburnerActionObject(ctx, type, name); action.autoLevel = autoLevel; @@ -244,13 +244,13 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript setActionLevel: (ctx: NetscriptContext) => (_type: unknown, _name: unknown, _level: unknown = 1): void => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); - const level = ctx.helper.number("level", _level); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); + const level = helpers.number(ctx, "level", _level); checkBladeburnerAccess(ctx); const action = getBladeburnerActionObject(ctx, type, name); if (level < 1 || level > action.maxLevel) { - ctx.helper.makeRuntimeErrorMsg(`Level must be between 1 and ${action.maxLevel}, is ${level}`); + helpers.makeRuntimeErrorMsg(ctx, `Level must be between 1 and ${action.maxLevel}, is ${level}`); } action.level = level; }, @@ -269,77 +269,77 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getSkillLevel: (ctx: NetscriptContext) => (_skillName: unknown): number => { - const skillName = ctx.helper.string("skillName", _skillName); + const skillName = helpers.string(ctx, "skillName", _skillName); checkBladeburnerAccess(ctx); 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 ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, getSkillUpgradeCost: (ctx: NetscriptContext) => (_skillName: unknown, _count: unknown = 1): number => { - const skillName = ctx.helper.string("skillName", _skillName); - const count = ctx.helper.number("count", _count); + const skillName = helpers.string(ctx, "skillName", _skillName); + const count = helpers.number(ctx, "count", _count); checkBladeburnerAccess(ctx); 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 ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, upgradeSkill: (ctx: NetscriptContext) => (_skillName: unknown, _count: unknown = 1): boolean => { - const skillName = ctx.helper.string("skillName", _skillName); - const count = ctx.helper.number("count", _count); + const skillName = helpers.string(ctx, "skillName", _skillName); + const count = helpers.number(ctx, "count", _count); checkBladeburnerAccess(ctx); 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 ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, getTeamSize: (ctx: NetscriptContext) => (_type: unknown, _name: unknown): number => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); checkBladeburnerAccess(ctx); 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 ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, setTeamSize: (ctx: NetscriptContext) => (_type: unknown, _name: unknown, _size: unknown): number => { - const type = ctx.helper.string("type", _type); - const name = ctx.helper.string("name", _name); - const size = ctx.helper.number("size", _size); + const type = helpers.string(ctx, "type", _type); + const name = helpers.string(ctx, "name", _name); + const size = helpers.number(ctx, "size", _size); checkBladeburnerAccess(ctx); 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 ctx.makeRuntimeErrorMsg(String(e)); + throw helpers.makeRuntimeErrorMsg(ctx, String(e)); } }, getCityEstimatedPopulation: (ctx: NetscriptContext) => (_cityName: unknown): number => { - const cityName = ctx.helper.string("cityName", _cityName); + const cityName = helpers.string(ctx, "cityName", _cityName); checkBladeburnerAccess(ctx); checkBladeburnerCity(ctx, cityName); const bladeburner = player.bladeburner; @@ -349,7 +349,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getCityCommunities: (ctx: NetscriptContext) => (_cityName: unknown): number => { - const cityName = ctx.helper.string("cityName", _cityName); + const cityName = helpers.string(ctx, "cityName", _cityName); checkBladeburnerAccess(ctx); checkBladeburnerCity(ctx, cityName); const bladeburner = player.bladeburner; @@ -359,7 +359,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript getCityChaos: (ctx: NetscriptContext) => (_cityName: unknown): number => { - const cityName = ctx.helper.string("cityName", _cityName); + const cityName = helpers.string(ctx, "cityName", _cityName); checkBladeburnerAccess(ctx); checkBladeburnerCity(ctx, cityName); const bladeburner = player.bladeburner; @@ -375,7 +375,7 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript switchCity: (ctx: NetscriptContext) => (_cityName: unknown): boolean => { - const cityName = ctx.helper.string("cityName", _cityName); + const cityName = helpers.string(ctx, "cityName", _cityName); checkBladeburnerAccess(ctx); checkBladeburnerCity(ctx, cityName); const bladeburner = player.bladeburner; @@ -393,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) { @@ -409,11 +409,11 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript player.skills.agility >= 100 ) { player.bladeburner = new Bladeburner(player); - ctx.log(() => "You have been accepted into the Bladeburner division"); + helpers.log(ctx, () => "You have been accepted into the Bladeburner division"); return true; } else { - ctx.log(() => "You do not meet the requirements for joining the Bladeburner division"); + helpers.log(ctx, () => "You do not meet the requirements for joining the Bladeburner division"); return false; } } diff --git a/src/NetscriptFunctions/CodingContract.ts b/src/NetscriptFunctions/CodingContract.ts index 77afb7f22..785f0e9f8 100644 --- a/src/NetscriptFunctions/CodingContract.ts +++ b/src/NetscriptFunctions/CodingContract.ts @@ -1,21 +1,21 @@ -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 "src/Netscript/APIWrapper"; +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, hostname: string, filename: string, ): CodingContract { - const server = ctx.helper.getServer(hostname); + const server = helpers.getServer(ctx, hostname); const contract = server.getContract(filename); if (contract == null) { - throw ctx.makeRuntimeErrorMsg(`Cannot find contract '${filename}' on server '${hostname}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Cannot find contract '${filename}' on server '${hostname}'`); } return contract; @@ -27,11 +27,11 @@ 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 = ctx.helper.string("filename", _filename); - const hostname = ctx.helper.string("hostname", _hostname); + const filename = helpers.string(ctx, "filename", _filename); + const hostname = helpers.string(ctx, "hostname", _hostname); const contract = getCodingContract(ctx, "attempt", hostname, filename); // Convert answer to string. If the answer is a 2D array, then we have to @@ -51,19 +51,20 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr const creward = contract.reward; if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward"); - const serv = ctx.helper.getServer(hostname); + const serv = helpers.getServer(ctx, hostname); if (contract.isSolution(answerStr)) { const reward = player.gainCodingContractReward(creward, contract.getDifficulty()); - ctx.log(() => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`); + helpers.log(ctx, () => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`); serv.removeContract(filename); return returnReward ? reward : true; } else { ++contract.tries; if (contract.tries >= contract.getMaxNumTries()) { - ctx.log(() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`); + helpers.log(ctx, () => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`); serv.removeContract(filename); } else { - ctx.log( + helpers.log( + ctx, () => `Coding Contract attempt '${filename}' failed. ${ contract.getMaxNumTries() - contract.tries @@ -76,17 +77,17 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr }, getContractType: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): string => { - const filename = ctx.helper.string("filename", _filename); - const hostname = ctx.helper.string("hostname", _hostname); + (_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); return contract.getType(); }, getData: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): unknown => { - const filename = ctx.helper.string("filename", _filename); - const hostname = ctx.helper.string("hostname", _hostname); + (_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); const data = contract.getData(); if (Array.isArray(data)) { @@ -107,17 +108,17 @@ export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScr }, getDescription: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): string => { - const filename = ctx.helper.string("filename", _filename); - const hostname = ctx.helper.string("hostname", _hostname); + (_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); return contract.getDescription(); }, getNumTriesRemaining: (ctx: NetscriptContext) => - (_filename: unknown, _hostname: unknown = workerScript.hostname): number => { - const filename = ctx.helper.string("filename", _filename); - const hostname = ctx.helper.string("hostname", _hostname); + (_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); return contract.getMaxNumTries() - contract.tries; }, diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index f154c4025..1b495484f 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"; @@ -65,9 +65,10 @@ import { CorporationConstants } from "../Corporation/data/Constants"; import { ResearchMap } from "../Corporation/ResearchMap"; import { Factions } from "../Faction/Factions"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; -import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper"; +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; @@ -101,7 +102,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI upgrade.name === upgradeName); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); @@ -110,7 +111,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI upgrade.name === upgradeName); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); @@ -274,10 +275,11 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _amt: unknown = 1): number => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const amt = ctx.helper.number("amount", _amt); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const amt = helpers.number(ctx, "amount", _amt); if (amt < 1) { - throw ctx.makeRuntimeErrorMsg("You must provide a positive number"); + throw helpers.makeRuntimeErrorMsg(ctx, "You must provide a positive number"); } const warehouse = getWarehouse(divisionName, cityName); return UpgradeWarehouseCost(warehouse, amt); @@ -327,8 +329,8 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown): boolean => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); const division = getDivision(divisionName); if (!(cityName in division.warehouses)) throw new Error(`Invalid city name '${cityName}'`); const warehouse = division.warehouses[cityName]; @@ -338,8 +340,8 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown): NSWarehouse => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); const warehouse = getWarehouse(divisionName, cityName); return { level: warehouse.level, @@ -353,9 +355,9 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _materialName: unknown): NSMaterial => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); const material = getMaterial(divisionName, cityName, materialName); const corporation = getCorporation(); return { @@ -374,8 +376,8 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _productName: unknown): NSProduct => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const productName = ctx.helper.string("productName", _productName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const productName = helpers.string(ctx, "productName", _productName); const product = getProduct(divisionName, productName); const corporation = getCorporation(); return { @@ -401,8 +403,8 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); const corporation = getCorporation(); PurchaseWarehouse(corporation, getDivision(divisionName), cityName); }, @@ -410,12 +412,12 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _amt: unknown = 1): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const amt = ctx.helper.number("amount", _amt); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const amt = helpers.number(ctx, "amount", _amt); const corporation = getCorporation(); if (amt < 1) { - throw ctx.makeRuntimeErrorMsg("You must provide a positive number"); + throw helpers.makeRuntimeErrorMsg(ctx, "You must provide a positive number"); } UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName), amt); }, @@ -423,11 +425,11 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _materialName: unknown, _amt: unknown, _price: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); - const amt = ctx.helper.string("amt", _amt); - const price = ctx.helper.string("price", _price); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); + const amt = helpers.string(ctx, "amt", _amt); + const price = helpers.string(ctx, "price", _price); const material = getMaterial(divisionName, cityName, materialName); SellMaterial(material, amt, price); }, @@ -442,12 +444,12 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const productName = ctx.helper.string("productName", _productName); - const amt = ctx.helper.string("amt", _amt); - const price = ctx.helper.string("price", _price); - const all = ctx.helper.boolean(_all); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const productName = helpers.string(ctx, "productName", _productName); + const amt = helpers.string(ctx, "amt", _amt); + const price = helpers.string(ctx, "price", _price); + const all = !!_all; const product = getProduct(divisionName, productName); SellProduct(product, cityName, amt, price, all); }, @@ -455,44 +457,44 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _productName: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const productName = ctx.helper.string("productName", _productName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const productName = helpers.string(ctx, "productName", _productName); getDivision(divisionName).discontinueProduct(getProduct(divisionName, productName)); }, setSmartSupply: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown, _enabled: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const enabled = ctx.helper.boolean(_enabled); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const enabled = !!_enabled; const warehouse = getWarehouse(divisionName, cityName); if (!hasUnlockUpgrade("Smart Supply")) - throw ctx.makeRuntimeErrorMsg(`You have not purchased the Smart Supply upgrade!`); + throw helpers.makeRuntimeErrorMsg(ctx, `You have not purchased the Smart Supply upgrade!`); SetSmartSupply(warehouse, enabled); }, setSmartSupplyUseLeftovers: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown, _materialName: unknown, _enabled: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); - const enabled = ctx.helper.boolean(_enabled); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); + const enabled = !!_enabled; const warehouse = getWarehouse(divisionName, cityName); const material = getMaterial(divisionName, cityName, materialName); if (!hasUnlockUpgrade("Smart Supply")) - throw ctx.makeRuntimeErrorMsg(`You have not purchased the Smart Supply upgrade!`); + throw helpers.makeRuntimeErrorMsg(ctx, `You have not purchased the Smart Supply upgrade!`); SetSmartSupplyUseLeftovers(warehouse, material, enabled); }, buyMaterial: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown, _materialName: unknown, _amt: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); - const amt = ctx.helper.number("amt", _amt); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); + const amt = helpers.number(ctx, "amt", _amt); if (amt < 0) throw new Error("Invalid value for amount field! Must be numeric and greater than 0"); const material = getMaterial(divisionName, cityName, materialName); BuyMaterial(material, amt); @@ -501,13 +503,13 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _materialName: unknown, _amt: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); if (!hasResearched(getDivision(divisionName), "Bulk Purchasing")) throw new Error(`You have not researched Bulk Purchasing in ${divisionName}`); const corporation = getCorporation(); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); - const amt = ctx.helper.number("amt", _amt); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); + const amt = helpers.number(ctx, "amt", _amt); const warehouse = getWarehouse(divisionName, cityName); const material = getMaterial(divisionName, cityName, materialName); BulkPurchase(corporation, warehouse, material, amt); @@ -522,11 +524,11 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const productName = ctx.helper.string("productName", _productName); - const designInvest = ctx.helper.number("designInvest", _designInvest); - const marketingInvest = ctx.helper.number("marketingInvest", _marketingInvest); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const productName = helpers.string(ctx, "productName", _productName); + const designInvest = helpers.number(ctx, "designInvest", _designInvest); + const marketingInvest = helpers.number(ctx, "marketingInvest", _marketingInvest); const corporation = getCorporation(); MakeProduct(corporation, getDivision(divisionName), cityName, productName, designInvest, marketingInvest); }, @@ -534,10 +536,10 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _productName: unknown, _cityName: unknown, _qty: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const productName = ctx.helper.string("productName", _productName); - const qty = ctx.helper.number("qty", _qty); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const productName = helpers.string(ctx, "productName", _productName); + const qty = helpers.number(ctx, "qty", _qty); LimitProductProduction(getProduct(divisionName, productName), cityName, qty); }, exportMaterial: @@ -551,12 +553,12 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI { checkAccess(ctx, 7); - const sourceDivision = ctx.helper.string("sourceDivision", _sourceDivision); - const sourceCity = ctx.helper.string("sourceCity", _sourceCity); - const targetDivision = ctx.helper.string("targetDivision", _targetDivision); - const targetCity = ctx.helper.string("targetCity", _targetCity); - const materialName = ctx.helper.string("materialName", _materialName); - const amt = ctx.helper.string("amt", _amt); + const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision); + const sourceCity = helpers.string(ctx, "sourceCity", _sourceCity); + const targetDivision = helpers.string(ctx, "targetDivision", _targetDivision); + const targetCity = helpers.string(ctx, "targetCity", _targetCity); + const materialName = helpers.string(ctx, "materialName", _materialName); + const amt = helpers.string(ctx, "amt", _amt); ExportMaterial( targetDivision, targetCity, @@ -576,12 +578,12 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI { checkAccess(ctx, 7); - const sourceDivision = ctx.helper.string("sourceDivision", _sourceDivision); - const sourceCity = ctx.helper.string("sourceCity", _sourceCity); - const targetDivision = ctx.helper.string("targetDivision", _targetDivision); - const targetCity = ctx.helper.string("targetCity", _targetCity); - const materialName = ctx.helper.string("materialName", _materialName); - const amt = ctx.helper.string("amt", _amt); + const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision); + const sourceCity = helpers.string(ctx, "sourceCity", _sourceCity); + const targetDivision = helpers.string(ctx, "targetDivision", _targetDivision); + const targetCity = helpers.string(ctx, "targetCity", _targetCity); + const materialName = helpers.string(ctx, "materialName", _materialName); + const amt = helpers.string(ctx, "amt", _amt); CancelExportMaterial( targetDivision, targetCity, @@ -593,56 +595,56 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _materialName: unknown, _qty: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); - const qty = ctx.helper.number("qty", _qty); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); + const qty = helpers.number(ctx, "qty", _qty); LimitMaterialProduction(getMaterial(divisionName, cityName, materialName), qty); }, setMaterialMarketTA1: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown, _materialName: unknown, _on: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); - const on = ctx.helper.boolean(_on); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); + const on = !!_on; if (!getDivision(divisionName).hasResearch("Market-TA.I")) - throw ctx.makeRuntimeErrorMsg(`You have not researched MarketTA.I for division: ${divisionName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.I for division: ${divisionName}`); SetMaterialMarketTA1(getMaterial(divisionName, cityName, materialName), on); }, setMaterialMarketTA2: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown, _materialName: unknown, _on: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const materialName = ctx.helper.string("materialName", _materialName); - const on = ctx.helper.boolean(_on); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const materialName = helpers.string(ctx, "materialName", _materialName); + const on = !!_on; if (!getDivision(divisionName).hasResearch("Market-TA.II")) - throw ctx.makeRuntimeErrorMsg(`You have not researched MarketTA.II for division: ${divisionName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.II for division: ${divisionName}`); SetMaterialMarketTA2(getMaterial(divisionName, cityName, materialName), on); }, setProductMarketTA1: (ctx: NetscriptContext) => (_divisionName: unknown, _productName: unknown, _on: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const productName = ctx.helper.string("productName", _productName); - const on = ctx.helper.boolean(_on); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const productName = helpers.string(ctx, "productName", _productName); + const on = !!_on; if (!getDivision(divisionName).hasResearch("Market-TA.I")) - throw ctx.makeRuntimeErrorMsg(`You have not researched MarketTA.I for division: ${divisionName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.I for division: ${divisionName}`); SetProductMarketTA1(getProduct(divisionName, productName), on); }, setProductMarketTA2: (ctx: NetscriptContext) => (_divisionName: unknown, _productName: unknown, _on: unknown): void => { checkAccess(ctx, 7); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const productName = ctx.helper.string("productName", _productName); - const on = ctx.helper.boolean(_on); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const productName = helpers.string(ctx, "productName", _productName); + const on = !!_on; if (!getDivision(divisionName).hasResearch("Market-TA.II")) - throw ctx.makeRuntimeErrorMsg(`You have not researched MarketTA.II for division: ${divisionName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.II for division: ${divisionName}`); SetProductMarketTA2(getProduct(divisionName, productName), on); }, }; @@ -652,7 +654,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown): number => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); const division = getDivision(divisionName); return division.getAdVertCost(); }, @@ -660,7 +662,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown): number => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); const division = getDivision(divisionName); return division.numAdVerts; }, @@ -668,25 +670,25 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _researchName: unknown): number => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const researchName = ctx.helper.string("researchName", _researchName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const researchName = helpers.string(ctx, "researchName", _researchName); return getResearchCost(getDivision(divisionName), researchName); }, hasResearched: (ctx: NetscriptContext) => (_divisionName: unknown, _researchName: unknown): boolean => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const researchName = ctx.helper.string("researchName", _researchName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const researchName = helpers.string(ctx, "researchName", _researchName); return hasResearched(getDivision(divisionName), researchName); }, getOfficeSizeUpgradeCost: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown, _size: unknown): number => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const size = ctx.helper.number("size", _size); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const size = helpers.number(ctx, "size", _size); if (size < 0) throw new Error("Invalid value for size field! Must be numeric and greater than 0"); const office = getOffice(divisionName, cityName); const initialPriceMult = Math.round(office.size / CorporationConstants.OfficeInitialSize); @@ -701,10 +703,10 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _employeeName: unknown, _job: unknown): void => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const employeeName = ctx.helper.string("employeeName", _employeeName); - const job = ctx.helper.string("job", _job); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const employeeName = helpers.string(ctx, "employeeName", _employeeName); + const job = helpers.string(ctx, "job", _job); if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); const office = getOffice(divisionName, cityName); @@ -715,10 +717,10 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _job: unknown, _amount: unknown): boolean => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const amount = ctx.helper.number("amount", _amount); - const job = ctx.helper.string("job", _job); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const amount = helpers.number(ctx, "amount", _amount); + const job = helpers.string(ctx, "job", _job); if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`); const office = getOffice(divisionName, cityName); @@ -729,8 +731,8 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown): NSEmployee | undefined => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); const office = getOffice(divisionName, cityName); const employee = office.hireRandomEmployee(); if (employee === undefined) return undefined; @@ -753,9 +755,9 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _size: unknown): void => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const size = ctx.helper.number("size", _size); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const size = helpers.number(ctx, "size", _size); if (size < 0) throw new Error("Invalid value for size field! Must be numeric and greater than 0"); const office = getOffice(divisionName, cityName); const corporation = getCorporation(); @@ -765,9 +767,9 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _costPerEmployee: unknown): number => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const costPerEmployee = ctx.helper.number("costPerEmployee", _costPerEmployee); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const costPerEmployee = helpers.number(ctx, "costPerEmployee", _costPerEmployee); if (costPerEmployee < 0) { throw new Error("Invalid value for Cost Per Employee field! Must be numeric and greater than 0"); @@ -782,8 +784,8 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown): boolean => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); const corporation = getCorporation(); const office = getOffice(divisionName, cityName); @@ -794,7 +796,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown): void => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); const corporation = getCorporation(); HireAdVert(corporation, getDivision(divisionName)); }, @@ -802,16 +804,16 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _researchName: unknown): void => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const researchName = ctx.helper.string("researchName", _researchName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const researchName = helpers.string(ctx, "researchName", _researchName); Research(getDivision(divisionName), researchName); }, getOffice: (ctx: NetscriptContext) => (_divisionName: unknown, _cityName: unknown): NSOffice => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); const office = getOffice(divisionName, cityName); return { loc: office.loc, @@ -847,9 +849,9 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown, _employeeName: unknown): NSEmployee => { checkAccess(ctx, 8); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); - const employeeName = ctx.helper.string("employeeName", _employeeName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); + const employeeName = helpers.string(ctx, "employeeName", _employeeName); const employee = getEmployee(divisionName, cityName, employeeName); return { name: employee.name, @@ -871,37 +873,27 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI - (): string[] =>{ + getMaterialNames: () => (): string[] => { return CorporationConstants.AllMaterials; - }, - getIndustryTypes: - (ctx: NetscriptContext) => - (): string[] =>{ + }, + getIndustryTypes: () => (): string[] => { return CorporationConstants.AllIndustryTypes; - }, - getUnlockables: - (ctx: NetscriptContext) => - (): string[] =>{ + }, + getUnlockables: () => (): string[] => { return CorporationConstants.AllUnlocks; - }, - getUpgradeNames: - (ctx: NetscriptContext) => - (): string[] =>{ + }, + getUpgradeNames: () => (): string[] => { return CorporationConstants.AllUpgrades; - }, - getResarchNames: - (ctx: NetscriptContext) => - (): string[] =>{ + }, + getResearchNames: () => (): string[] => { return CorporationConstants.AllResearch; - }, + }, expandIndustry: (ctx: NetscriptContext) => (_industryName: unknown, _divisionName: unknown): void => { checkAccess(ctx); - const industryName = ctx.helper.string("industryName", _industryName); - const divisionName = ctx.helper.string("divisionName", _divisionName); + const industryName = helpers.string(ctx, "industryName", _industryName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); const corporation = getCorporation(); NewIndustry(corporation, industryName, divisionName); }, @@ -909,8 +901,8 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown, _cityName: unknown): void => { checkAccess(ctx); - const divisionName = ctx.helper.string("divisionName", _divisionName); - const cityName = ctx.helper.city("cityName", _cityName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); + const cityName = helpers.city(ctx, "cityName", _cityName); if (!CorporationConstants.Cities.includes(cityName)) throw new Error("Invalid city name"); const corporation = getCorporation(); const division = getDivision(divisionName); @@ -920,7 +912,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_upgradeName: unknown): void => { checkAccess(ctx); - const upgradeName = ctx.helper.string("upgradeName", _upgradeName); + const upgradeName = helpers.string(ctx, "upgradeName", _upgradeName); const corporation = getCorporation(); const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade.name === upgradeName); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); @@ -930,7 +922,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_upgradeName: unknown): void => { checkAccess(ctx); - const upgradeName = ctx.helper.string("upgradeName", _upgradeName); + const upgradeName = helpers.string(ctx, "upgradeName", _upgradeName); const corporation = getCorporation(); const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade.name === upgradeName); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); @@ -940,12 +932,12 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_rate: unknown): void => { checkAccess(ctx); - const rate = ctx.helper.number("rate", _rate); + const rate = helpers.number(ctx, "rate", _rate); const max = CorporationConstants.DividendMaxRate; if (rate < 0 || rate > max) throw new Error(`Invalid value for rate field! Must be numeric, greater than 0, and less than ${max}`); const corporation = getCorporation(); - if (!corporation.public) throw ctx.makeRuntimeErrorMsg(`Your company has not gone public!`); + if (!corporation.public) throw helpers.makeRuntimeErrorMsg(ctx, `Your company has not gone public!`); IssueDividends(corporation, rate); }, @@ -955,7 +947,7 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_divisionName: unknown): NSDivision => { checkAccess(ctx); - const divisionName = ctx.helper.string("divisionName", _divisionName); + const divisionName = helpers.string(ctx, "divisionName", _divisionName); const division = getDivision(divisionName); return getSafeDivision(division); }, @@ -983,43 +975,43 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_corporationName: unknown, _selfFund: unknown = true): boolean => { - const corporationName = ctx.helper.string("corporationName", _corporationName); - const selfFund = ctx.helper.boolean(_selfFund); + const corporationName = helpers.string(ctx, "corporationName", _corporationName); + const selfFund = !_selfFund; return createCorporation(corporationName, selfFund); }, hasUnlockUpgrade: (ctx: NetscriptContext) => (_upgradeName: unknown): boolean => { checkAccess(ctx); - const upgradeName = ctx.helper.string("upgradeName", _upgradeName); + const upgradeName = helpers.string(ctx, "upgradeName", _upgradeName); return hasUnlockUpgrade(upgradeName); }, getUnlockUpgradeCost: (ctx: NetscriptContext) => (_upgradeName: unknown): number => { checkAccess(ctx); - const upgradeName = ctx.helper.string("upgradeName", _upgradeName); + const upgradeName = helpers.string(ctx, "upgradeName", _upgradeName); return getUnlockUpgradeCost(upgradeName); }, getUpgradeLevel: (ctx: NetscriptContext) => (_upgradeName: unknown): number => { checkAccess(ctx); - const upgradeName = ctx.helper.string("upgradeName", _upgradeName); + const upgradeName = helpers.string(ctx, "upgradeName", _upgradeName); return getUpgradeLevel(ctx, upgradeName); }, getUpgradeLevelCost: (ctx: NetscriptContext) => (_upgradeName: unknown): number => { checkAccess(ctx); - const upgradeName = ctx.helper.string("upgradeName", _upgradeName); + const upgradeName = helpers.string(ctx, "upgradeName", _upgradeName); return getUpgradeLevelCost(ctx, upgradeName); }, getExpandIndustryCost: (ctx: NetscriptContext) => (_industryName: unknown): number => { checkAccess(ctx); - const industryName = ctx.helper.string("industryName", _industryName); + const industryName = helpers.string(ctx, "industryName", _industryName); return getExpandIndustryCost(industryName); }, getExpandCityCost: (ctx: NetscriptContext) => (): number => { @@ -1038,29 +1030,29 @@ export function NetscriptCorporation(player: IPlayer): InternalAPI (_numShares: unknown): boolean => { checkAccess(ctx); - const numShares = ctx.helper.number("numShares", _numShares); + const numShares = helpers.number(ctx, "numShares", _numShares); return goPublic(numShares); }, sellShares: (ctx: NetscriptContext) => (_numShares: unknown): number => { checkAccess(ctx); - const numShares = ctx.helper.number("numShares", _numShares); + const numShares = helpers.number(ctx, "numShares", _numShares); return SellShares(getCorporation(), player, numShares); }, buyBackShares: (ctx: NetscriptContext) => (_numShares: unknown): boolean => { checkAccess(ctx); - const numShares = ctx.helper.number("numShares", _numShares); + const numShares = helpers.number(ctx, "numShares", _numShares); return BuyBackShares(getCorporation(), player, numShares); }, bribe: (ctx: NetscriptContext) => (_factionName: unknown, _amountCash: unknown): boolean => { checkAccess(ctx); - const factionName = ctx.helper.string("factionName", _factionName); - const amountCash = ctx.helper.number("amountCash", _amountCash); + const factionName = helpers.string(ctx, "factionName", _factionName); + const amountCash = helpers.number(ctx, "amountCash", _amountCash); return bribe(factionName, amountCash); }, getBonusTime: (ctx: NetscriptContext) => (): number => { diff --git a/src/NetscriptFunctions/Extra.ts b/src/NetscriptFunctions/Extra.ts index a5e2b04ba..427a1ec63 100644 --- a/src/NetscriptFunctions/Extra.ts +++ b/src/NetscriptFunctions/Extra.ts @@ -1,8 +1,9 @@ -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"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; +import { helpers } from "../Netscript/NetscriptHelpers"; export interface INetscriptExtra { heart: { @@ -15,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 @@ -67,7 +68,7 @@ export function NetscriptExtra(player: IPlayer): InternalAPI { function tryGuess(): boolean { // eslint-disable-next-line no-sync const verified = bcrypt.compareSync( - ctx.helper.string("guess", guess), + helpers.string(ctx, "guess", guess), "$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO", ); if (verified) { diff --git a/src/NetscriptFunctions/Flags.ts b/src/NetscriptFunctions/Flags.ts index d67158dc0..1d08dc67b 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 f7ed0797d..3cc659d2e 100644 --- a/src/NetscriptFunctions/Formulas.ts +++ b/src/NetscriptFunctions/Formulas.ts @@ -1,5 +1,4 @@ -import { INetscriptHelper } from "./INetscriptHelper"; -import { IPlayer } from "../PersonObjects/IPlayer"; +import { Player as player } from "../Player"; import { calculateServerGrowth } from "../Server/formulas/grow"; import { calculateMoneyGainRate, @@ -43,11 +42,12 @@ import { import { favorToRep as calculateFavorToRep, repToFavor as calculateRepToFavor } from "../Faction/formulas/favor"; import { repFromDonation } from "../Faction/formulas/donation"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; +import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): InternalAPI { +export function NetscriptFormulas(): InternalAPI { const checkFormulasAccess = function (ctx: NetscriptContext): void { if (!player.hasProgram(Programs.Formulas.name)) { - throw helper.makeRuntimeErrorMsg(`formulas.${ctx.function}`, `Requires Formulas.exe to run.`); + throw helpers.makeRuntimeErrorMsg(ctx, `Requires Formulas.exe to run.`); } }; return { @@ -55,22 +55,22 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In calculateFavorToRep: (ctx: NetscriptContext) => (_favor: unknown): number => { - const favor = ctx.helper.number("favor", _favor); + const favor = helpers.number(ctx, "favor", _favor); checkFormulasAccess(ctx); return calculateFavorToRep(favor); }, calculateRepToFavor: (ctx: NetscriptContext) => (_rep: unknown): number => { - const rep = ctx.helper.number("rep", _rep); + const rep = helpers.number(ctx, "rep", _rep); checkFormulasAccess(ctx); return calculateRepToFavor(rep); }, repFromDonation: (ctx: NetscriptContext) => (_amount: unknown, _player: unknown): number => { - const amount = ctx.helper.number("amount", _amount); - const player = ctx.helper.player(_player); + const amount = helpers.number(ctx, "amount", _amount); + const player = helpers.player(ctx, _player); checkFormulasAccess(ctx); return repFromDonation(amount, player); }, @@ -79,16 +79,16 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In calculateSkill: (ctx: NetscriptContext) => (_exp: unknown, _mult: unknown = 1): number => { - const exp = ctx.helper.number("exp", _exp); - const mult = ctx.helper.number("mult", _mult); + const exp = helpers.number(ctx, "exp", _exp); + const mult = helpers.number(ctx, "mult", _mult); checkFormulasAccess(ctx); return calculateSkill(exp, mult); }, calculateExp: (ctx: NetscriptContext) => (_skill: unknown, _mult: unknown = 1): number => { - const skill = ctx.helper.number("skill", _skill); - const mult = ctx.helper.number("mult", _mult); + const skill = helpers.number(ctx, "skill", _skill); + const mult = helpers.number(ctx, "mult", _mult); checkFormulasAccess(ctx); return calculateExp(skill, mult); }, @@ -97,58 +97,58 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In hackChance: (ctx: NetscriptContext) => (_server: unknown, _player: unknown): number => { - const server = ctx.helper.server(_server); - const player = ctx.helper.player(_player); + const server = helpers.server(ctx, _server); + const player = helpers.player(ctx, _player); checkFormulasAccess(ctx); return calculateHackingChance(server, player); }, hackExp: (ctx: NetscriptContext) => (_server: unknown, _player: unknown): number => { - const server = ctx.helper.server(_server); - const player = ctx.helper.player(_player); + const server = helpers.server(ctx, _server); + const player = helpers.player(ctx, _player); checkFormulasAccess(ctx); return calculateHackingExpGain(server, player); }, hackPercent: (ctx: NetscriptContext) => (_server: unknown, _player: unknown): number => { - const server = ctx.helper.server(_server); - const player = ctx.helper.player(_player); + const server = helpers.server(ctx, _server); + const player = helpers.player(ctx, _player); checkFormulasAccess(ctx); return calculatePercentMoneyHacked(server, player); }, growPercent: (ctx: NetscriptContext) => (_server: unknown, _threads: unknown, _player: unknown, _cores: unknown = 1): number => { - const server = ctx.helper.server(_server); - const player = ctx.helper.player(_player); - const threads = ctx.helper.number("threads", _threads); - const cores = ctx.helper.number("cores", _cores); + const server = helpers.server(ctx, _server); + const player = helpers.player(ctx, _player); + const threads = helpers.number(ctx, "threads", _threads); + const cores = helpers.number(ctx, "cores", _cores); checkFormulasAccess(ctx); return calculateServerGrowth(server, threads, player, cores); }, hackTime: (ctx: NetscriptContext) => (_server: unknown, _player: unknown): number => { - const server = ctx.helper.server(_server); - const player = ctx.helper.player(_player); + const server = helpers.server(ctx, _server); + const player = helpers.player(ctx, _player); checkFormulasAccess(ctx); return calculateHackingTime(server, player) * 1000; }, growTime: (ctx: NetscriptContext) => (_server: unknown, _player: unknown): number => { - const server = ctx.helper.server(_server); - const player = ctx.helper.player(_player); + const server = helpers.server(ctx, _server); + const player = helpers.player(ctx, _player); checkFormulasAccess(ctx); return calculateGrowTime(server, player) * 1000; }, weakenTime: (ctx: NetscriptContext) => (_server: unknown, _player: unknown): number => { - const server = ctx.helper.server(_server); - const player = ctx.helper.player(_player); + const server = helpers.server(ctx, _server); + const player = helpers.player(ctx, _player); checkFormulasAccess(ctx); return calculateWeakenTime(server, player) * 1000; }, @@ -157,45 +157,45 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In moneyGainRate: (ctx: NetscriptContext) => (_level: unknown, _ram: unknown, _cores: unknown, _mult: unknown = 1): number => { - const level = ctx.helper.number("level", _level); - const ram = ctx.helper.number("ram", _ram); - const cores = ctx.helper.number("cores", _cores); - const mult = ctx.helper.number("mult", _mult); + const level = helpers.number(ctx, "level", _level); + const ram = helpers.number(ctx, "ram", _ram); + const cores = helpers.number(ctx, "cores", _cores); + const mult = helpers.number(ctx, "mult", _mult); checkFormulasAccess(ctx); return calculateMoneyGainRate(level, ram, cores, mult); }, levelUpgradeCost: (ctx: NetscriptContext) => (_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { - const startingLevel = ctx.helper.number("startingLevel", _startingLevel); - const extraLevels = ctx.helper.number("extraLevels", _extraLevels); - const costMult = ctx.helper.number("costMult", _costMult); + const startingLevel = helpers.number(ctx, "startingLevel", _startingLevel); + const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); + const costMult = helpers.number(ctx, "costMult", _costMult); checkFormulasAccess(ctx); return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult); }, ramUpgradeCost: (ctx: NetscriptContext) => (_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { - const startingRam = ctx.helper.number("startingRam", _startingRam); - const extraLevels = ctx.helper.number("extraLevels", _extraLevels); - const costMult = ctx.helper.number("costMult", _costMult); + const startingRam = helpers.number(ctx, "startingRam", _startingRam); + const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); + const costMult = helpers.number(ctx, "costMult", _costMult); checkFormulasAccess(ctx); return calculateRamUpgradeCost(startingRam, extraLevels, costMult); }, coreUpgradeCost: (ctx: NetscriptContext) => (_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number => { - const startingCore = ctx.helper.number("startingCore", _startingCore); - const extraCores = ctx.helper.number("extraCores", _extraCores); - const costMult = ctx.helper.number("costMult", _costMult); + const startingCore = helpers.number(ctx, "startingCore", _startingCore); + const extraCores = helpers.number(ctx, "extraCores", _extraCores); + const costMult = helpers.number(ctx, "costMult", _costMult); checkFormulasAccess(ctx); return calculateCoreUpgradeCost(startingCore, extraCores, costMult); }, hacknetNodeCost: (ctx: NetscriptContext) => (_n: unknown, _mult: unknown): number => { - const n = ctx.helper.number("n", _n); - const mult = ctx.helper.number("mult", _mult); + const n = helpers.number(ctx, "n", _n); + const mult = helpers.number(ctx, "mult", _mult); checkFormulasAccess(ctx); return calculateNodeCost(n, mult); }, @@ -208,69 +208,66 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In hashGainRate: (ctx: NetscriptContext) => (_level: unknown, _ramUsed: unknown, _maxRam: unknown, _cores: unknown, _mult: unknown = 1): number => { - const level = ctx.helper.number("level", _level); - const ramUsed = ctx.helper.number("ramUsed", _ramUsed); - const maxRam = ctx.helper.number("maxRam", _maxRam); - const cores = ctx.helper.number("cores", _cores); - const mult = ctx.helper.number("mult", _mult); + const level = helpers.number(ctx, "level", _level); + const ramUsed = helpers.number(ctx, "ramUsed", _ramUsed); + const maxRam = helpers.number(ctx, "maxRam", _maxRam); + const cores = helpers.number(ctx, "cores", _cores); + const mult = helpers.number(ctx, "mult", _mult); checkFormulasAccess(ctx); return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult); }, levelUpgradeCost: (ctx: NetscriptContext) => (_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { - const startingLevel = ctx.helper.number("startingLevel", _startingLevel); - const extraLevels = ctx.helper.number("extraLevels", _extraLevels); - const costMult = ctx.helper.number("costMult", _costMult); + const startingLevel = helpers.number(ctx, "startingLevel", _startingLevel); + const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); + const costMult = helpers.number(ctx, "costMult", _costMult); checkFormulasAccess(ctx); return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult); }, ramUpgradeCost: (ctx: NetscriptContext) => (_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number => { - const startingRam = ctx.helper.number("startingRam", _startingRam); - const extraLevels = ctx.helper.number("extraLevels", _extraLevels); - const costMult = ctx.helper.number("costMult", _costMult); + const startingRam = helpers.number(ctx, "startingRam", _startingRam); + const extraLevels = helpers.number(ctx, "extraLevels", _extraLevels); + const costMult = helpers.number(ctx, "costMult", _costMult); checkFormulasAccess(ctx); return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult); }, coreUpgradeCost: (ctx: NetscriptContext) => (_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number => { - const startingCore = ctx.helper.number("startingCore", _startingCore); - const extraCores = ctx.helper.number("extraCores", _extraCores); - const costMult = ctx.helper.number("costMult", _costMult); + const startingCore = helpers.number(ctx, "startingCore", _startingCore); + const extraCores = helpers.number(ctx, "extraCores", _extraCores); + const costMult = helpers.number(ctx, "costMult", _costMult); checkFormulasAccess(ctx); return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult); }, cacheUpgradeCost: (ctx: NetscriptContext) => (_startingCache: unknown, _extraCache: unknown = 1): number => { - const startingCache = ctx.helper.number("startingCache", _startingCache); - const extraCache = ctx.helper.number("extraCache", _extraCache); + const startingCache = helpers.number(ctx, "startingCache", _startingCache); + const extraCache = helpers.number(ctx, "extraCache", _extraCache); checkFormulasAccess(ctx); return HScalculateCacheUpgradeCost(startingCache, extraCache); }, hashUpgradeCost: (ctx: NetscriptContext) => (_upgName: unknown, _level: unknown): number => { - const upgName = helper.string("hashUpgradeCost", "upgName", _upgName); - const level = ctx.helper.number("level", _level); + const upgName = helpers.string(ctx, "upgName", _upgName); + const level = helpers.number(ctx, "level", _level); checkFormulasAccess(ctx); const upg = player.hashManager.getUpgrade(upgName); if (!upg) { - throw helper.makeRuntimeErrorMsg( - "formulas.hacknetServers.calculateHashUpgradeCost", - `Invalid Hash Upgrade: ${upgName}`, - ); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid Hash Upgrade: ${upgName}`); } return upg.getCost(level); }, hacknetServerCost: (ctx: NetscriptContext) => (_n: unknown, _mult: unknown = 1): number => { - const n = ctx.helper.number("n", _n); - const mult = ctx.helper.number("mult", _mult); + const n = helpers.number(ctx, "n", _n); + const mult = helpers.number(ctx, "mult", _mult); checkFormulasAccess(ctx); return HScalculateServerCost(n, mult); }, @@ -283,48 +280,48 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In wantedPenalty: (ctx: NetscriptContext) => (_gang: unknown): number => { - const gang = ctx.helper.gang(_gang); + const gang = helpers.gang(ctx, _gang); checkFormulasAccess(ctx); return calculateWantedPenalty(gang); }, respectGain: (ctx: NetscriptContext) => (_gang: unknown, _member: unknown, _task: unknown): number => { - const gang = ctx.helper.gang(_gang); - const member = ctx.helper.gangMember(_member); - const task = ctx.helper.gangTask(_task); + const gang = helpers.gang(ctx, _gang); + const member = helpers.gangMember(ctx, _member); + const task = helpers.gangTask(ctx, _task); checkFormulasAccess(ctx); return calculateRespectGain(gang, member, task); }, wantedLevelGain: (ctx: NetscriptContext) => (_gang: unknown, _member: unknown, _task: unknown): number => { - const gang = ctx.helper.gang(_gang); - const member = ctx.helper.gangMember(_member); - const task = ctx.helper.gangTask(_task); + const gang = helpers.gang(ctx, _gang); + const member = helpers.gangMember(ctx, _member); + const task = helpers.gangTask(ctx, _task); checkFormulasAccess(ctx); return calculateWantedLevelGain(gang, member, task); }, moneyGain: (ctx: NetscriptContext) => (_gang: unknown, _member: unknown, _task: unknown): number => { - const gang = ctx.helper.gang(_gang); - const member = ctx.helper.gangMember(_member); - const task = ctx.helper.gangTask(_task); + const gang = helpers.gang(ctx, _gang); + const member = helpers.gangMember(ctx, _member); + const task = helpers.gangTask(ctx, _task); checkFormulasAccess(ctx); return calculateMoneyGain(gang, member, task); }, ascensionPointsGain: (ctx: NetscriptContext) => (_exp: unknown): number => { - const exp = ctx.helper.number("exp", _exp); + const exp = helpers.number(ctx, "exp", _exp); checkFormulasAccess(ctx); return calculateAscensionPointsGain(exp); }, ascensionMultiplier: (ctx: NetscriptContext) => (_points: unknown): number => { - const points = ctx.helper.number("points", _points); + const points = helpers.number(ctx, "points", _points); checkFormulasAccess(ctx); return calculateAscensionMult(points); }, diff --git a/src/NetscriptFunctions/Gang.ts b/src/NetscriptFunctions/Gang.ts index 8e13bf9ba..69c889397 100644 --- a/src/NetscriptFunctions/Gang.ts +++ b/src/NetscriptFunctions/Gang.ts @@ -1,13 +1,13 @@ 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"; import { Gang as IGang, @@ -21,13 +21,13 @@ 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"); const hasAccess = gang instanceof Gang; if (!hasAccess) { - throw ctx.makeRuntimeErrorMsg(`You do not currently have a Gang`); + throw helpers.makeRuntimeErrorMsg(ctx, `You do not currently have a Gang`); } }; @@ -35,13 +35,13 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte const gang = player.gang; if (gang === null) throw new Error("Must have joined gang"); for (const member of gang.members) if (member.name === name) return member; - throw ctx.makeRuntimeErrorMsg(`Invalid gang member: '${name}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid gang member: '${name}'`); }; const getGangTask = function (ctx: NetscriptContext, name: string): GangMemberTask { const task = GangMemberTasks[name]; if (!task) { - throw ctx.makeRuntimeErrorMsg(`Invalid task: '${name}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid task: '${name}'`); } return task; @@ -51,7 +51,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte createGang: (ctx: NetscriptContext) => (_faction: unknown): boolean => { - const faction = ctx.helper.string("faction", _faction); + const faction = helpers.string(ctx, "faction", _faction); // this list is copied from Faction/ui/Root.tsx if (!player.canAccessGang() || !GangConstants.Names.includes(faction)) return false; @@ -102,7 +102,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte getMemberInformation: (ctx: NetscriptContext) => (_memberName: unknown): GangMemberInfo => { - const memberName = ctx.helper.string("memberName", _memberName); + const memberName = helpers.string(ctx, "memberName", _memberName); checkGangApiAccess(ctx); const gang = player.gang; if (gang === null) throw new Error("Should not be called without Gang"); @@ -163,15 +163,15 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte recruitMember: (ctx: NetscriptContext) => (_memberName: unknown): boolean => { - const memberName = ctx.helper.string("memberName", _memberName); + const memberName = helpers.string(ctx, "memberName", _memberName); checkGangApiAccess(ctx); const gang = player.gang; 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; @@ -187,14 +187,14 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte setMemberTask: (ctx: NetscriptContext) => (_memberName: unknown, _taskName: unknown): boolean => { - const memberName = ctx.helper.string("memberName", _memberName); - const taskName = ctx.helper.string("taskName", _taskName); + const memberName = helpers.string(ctx, "memberName", _memberName); + const taskName = helpers.string(ctx, "taskName", _taskName); checkGangApiAccess(ctx); const member = getGangMember(ctx, memberName); 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`, @@ -203,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`, @@ -220,7 +220,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte getTaskStats: (ctx: NetscriptContext) => (_taskName: unknown): GangTaskStats => { - const taskName = ctx.helper.string("taskName", _taskName); + const taskName = helpers.string(ctx, "taskName", _taskName); checkGangApiAccess(ctx); const task = getGangTask(ctx, taskName); const copy = Object.assign({}, task); @@ -234,7 +234,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte getEquipmentCost: (ctx: NetscriptContext) => (_equipName: unknown): number => { - const equipName = ctx.helper.string("equipName", _equipName); + const equipName = helpers.string(ctx, "equipName", _equipName); checkGangApiAccess(ctx); const gang = player.gang; if (gang === null) throw new Error("Should not be called without Gang"); @@ -245,7 +245,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte getEquipmentType: (ctx: NetscriptContext) => (_equipName: unknown): string => { - const equipName = ctx.helper.string("equipName", _equipName); + const equipName = helpers.string(ctx, "equipName", _equipName); checkGangApiAccess(ctx); const upg = GangMemberUpgrades[equipName]; if (upg == null) return ""; @@ -254,11 +254,11 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte getEquipmentStats: (ctx: NetscriptContext) => (_equipName: unknown): EquipmentStats => { - const equipName = ctx.helper.string("equipName", _equipName); + const equipName = helpers.string(ctx, "equipName", _equipName); checkGangApiAccess(ctx); const equipment = GangMemberUpgrades[equipName]; if (!equipment) { - throw ctx.makeRuntimeErrorMsg(`Invalid equipment: ${equipName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid equipment: ${equipName}`); } const typecheck: EquipmentStats = equipment.mults; return Object.assign({}, typecheck) as any; @@ -266,8 +266,8 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte purchaseEquipment: (ctx: NetscriptContext) => (_memberName: unknown, _equipName: unknown): boolean => { - const memberName = ctx.helper.string("memberName", _memberName); - const equipName = ctx.helper.string("equipName", _equipName); + const memberName = helpers.string(ctx, "memberName", _memberName); + const equipName = helpers.string(ctx, "equipName", _equipName); checkGangApiAccess(ctx); const gang = player.gang; if (gang === null) throw new Error("Should not be called without Gang"); @@ -276,9 +276,12 @@ 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}'`, ); @@ -289,18 +292,18 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte ascendMember: (ctx: NetscriptContext) => (_memberName: unknown): GangMemberAscension | undefined => { - const memberName = ctx.helper.string("memberName", _memberName); + const memberName = helpers.string(ctx, "memberName", _memberName); checkGangApiAccess(ctx); const gang = player.gang; 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) => (_memberName: unknown): GangMemberAscension | undefined => { - const memberName = ctx.helper.string("memberName", _memberName); + const memberName = helpers.string(ctx, "memberName", _memberName); checkGangApiAccess(ctx); const gang = player.gang; if (gang === null) throw new Error("Should not be called without Gang"); @@ -314,27 +317,27 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): Inte setTerritoryWarfare: (ctx: NetscriptContext) => (_engage: unknown): void => { - const engage = ctx.helper.boolean(_engage); + const engage = !!_engage; checkGangApiAccess(ctx); const gang = player.gang; 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: (ctx: NetscriptContext) => (_otherGang: unknown): number => { - const otherGang = ctx.helper.string("otherGang", _otherGang); + const otherGang = helpers.string(ctx, "otherGang", _otherGang); checkGangApiAccess(ctx); const gang = player.gang; if (gang === null) throw new Error("Should not be called without Gang"); if (AllGangs[otherGang] == null) { - throw ctx.makeRuntimeErrorMsg(`Invalid gang: ${otherGang}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid gang: ${otherGang}`); } const playerPower = AllGangs[gang.facName].power; diff --git a/src/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts index f74e85ba9..1b1623b52 100644 --- a/src/NetscriptFunctions/Grafting.ts +++ b/src/NetscriptFunctions/Grafting.ts @@ -4,15 +4,17 @@ 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 ctx.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, "You do not currently have access to the Grafting API. This is either because you are not in BitNode 10 or because you do not have Source-File 10", ); } @@ -22,10 +24,10 @@ export function NetscriptGrafting(player: IPlayer): InternalAPI { getAugmentationGraftPrice: (ctx: NetscriptContext) => (_augName: unknown): number => { - const augName = ctx.helper.string("augName", _augName); + const augName = helpers.string(ctx, "augName", _augName); checkGraftingAPIAccess(ctx); if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { - throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); } const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); return graftableAug.cost; @@ -34,10 +36,10 @@ export function NetscriptGrafting(player: IPlayer): InternalAPI { getAugmentationGraftTime: (ctx: NetscriptContext) => (_augName: string): number => { - const augName = ctx.helper.string("augName", _augName); + const augName = helpers.string(ctx, "augName", _augName); checkGraftingAPIAccess(ctx); if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { - throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); } const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); return calculateGraftingTimeWithBonus(player, graftableAug); @@ -52,14 +54,14 @@ export function NetscriptGrafting(player: IPlayer): InternalAPI { graftAugmentation: (ctx: NetscriptContext) => (_augName: string, _focus: unknown = true): boolean => { - const augName = ctx.helper.string("augName", _augName); - const focus = ctx.helper.boolean(_focus); + const augName = helpers.string(ctx, "augName", _augName); + const focus = !!_focus; checkGraftingAPIAccess(ctx); if (player.city !== CityName.NewTokyo) { - throw ctx.makeRuntimeErrorMsg("You must be in New Tokyo to begin grafting an Augmentation."); + throw helpers.makeRuntimeErrorMsg(ctx, "You must be in New Tokyo to begin grafting an Augmentation."); } if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { - ctx.log(() => `Invalid aug: ${augName}`); + helpers.log(ctx, () => `Invalid aug: ${augName}`); return false; } @@ -67,12 +69,12 @@ export function NetscriptGrafting(player: IPlayer): InternalAPI { const craftableAug = new GraftableAugmentation(StaticAugmentations[augName]); if (player.money < craftableAug.cost) { - ctx.log(() => `You don't have enough money to craft ${augName}`); + helpers.log(ctx, () => `You don't have enough money to craft ${augName}`); return false; } if (!hasAugmentationPrereqs(craftableAug.augmentation)) { - ctx.log(() => `You don't have the pre-requisites for ${augName}`); + helpers.log(ctx, () => `You don't have the pre-requisites for ${augName}`); return false; } @@ -92,7 +94,7 @@ export function NetscriptGrafting(player: IPlayer): InternalAPI { Router.toTerminal(); } - ctx.log(() => `Began grafting Augmentation ${augName}.`); + helpers.log(ctx, () => `Began grafting Augmentation ${augName}.`); return true; }, }; diff --git a/src/NetscriptFunctions/Hacknet.ts b/src/NetscriptFunctions/Hacknet.ts index 8454c4789..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, @@ -21,12 +20,13 @@ import { GetServer } from "../Server/AllServers"; import { Hacknet as IHacknet, NodeStats } from "../ScriptEditor/NetscriptDefinitions"; 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) { - throw ctx.makeRuntimeErrorMsg("Index specified for Hacknet Node is out-of-bounds: " + i); + throw helpers.makeRuntimeErrorMsg(ctx, "Index specified for Hacknet Node is out-of-bounds: " + i); } if (hasHacknetServers(player)) { @@ -35,7 +35,8 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I const hserver = GetServer(hi); if (!(hserver instanceof HacknetServer)) throw new Error("hacknet server was not actually hacknet server"); if (hserver == null) { - throw ctx.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, `Could not get Hacknet Server for index ${i}. This is probably a bug, please report to game dev`, ); } @@ -71,7 +72,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I getNodeStats: (ctx: NetscriptContext) => (_i: unknown): NodeStats => { - const i = ctx.helper.number("i", _i); + const i = helpers.number(ctx, "i", _i); const node = getHacknetNode(ctx, i); const hasUpgraded = hasHacknetServers(player); const res: NodeStats = { @@ -95,38 +96,38 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I upgradeLevel: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): boolean => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); return purchaseLevelUpgrade(player, node, n); }, upgradeRam: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): boolean => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); return purchaseRamUpgrade(player, node, n); }, upgradeCore: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): boolean => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); return purchaseCoreUpgrade(player, node, n); }, upgradeCache: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): boolean => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); if (!hasHacknetServers(player)) { return false; } 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); @@ -138,38 +139,38 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I getLevelUpgradeCost: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): number => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); return node.calculateLevelUpgradeCost(n, player.mults.hacknet_node_level_cost); }, getRamUpgradeCost: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): number => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); return node.calculateRamUpgradeCost(n, player.mults.hacknet_node_ram_cost); }, getCoreUpgradeCost: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): number => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); const node = getHacknetNode(ctx, i); return node.calculateCoreUpgradeCost(n, player.mults.hacknet_node_core_cost); }, getCacheUpgradeCost: (ctx: NetscriptContext) => (_i: unknown, _n: unknown = 1): number => { - const i = ctx.helper.number("i", _i); - const n = ctx.helper.number("n", _n); + const i = helpers.number(ctx, "i", _i); + const n = helpers.number(ctx, "n", _n); if (!hasHacknetServers(player)) { return Infinity; } 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); @@ -189,8 +190,8 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I hashCost: (ctx: NetscriptContext) => (_upgName: unknown, _count: unknown = 1): number => { - const upgName = ctx.helper.string("upgName", _upgName); - const count = ctx.helper.number("count", _count); + const upgName = helpers.string(ctx, "upgName", _upgName); + const count = helpers.number(ctx, "count", _count); if (!hasHacknetServers(player)) { return Infinity; } @@ -200,9 +201,9 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I spendHashes: (ctx: NetscriptContext) => (_upgName: unknown, _upgTarget: unknown = "", _count: unknown = 1): boolean => { - const upgName = ctx.helper.string("upgName", _upgName); - const upgTarget = ctx.helper.string("upgTarget", _upgTarget); - const count = ctx.helper.number("count", _count); + const upgName = helpers.string(ctx, "upgName", _upgName); + const upgTarget = helpers.string(ctx, "upgTarget", _upgTarget); + const count = helpers.number(ctx, "count", _count); if (!hasHacknetServers(player)) { return false; } @@ -217,10 +218,10 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): I getHashUpgradeLevel: (ctx: NetscriptContext) => (_upgName: unknown): number => { - const upgName = ctx.helper.string("upgName", _upgName); + const upgName = helpers.string(ctx, "upgName", _upgName); const level = player.hashManager.upgrades[upgName]; if (level === undefined) { - throw ctx.makeRuntimeErrorMsg(`Invalid Hash Upgrade: ${upgName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid Hash Upgrade: ${upgName}`); } return level; }, diff --git a/src/NetscriptFunctions/INetscriptHelper.ts b/src/NetscriptFunctions/INetscriptHelper.ts deleted file mode 100644 index 6d18df6cc..000000000 --- a/src/NetscriptFunctions/INetscriptHelper.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { CityName } from "../Locations/data/CityNames"; -import { NetscriptContext } from "../Netscript/APIWrapper"; -import { IPort } from "../NetscriptPort"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { Server } from "../Server/Server"; -import { BaseServer } from "../Server/BaseServer"; -import { FormulaGang } from "../Gang/formulas/formulas"; -import { GangMember } from "../Gang/GangMember"; -import { GangMemberTask } from "../Gang/GangMemberTask"; -import { BasicHGWOptions } from "../ScriptEditor/NetscriptDefinitions"; -import { ScriptArg } from "../Netscript/ScriptArg"; - -export type ScriptIdentifier = - | number - | { - fn: string; - hostname: string; - args: ScriptArg[]; - }; - -export interface INetscriptHelper { - updateDynamicRam(functionName: string, ram: number): void; - makeRuntimeErrorMsg(functionName: string, message: string): string; - string(funcName: string, argName: string, v: unknown): string; - number(funcName: string, argName: string, v: unknown): number; - ustring(funcName: string, argName: string, v: unknown): string | undefined; - unumber(funcName: string, argName: string, v: unknown): number | undefined; - scriptArgs(funcName: string, args: unknown): ScriptArg[]; - scriptIdentifier(funcName: string, fn: unknown, hostname: unknown, args: unknown): ScriptIdentifier; - city(funcName: string, argName: string, v: unknown): CityName; - boolean(v: unknown): boolean; - getServer(ip: string, ctx: NetscriptContext): BaseServer; - checkSingularityAccess(func: string): void; - hack(ctx: NetscriptContext, hostname: string, manual: boolean, extra?: BasicHGWOptions): Promise; - getValidPort(funcName: string, port: number): IPort; - player(funcName: string, p: unknown): IPlayer; - server(funcName: string, s: unknown): Server; - gang(funcName: string, g: unknown): FormulaGang; - gangMember(funcName: string, m: unknown): GangMember; - gangTask(funcName: string, m: unknown): GangMemberTask; -} diff --git a/src/NetscriptFunctions/Infiltration.ts b/src/NetscriptFunctions/Infiltration.ts index 692abaa50..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, @@ -18,8 +18,9 @@ import { Factions } from "../Faction/Factions"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; 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, ); @@ -27,9 +28,9 @@ export function NetscriptInfiltration(player: IPlayer): InternalAPI { if (!checkEnum(LocationName, locationName)) throw new Error(`Location '${locationName}' does not exists.`); const location = Locations[locationName]; - if (location === undefined) throw ctx.makeRuntimeErrorMsg(`Location '${location}' does not exists.`); + if (location === undefined) throw helpers.makeRuntimeErrorMsg(ctx, `Location '${location}' does not exists.`); if (location.infiltrationData === undefined) - throw ctx.makeRuntimeErrorMsg(`Location '${location}' does not provide infiltrations.`); + throw helpers.makeRuntimeErrorMsg(ctx, `Location '${location}' does not provide infiltrations.`); const startingSecurityLevel = location.infiltrationData.startingSecurityLevel; const difficulty = calculateDifficulty(player, startingSecurityLevel); const reward = calculateReward(player, startingSecurityLevel); @@ -54,7 +55,7 @@ export function NetscriptInfiltration(player: IPlayer): InternalAPI (_location: unknown): InfiltrationLocation => { - const location = ctx.helper.string("location", _location); + const location = helpers.string(ctx, "location", _location); return calculateInfiltrationData(ctx, location); }, }; diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts index ff6402bae..74ee8b925 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"; @@ -36,7 +35,7 @@ import { Company } from "../Company/Company"; import { Companies } from "../Company/Companies"; import { Factions, factionExists } from "../Faction/Factions"; import { Faction } from "../Faction/Faction"; -import { netscriptDelay } from "../NetscriptEvaluator"; +import { helpers } from "../Netscript/NetscriptHelpers"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { getServerOnNetwork } from "../Server/ServerHelpers"; import { Terminal } from "../Terminal"; @@ -54,27 +53,27 @@ import { FactionWork } from "../Work/FactionWork"; import { FactionWorkType } from "../Work/data/FactionWorkType"; import { CompanyWork } from "../Work/CompanyWork"; -export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI { - const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation { +export function NetscriptSingularity(): InternalAPI { + const getAugmentation = function (ctx: NetscriptContext, name: string): Augmentation { if (!augmentationExists(name)) { - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid augmentation: '${name}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid augmentation: '${name}'`); } return StaticAugmentations[name]; }; - const getFaction = function (_ctx: NetscriptContext, name: string): Faction { + const getFaction = function (ctx: NetscriptContext, name: string): Faction { if (!factionExists(name)) { - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid faction name: '${name}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid faction name: '${name}`); } return Factions[name]; }; - const getCompany = function (_ctx: NetscriptContext, name: string): Company { + const getCompany = function (ctx: NetscriptContext, name: string): Company { const company = Companies[name]; if (company == null || !(company instanceof Company)) { - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid company name: '${name}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company name: '${name}'`); } return company; }; @@ -98,10 +97,10 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript }; return { - getOwnedAugmentations: (_ctx: NetscriptContext) => + getOwnedAugmentations: (ctx: NetscriptContext) => function (_purchased: unknown = false): string[] { - _ctx.helper.checkSingularityAccess(); - const purchased = _ctx.helper.boolean(_purchased); + helpers.checkSingularityAccess(ctx); + const purchased = !!_purchased; const res = []; for (let i = 0; i < player.augmentations.length; ++i) { res.push(player.augmentations[i].name); @@ -123,74 +122,74 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript } return res; }, - getAugmentationsFromFaction: (_ctx: NetscriptContext) => + getAugmentationsFromFaction: (ctx: NetscriptContext) => function (_facName: unknown): string[] { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - const faction = getFaction(_ctx, facName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + const faction = getFaction(ctx, facName); return getFactionAugmentationsFiltered(player, faction); }, - getAugmentationCost: (_ctx: NetscriptContext) => + getAugmentationCost: (ctx: NetscriptContext) => function (_augName: unknown): [number, number] { - _ctx.helper.checkSingularityAccess(); - const augName = _ctx.helper.string("augName", _augName); - const aug = getAugmentation(_ctx, augName); + helpers.checkSingularityAccess(ctx); + const augName = helpers.string(ctx, "augName", _augName); + const aug = getAugmentation(ctx, augName); const costs = aug.getCost(player); return [costs.repCost, costs.moneyCost]; }, - getAugmentationPrereq: (_ctx: NetscriptContext) => + getAugmentationPrereq: (ctx: NetscriptContext) => function (_augName: unknown): string[] { - _ctx.helper.checkSingularityAccess(); - const augName = _ctx.helper.string("augName", _augName); - const aug = getAugmentation(_ctx, augName); + helpers.checkSingularityAccess(ctx); + const augName = helpers.string(ctx, "augName", _augName); + const aug = getAugmentation(ctx, augName); return aug.prereqs.slice(); }, - getAugmentationBasePrice: (_ctx: NetscriptContext) => + getAugmentationBasePrice: (ctx: NetscriptContext) => function (_augName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const augName = _ctx.helper.string("augName", _augName); - const aug = getAugmentation(_ctx, augName); + helpers.checkSingularityAccess(ctx); + const augName = helpers.string(ctx, "augName", _augName); + const aug = getAugmentation(ctx, augName); return aug.baseCost * BitNodeMultipliers.AugmentationMoneyCost; }, - getAugmentationPrice: (_ctx: NetscriptContext) => + getAugmentationPrice: (ctx: NetscriptContext) => function (_augName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const augName = _ctx.helper.string("augName", _augName); - const aug = getAugmentation(_ctx, augName); + helpers.checkSingularityAccess(ctx); + const augName = helpers.string(ctx, "augName", _augName); + const aug = getAugmentation(ctx, augName); return aug.getCost(player).moneyCost; }, - getAugmentationRepReq: (_ctx: NetscriptContext) => + getAugmentationRepReq: (ctx: NetscriptContext) => function (_augName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const augName = _ctx.helper.string("augName", _augName); - const aug = getAugmentation(_ctx, augName); + helpers.checkSingularityAccess(ctx); + const augName = helpers.string(ctx, "augName", _augName); + const aug = getAugmentation(ctx, augName); return aug.getCost(player).repCost; }, - getAugmentationStats: (_ctx: NetscriptContext) => + getAugmentationStats: (ctx: NetscriptContext) => function (_augName: unknown): AugmentationStats { - _ctx.helper.checkSingularityAccess(); - const augName = _ctx.helper.string("augName", _augName); - const aug = getAugmentation(_ctx, augName); + helpers.checkSingularityAccess(ctx); + const augName = helpers.string(ctx, "augName", _augName); + const aug = getAugmentation(ctx, augName); return Object.assign({}, aug.mults); }, - purchaseAugmentation: (_ctx: NetscriptContext) => + purchaseAugmentation: (ctx: NetscriptContext) => function (_facName: unknown, _augName: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - const augName = _ctx.helper.string("augName", _augName); - const fac = getFaction(_ctx, facName); - const aug = getAugmentation(_ctx, augName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + const augName = helpers.string(ctx, "augName", _augName); + const fac = getFaction(ctx, facName); + const aug = getAugmentation(ctx, augName); const augs = getFactionAugmentationsFiltered(player, fac); if (!player.factions.includes(fac.name)) { - _ctx.log(() => `You can't purchase augmentations from '${facName}' because you aren't a member`); + helpers.log(ctx, () => `You can't purchase augmentations from '${facName}' because you aren't a member`); return false; } if (!augs.includes(augName)) { - _ctx.log(() => `Faction '${facName}' does not have the '${augName}' augmentation.`); + helpers.log(ctx, () => `Faction '${facName}' does not have the '${augName}' augmentation.`); return false; } @@ -198,25 +197,25 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript if (!isNeuroflux) { for (let j = 0; j < player.queuedAugmentations.length; ++j) { if (player.queuedAugmentations[j].name === aug.name) { - _ctx.log(() => `You already have the '${augName}' augmentation.`); + helpers.log(ctx, () => `You already have the '${augName}' augmentation.`); return false; } } for (let j = 0; j < player.augmentations.length; ++j) { if (player.augmentations[j].name === aug.name) { - _ctx.log(() => `You already have the '${augName}' augmentation.`); + helpers.log(ctx, () => `You already have the '${augName}' augmentation.`); return false; } } } if (fac.playerReputation < aug.getCost(player).repCost) { - _ctx.log(() => `You do not have enough reputation with '${fac.name}'.`); + helpers.log(ctx, () => `You do not have enough reputation with '${fac.name}'.`); return false; } const res = purchaseAugmentation(aug, fac, true); - _ctx.log(() => res); + helpers.log(ctx, () => res); if (isString(res) && res.startsWith("You purchased")) { player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 10); return true; @@ -224,53 +223,53 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript return false; } }, - softReset: (_ctx: NetscriptContext) => + softReset: (ctx: NetscriptContext) => function (_cbScript: unknown = ""): void { - _ctx.helper.checkSingularityAccess(); - const cbScript = _ctx.helper.string("cbScript", _cbScript); + helpers.checkSingularityAccess(ctx); + const cbScript = helpers.string(ctx, "cbScript", _cbScript); - _ctx.log(() => "Soft resetting. This will cause this script to be killed"); + helpers.log(ctx, () => "Soft resetting. This will cause this script to be killed"); setTimeout(() => { installAugmentations(true); 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) => + installAugmentations: (ctx: NetscriptContext) => function (_cbScript: unknown = ""): boolean { - _ctx.helper.checkSingularityAccess(); - const cbScript = _ctx.helper.string("cbScript", _cbScript); + helpers.checkSingularityAccess(ctx); + const cbScript = helpers.string(ctx, "cbScript", _cbScript); if (player.queuedAugmentations.length === 0) { - _ctx.log(() => "You do not have any Augmentations to be installed."); + helpers.log(ctx, () => "You do not have any Augmentations to be installed."); return false; } player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 10); - _ctx.log(() => "Installing Augmentations. This will cause this script to be killed"); + helpers.log(ctx, () => "Installing Augmentations. This will cause this script to be killed"); setTimeout(() => { installAugmentations(); 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; }, - goToLocation: (_ctx: NetscriptContext) => + goToLocation: (ctx: NetscriptContext) => function (_locationName: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const locationName = _ctx.helper.string("locationName", _locationName); + helpers.checkSingularityAccess(ctx); + const locationName = helpers.string(ctx, "locationName", _locationName); const location = Object.values(Locations).find((l) => l.name === locationName); if (!location) { - _ctx.log(() => `No location named ${locationName}`); + helpers.log(ctx, () => `No location named ${locationName}`); return false; } if (location.city && player.city !== location.city) { - _ctx.log(() => `No location named ${locationName} in ${player.city}`); + helpers.log(ctx, () => `No location named ${locationName} in ${player.city}`); return false; } if (location.name === LocationName.TravelAgency) { @@ -283,32 +282,39 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000); return true; }, - universityCourse: (_ctx: NetscriptContext) => + universityCourse: (ctx: NetscriptContext) => function (_universityName: unknown, _className: unknown, _focus: unknown = true): boolean { - _ctx.helper.checkSingularityAccess(); - const universityName = _ctx.helper.string("universityName", _universityName); - const className = _ctx.helper.string("className", _className); - const focus = _ctx.helper.boolean(_focus); + helpers.checkSingularityAccess(ctx); + const universityName = helpers.string(ctx, "universityName", _universityName); + const className = helpers.string(ctx, "className", _className); + const focus = !!_focus; const wasFocusing = player.focus; switch (universityName.toLowerCase()) { case LocationName.AevumSummitUniversity.toLowerCase(): if (player.city != CityName.Aevum) { - _ctx.log(() => `You cannot study at 'Summit University' because you are not in '${CityName.Aevum}'.`); + helpers.log( + ctx, + () => `You cannot study at 'Summit University' because you are not in '${CityName.Aevum}'.`, + ); return false; } player.gotoLocation(LocationName.AevumSummitUniversity); break; case LocationName.Sector12RothmanUniversity.toLowerCase(): if (player.city != CityName.Sector12) { - _ctx.log(() => `You cannot study at 'Rothman University' because you are not in '${CityName.Sector12}'.`); + helpers.log( + ctx, + () => `You cannot study at 'Rothman University' because you are not in '${CityName.Sector12}'.`, + ); return false; } player.location = LocationName.Sector12RothmanUniversity; break; case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase(): if (player.city != CityName.Volhaven) { - _ctx.log( + helpers.log( + ctx, () => `You cannot study at 'ZB Institute of Technology' because you are not in '${CityName.Volhaven}'.`, ); return false; @@ -316,7 +322,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.location = LocationName.VolhavenZBInstituteOfTechnology; break; default: - _ctx.log(() => `Invalid university name: '${universityName}'.`); + helpers.log(ctx, () => `Invalid university name: '${universityName}'.`); return false; } @@ -341,7 +347,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript task = ClassType.Leadership; break; default: - _ctx.log(() => `Invalid class name: ${className}.`); + helpers.log(ctx, () => `Invalid class name: ${className}.`); return false; } player.startWork( @@ -358,22 +364,23 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.stopFocusing(); Router.toTerminal(); } - _ctx.log(() => `Started ${task} at ${universityName}`); + helpers.log(ctx, () => `Started ${task} at ${universityName}`); return true; }, - gymWorkout: (_ctx: NetscriptContext) => + gymWorkout: (ctx: NetscriptContext) => function (_gymName: unknown, _stat: unknown, _focus: unknown = true): boolean { - _ctx.helper.checkSingularityAccess(); - const gymName = _ctx.helper.string("gymName", _gymName); - const stat = _ctx.helper.string("stat", _stat); - const focus = _ctx.helper.boolean(_focus); + helpers.checkSingularityAccess(ctx); + const gymName = helpers.string(ctx, "gymName", _gymName); + const stat = helpers.string(ctx, "stat", _stat); + const focus = !!_focus; const wasFocusing = player.focus; switch (gymName.toLowerCase()) { case LocationName.AevumCrushFitnessGym.toLowerCase(): if (player.city != CityName.Aevum) { - _ctx.log( + helpers.log( + ctx, () => `You cannot workout at '${LocationName.AevumCrushFitnessGym}' because you are not in '${CityName.Aevum}'.`, ); @@ -383,7 +390,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript break; case LocationName.AevumSnapFitnessGym.toLowerCase(): if (player.city != CityName.Aevum) { - _ctx.log( + helpers.log( + ctx, () => `You cannot workout at '${LocationName.AevumSnapFitnessGym}' because you are not in '${CityName.Aevum}'.`, ); @@ -393,7 +401,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript break; case LocationName.Sector12IronGym.toLowerCase(): if (player.city != CityName.Sector12) { - _ctx.log( + helpers.log( + ctx, () => `You cannot workout at '${LocationName.Sector12IronGym}' because you are not in '${CityName.Sector12}'.`, ); @@ -403,7 +412,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript break; case LocationName.Sector12PowerhouseGym.toLowerCase(): if (player.city != CityName.Sector12) { - _ctx.log( + helpers.log( + ctx, () => `You cannot workout at '${LocationName.Sector12PowerhouseGym}' because you are not in '${CityName.Sector12}'.`, ); @@ -413,7 +423,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript break; case LocationName.VolhavenMilleniumFitnessGym.toLowerCase(): if (player.city != CityName.Volhaven) { - _ctx.log( + helpers.log( + ctx, () => `You cannot workout at '${LocationName.VolhavenMilleniumFitnessGym}' because you are not in '${CityName.Volhaven}'.`, ); @@ -422,7 +433,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.location = LocationName.VolhavenMilleniumFitnessGym; break; default: - _ctx.log(() => `Invalid gym name: ${gymName}. gymWorkout() failed`); + helpers.log(ctx, () => `Invalid gym name: ${gymName}. gymWorkout() failed`); return false; } @@ -452,7 +463,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript ); break; default: - _ctx.log(() => `Invalid stat: ${stat}.`); + helpers.log(ctx, () => `Invalid stat: ${stat}.`); return false; } if (focus) { @@ -462,14 +473,14 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.stopFocusing(); Router.toTerminal(); } - _ctx.log(() => `Started training ${stat} at ${gymName}`); + helpers.log(ctx, () => `Started training ${stat} at ${gymName}`); return true; }, - travelToCity: (_ctx: NetscriptContext) => + travelToCity: (ctx: NetscriptContext) => function (_cityName: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const cityName = _ctx.helper.city("cityName", _cityName); + helpers.checkSingularityAccess(ctx); + const cityName = helpers.city(ctx, "cityName", _cityName); switch (cityName) { case CityName.Aevum: @@ -479,68 +490,69 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript case CityName.Ishima: case CityName.Volhaven: if (player.money < CONSTANTS.TravelCost) { - _ctx.log(() => "Not enough money to travel."); + helpers.log(ctx, () => "Not enough money to travel."); return false; } player.loseMoney(CONSTANTS.TravelCost, "other"); player.city = cityName; - _ctx.log(() => `Traveled to ${cityName}`); + helpers.log(ctx, () => `Traveled to ${cityName}`); player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000); return true; default: - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid city name: '${cityName}'.`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city name: '${cityName}'.`); } }, - purchaseTor: (_ctx: NetscriptContext) => + purchaseTor: (ctx: NetscriptContext) => function (): boolean { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); if (player.hasTorRouter()) { - _ctx.log(() => "You already have a TOR router!"); + helpers.log(ctx, () => "You already have a TOR router!"); return true; } if (player.money < CONSTANTS.TorRouterCost) { - _ctx.log(() => "You cannot afford to purchase a Tor router."); + helpers.log(ctx, () => "You cannot afford to purchase a Tor router."); return false; } player.loseMoney(CONSTANTS.TorRouterCost, "other"); const darkweb = GetServer(SpecialServers.DarkWeb); - if (!darkweb) throw _ctx.makeRuntimeErrorMsg("DarkWeb was not a server but should have been"); + if (!darkweb) throw helpers.makeRuntimeErrorMsg(ctx, "DarkWeb was not a server but should have been"); player.getHomeComputer().serversOnNetwork.push(darkweb.hostname); darkweb.serversOnNetwork.push(player.getHomeComputer().hostname); player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 500); - _ctx.log(() => "You have purchased a Tor router!"); + helpers.log(ctx, () => "You have purchased a Tor router!"); return true; }, - purchaseProgram: (_ctx: NetscriptContext) => + purchaseProgram: (ctx: NetscriptContext) => function (_programName: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const programName = _ctx.helper.string("programName", _programName).toLowerCase(); + helpers.checkSingularityAccess(ctx); + const programName = helpers.string(ctx, "programName", _programName).toLowerCase(); if (!player.hasTorRouter()) { - _ctx.log(() => "You do not have the TOR router."); + helpers.log(ctx, () => "You do not have the TOR router."); return false; } const item = Object.values(DarkWebItems).find((i) => i.program.toLowerCase() === programName); if (item == null) { - _ctx.log(() => `Invalid program name: '${programName}.`); + helpers.log(ctx, () => `Invalid program name: '${programName}.`); return false; } if (player.money < item.price) { - _ctx.log( + helpers.log( + ctx, () => `Not enough money to purchase '${item.program}'. Need ${numeralWrapper.formatMoney(item.price)}`, ); return false; } if (player.hasProgram(item.program)) { - _ctx.log(() => `You already have the '${item.program}' program`); + helpers.log(ctx, () => `You already have the '${item.program}' program`); return true; } @@ -551,28 +563,29 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript } player.loseMoney(item.price, "other"); - _ctx.log( + helpers.log( + ctx, () => `You have purchased the '${item.program}' program. The new program can be found on your home computer.`, ); player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 5000); return true; }, - getCurrentServer: (_ctx: NetscriptContext) => + getCurrentServer: (ctx: NetscriptContext) => function (): string { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); return player.getCurrentServer().hostname; }, - connect: (_ctx: NetscriptContext) => + connect: (ctx: NetscriptContext) => function (_hostname: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const hostname = _ctx.helper.string("hostname", _hostname); + helpers.checkSingularityAccess(ctx); + const hostname = helpers.string(ctx, "hostname", _hostname); if (!hostname) { - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid hostname: '${hostname}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid hostname: '${hostname}'`); } const target = GetServer(hostname); if (target == null) { - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid hostname: '${hostname}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid hostname: '${hostname}'`); } //Home case @@ -611,17 +624,17 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript //Failure case return false; }, - manualHack: (_ctx: NetscriptContext) => + manualHack: (ctx: NetscriptContext) => function (): Promise { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); const server = player.getCurrentServer(); - return _ctx.helper.hack(server.hostname, true); + return helpers.hack(ctx, server.hostname, true); }, - installBackdoor: (_ctx: NetscriptContext) => async (): Promise => { - _ctx.helper.checkSingularityAccess(); + installBackdoor: (ctx: NetscriptContext) => async (): Promise => { + helpers.checkSingularityAccess(ctx); const baseserver = player.getCurrentServer(); if (!(baseserver instanceof Server)) { - _ctx.log(() => "cannot backdoor this kind of server"); + helpers.log(ctx, () => "cannot backdoor this kind of server"); return Promise.resolve(); } const server = baseserver; @@ -630,15 +643,16 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript // No root access or skill level too low const canHack = netscriptCanHack(server, player); if (!canHack.res) { - throw _ctx.helper.makeRuntimeErrorMsg(canHack.msg || ""); + throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || ""); } - _ctx.log( + helpers.log( + ctx, () => `Installing backdoor on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(installTime, true)}`, ); - return netscriptDelay(installTime, workerScript).then(function () { - _ctx.log(() => `Successfully installed backdoor on '${server.hostname}'`); + return helpers.netscriptDelay(ctx, installTime).then(function () { + helpers.log(ctx, () => `Successfully installed backdoor on '${server.hostname}'`); server.backdoorInstalled = true; @@ -648,17 +662,17 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript return Promise.resolve(); }); }, - isFocused: (_ctx: NetscriptContext) => + isFocused: (ctx: NetscriptContext) => function (): boolean { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); return player.focus; }, - setFocus: (_ctx: NetscriptContext) => + setFocus: (ctx: NetscriptContext) => function (_focus: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const focus = _ctx.helper.boolean(_focus); + helpers.checkSingularityAccess(ctx); + const focus = !!_focus; if (player.currentWork === null) { - throw _ctx.helper.makeRuntimeErrorMsg("Not currently working"); + throw helpers.makeRuntimeErrorMsg(ctx, "Not currently working"); } if (!player.focus && focus) { @@ -672,41 +686,41 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript } return false; }, - hospitalize: (_ctx: NetscriptContext) => + hospitalize: (ctx: NetscriptContext) => function (): void { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); if (player.currentWork || Router.page() === Page.Infiltration || Router.page() === Page.BitVerse) { - _ctx.log(() => "Cannot go to the hospital because the player is busy."); + helpers.log(ctx, () => "Cannot go to the hospital because the player is busy."); return; } player.hospitalize(); }, - isBusy: (_ctx: NetscriptContext) => + isBusy: (ctx: NetscriptContext) => function (): boolean { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); return player.currentWork !== null || Router.page() === Page.Infiltration || Router.page() === Page.BitVerse; }, - stopAction: (_ctx: NetscriptContext) => + stopAction: (ctx: NetscriptContext) => function (): boolean { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); const wasWorking = player.currentWork !== null; player.finishWork(true); return wasWorking; }, - upgradeHomeCores: (_ctx: NetscriptContext) => + upgradeHomeCores: (ctx: NetscriptContext) => function (): boolean { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); // Check if we're at max cores const homeComputer = player.getHomeComputer(); if (homeComputer.cpuCores >= 8) { - _ctx.log(() => `Your home computer is at max cores.`); + helpers.log(ctx, () => `Your home computer is at max cores.`); return false; } const cost = player.getUpgradeHomeCoresCost(); if (player.money < cost) { - _ctx.log(() => `You don't have enough money. Need ${numeralWrapper.formatMoney(cost)}`); + helpers.log(ctx, () => `You don't have enough money. Need ${numeralWrapper.formatMoney(cost)}`); return false; } @@ -714,29 +728,32 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.loseMoney(cost, "servers"); player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 2); - _ctx.log(() => `Purchased an additional core for home computer! It now has ${homeComputer.cpuCores} cores.`); + helpers.log( + ctx, + () => `Purchased an additional core for home computer! It now has ${homeComputer.cpuCores} cores.`, + ); return true; }, - getUpgradeHomeCoresCost: (_ctx: NetscriptContext) => + getUpgradeHomeCoresCost: (ctx: NetscriptContext) => function (): number { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); return player.getUpgradeHomeCoresCost(); }, - upgradeHomeRam: (_ctx: NetscriptContext) => + upgradeHomeRam: (ctx: NetscriptContext) => function (): boolean { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); // Check if we're at max RAM const homeComputer = player.getHomeComputer(); if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) { - _ctx.log(() => `Your home computer is at max RAM.`); + helpers.log(ctx, () => `Your home computer is at max RAM.`); return false; } const cost = player.getUpgradeHomeRamCost(); if (player.money < cost) { - _ctx.log(() => `You don't have enough money. Need ${numeralWrapper.formatMoney(cost)}`); + helpers.log(ctx, () => `You don't have enough money. Need ${numeralWrapper.formatMoney(cost)}`); return false; } @@ -744,7 +761,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.loseMoney(cost, "servers"); player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 2); - _ctx.log( + helpers.log( + ctx, () => `Purchased additional RAM for home computer! It now has ${numeralWrapper.formatRAM( homeComputer.maxRam, @@ -752,27 +770,27 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript ); return true; }, - getUpgradeHomeRamCost: (_ctx: NetscriptContext) => + getUpgradeHomeRamCost: (ctx: NetscriptContext) => function (): number { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); return player.getUpgradeHomeRamCost(); }, - workForCompany: (_ctx: NetscriptContext) => + workForCompany: (ctx: NetscriptContext) => function (_companyName: unknown, _focus: unknown = true): boolean { - _ctx.helper.checkSingularityAccess(); - const companyName = _ctx.helper.string("companyName", _companyName); - const focus = _ctx.helper.boolean(_focus); + helpers.checkSingularityAccess(ctx); + const companyName = helpers.string(ctx, "companyName", _companyName); + const focus = !_focus; // Make sure its a valid company if (companyName == null || companyName === "" || !(Companies[companyName] instanceof Company)) { - _ctx.log(() => `Invalid company: '${companyName}'`); + helpers.log(ctx, () => `Invalid company: '${companyName}'`); return false; } // Make sure player is actually employed at the comapny if (!Object.keys(player.jobs).includes(companyName)) { - _ctx.log(() => `You do not have a job at '${companyName}'`); + helpers.log(ctx, () => `You do not have a job at '${companyName}'`); return false; } @@ -780,7 +798,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript const companyPositionName = player.jobs[companyName]; const companyPosition = CompanyPositions[companyPositionName]; if (companyPositionName === "" || !(companyPosition instanceof CompanyPosition)) { - _ctx.log(() => "You do not have a job"); + helpers.log(ctx, () => "You do not have a job"); return false; } @@ -799,15 +817,15 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.stopFocusing(); Router.toTerminal(); } - _ctx.log(() => `Began working at '${companyName}' as a '${companyPositionName}'`); + helpers.log(ctx, () => `Began working at '${companyName}' as a '${companyPositionName}'`); return true; }, - applyToCompany: (_ctx: NetscriptContext) => + applyToCompany: (ctx: NetscriptContext) => function (_companyName: unknown, _field: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const companyName = _ctx.helper.string("companyName", _companyName); - const field = _ctx.helper.string("field", _field); - getCompany(_ctx, companyName); + helpers.checkSingularityAccess(ctx); + const companyName = helpers.string(ctx, "companyName", _companyName); + const field = helpers.string(ctx, "field", _field); + getCompany(ctx, companyName); player.location = companyName as LocationName; let res; @@ -852,63 +870,63 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript res = player.applyForPartTimeWaiterJob(true); break; default: - _ctx.log(() => `Invalid job: '${field}'.`); + helpers.log(ctx, () => `Invalid job: '${field}'.`); return false; } // TODO https://github.com/danielyxie/bitburner/issues/1378 // The player object's applyForJob function can return string with special error messages // if (isString(res)) { - // _ctx.log("applyToCompany",()=> res); + // helpers.log(ctx,"applyToCompany",()=> res); // return false; // } if (res) { - _ctx.log(() => `You were offered a new job at '${companyName}' as a '${player.jobs[companyName]}'`); + helpers.log(ctx, () => `You were offered a new job at '${companyName}' as a '${player.jobs[companyName]}'`); } else { - _ctx.log(() => `You failed to get a new job/promotion at '${companyName}' in the '${field}' field.`); + helpers.log(ctx, () => `You failed to get a new job/promotion at '${companyName}' in the '${field}' field.`); } return res; }, - quitJob: (_ctx: NetscriptContext) => + quitJob: (ctx: NetscriptContext) => function (_companyName: unknown): void { - _ctx.helper.checkSingularityAccess(); - const companyName = _ctx.helper.string("companyName", _companyName); + helpers.checkSingularityAccess(ctx); + const companyName = helpers.string(ctx, "companyName", _companyName); player.quitJob(companyName); }, - getCompanyRep: (_ctx: NetscriptContext) => + getCompanyRep: (ctx: NetscriptContext) => function (_companyName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const companyName = _ctx.helper.string("companyName", _companyName); - const company = getCompany(_ctx, companyName); + helpers.checkSingularityAccess(ctx); + const companyName = helpers.string(ctx, "companyName", _companyName); + const company = getCompany(ctx, companyName); return company.playerReputation; }, - getCompanyFavor: (_ctx: NetscriptContext) => + getCompanyFavor: (ctx: NetscriptContext) => function (_companyName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const companyName = _ctx.helper.string("companyName", _companyName); - const company = getCompany(_ctx, companyName); + helpers.checkSingularityAccess(ctx); + const companyName = helpers.string(ctx, "companyName", _companyName); + const company = getCompany(ctx, companyName); return company.favor; }, - getCompanyFavorGain: (_ctx: NetscriptContext) => + getCompanyFavorGain: (ctx: NetscriptContext) => function (_companyName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const companyName = _ctx.helper.string("companyName", _companyName); - const company = getCompany(_ctx, companyName); + helpers.checkSingularityAccess(ctx); + const companyName = helpers.string(ctx, "companyName", _companyName); + const company = getCompany(ctx, companyName); return company.getFavorGain(); }, - checkFactionInvitations: (_ctx: NetscriptContext) => + checkFactionInvitations: (ctx: NetscriptContext) => function (): string[] { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); // Make a copy of player.factionInvitations return player.factionInvitations.slice(); }, - joinFaction: (_ctx: NetscriptContext) => + joinFaction: (ctx: NetscriptContext) => function (_facName: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - getFaction(_ctx, facName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + getFaction(ctx, facName); if (!player.factionInvitations.includes(facName)) { - _ctx.log(() => `You have not been invited by faction '${facName}'`); + helpers.log(ctx, () => `You have not been invited by faction '${facName}'`); return false; } const fac = Factions[facName]; @@ -922,25 +940,25 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript } } player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 5); - _ctx.log(() => `Joined the '${facName}' faction.`); + helpers.log(ctx, () => `Joined the '${facName}' faction.`); return true; }, - workForFaction: (_ctx: NetscriptContext) => + workForFaction: (ctx: NetscriptContext) => function (_facName: unknown, _type: unknown, _focus: unknown = true): boolean { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - const type = _ctx.helper.string("type", _type); - const focus = _ctx.helper.boolean(_focus); - const faction = getFaction(_ctx, facName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + const type = helpers.string(ctx, "type", _type); + const focus = !!_focus; + const faction = getFaction(ctx, facName); // if the player is in a gang and the target faction is any of the gang faction, fail if (player.inGang() && faction.name === player.getGangFaction().name) { - _ctx.log(() => `You can't work for '${facName}' because youre managing a gang for it`); + helpers.log(ctx, () => `You can't work for '${facName}' because youre managing a gang for it`); return false; } if (!player.factions.includes(facName)) { - _ctx.log(() => `You are not a member of '${facName}'`); + helpers.log(ctx, () => `You are not a member of '${facName}'`); return false; } @@ -951,7 +969,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript case "hacking contracts": case "hackingcontracts": if (!FactionInfos[faction.name].offerHackingWork) { - _ctx.log(() => `Faction '${faction.name}' do not need help with hacking contracts.`); + helpers.log(ctx, () => `Faction '${faction.name}' do not need help with hacking contracts.`); return false; } player.startWork( @@ -968,13 +986,13 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.stopFocusing(); Router.toTerminal(); } - _ctx.log(() => `Started carrying out hacking contracts for '${faction.name}'`); + helpers.log(ctx, () => `Started carrying out hacking contracts for '${faction.name}'`); return true; case "field": case "fieldwork": case "field work": if (!FactionInfos[faction.name].offerFieldWork) { - _ctx.log(() => `Faction '${faction.name}' do not need help with field missions.`); + helpers.log(ctx, () => `Faction '${faction.name}' do not need help with field missions.`); return false; } player.startWork( @@ -991,13 +1009,13 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.stopFocusing(); Router.toTerminal(); } - _ctx.log(() => `Started carrying out field missions for '${faction.name}'`); + helpers.log(ctx, () => `Started carrying out field missions for '${faction.name}'`); return true; case "security": case "securitywork": case "security work": if (!FactionInfos[faction.name].offerSecurityWork) { - _ctx.log(() => `Faction '${faction.name}' do not need help with security work.`); + helpers.log(ctx, () => `Faction '${faction.name}' do not need help with security work.`); return false; } player.startWork( @@ -1014,63 +1032,67 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.stopFocusing(); Router.toTerminal(); } - _ctx.log(() => `Started carrying out security work for '${faction.name}'`); + helpers.log(ctx, () => `Started carrying out security work for '${faction.name}'`); return true; default: - _ctx.log(() => `Invalid work type: '${type}`); + helpers.log(ctx, () => `Invalid work type: '${type}`); return false; } }, - getFactionRep: (_ctx: NetscriptContext) => + getFactionRep: (ctx: NetscriptContext) => function (_facName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - const faction = getFaction(_ctx, facName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + const faction = getFaction(ctx, facName); return faction.playerReputation; }, - getFactionFavor: (_ctx: NetscriptContext) => + getFactionFavor: (ctx: NetscriptContext) => function (_facName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - const faction = getFaction(_ctx, facName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + const faction = getFaction(ctx, facName); return faction.favor; }, - getFactionFavorGain: (_ctx: NetscriptContext) => + getFactionFavorGain: (ctx: NetscriptContext) => function (_facName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - const faction = getFaction(_ctx, facName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + const faction = getFaction(ctx, facName); return faction.getFavorGain(); }, - donateToFaction: (_ctx: NetscriptContext) => + donateToFaction: (ctx: NetscriptContext) => function (_facName: unknown, _amt: unknown): boolean { - _ctx.helper.checkSingularityAccess(); - const facName = _ctx.helper.string("facName", _facName); - const amt = _ctx.helper.number("amt", _amt); - const faction = getFaction(_ctx, facName); + helpers.checkSingularityAccess(ctx); + const facName = helpers.string(ctx, "facName", _facName); + const amt = helpers.number(ctx, "amt", _amt); + const faction = getFaction(ctx, facName); if (!player.factions.includes(faction.name)) { - _ctx.log(() => `You can't donate to '${facName}' because you aren't a member`); + helpers.log(ctx, () => `You can't donate to '${facName}' because you aren't a member`); return false; } if (player.inGang() && faction.name === player.getGangFaction().name) { - _ctx.log(() => `You can't donate to '${facName}' because youre managing a gang for it`); + helpers.log(ctx, () => `You can't donate to '${facName}' because youre managing a gang for it`); return false; } if (faction.name === FactionNames.ChurchOfTheMachineGod || faction.name === FactionNames.Bladeburners) { - _ctx.log(() => `You can't donate to '${facName}' because they do not accept donations`); + helpers.log(ctx, () => `You can't donate to '${facName}' because they do not accept donations`); return false; } if (typeof amt !== "number" || amt <= 0 || isNaN(amt)) { - _ctx.log(() => `Invalid donation amount: '${amt}'.`); + helpers.log(ctx, () => `Invalid donation amount: '${amt}'.`); return false; } if (player.money < amt) { - _ctx.log(() => `You do not have enough money to donate ${numeralWrapper.formatMoney(amt)} to '${facName}'`); + helpers.log( + ctx, + () => `You do not have enough money to donate ${numeralWrapper.formatMoney(amt)} to '${facName}'`, + ); return false; } const repNeededToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); if (faction.favor < repNeededToDonate) { - _ctx.log( + helpers.log( + ctx, () => `You do not have enough favor to donate to this faction. Have ${faction.favor}, need ${repNeededToDonate}`, ); @@ -1079,7 +1101,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript const repGain = (amt / CONSTANTS.DonateMoneyToRepDivisor) * player.mults.faction_rep; faction.playerReputation += repGain; player.loseMoney(amt, "other"); - _ctx.log( + helpers.log( + ctx, () => `${numeralWrapper.formatMoney(amt)} donated to '${facName}' for ${numeralWrapper.formatReputation( repGain, @@ -1087,34 +1110,34 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript ); return true; }, - createProgram: (_ctx: NetscriptContext) => + createProgram: (ctx: NetscriptContext) => function (_programName: unknown, _focus: unknown = true): boolean { - _ctx.helper.checkSingularityAccess(); - const programName = _ctx.helper.string("programName", _programName).toLowerCase(); - const focus = _ctx.helper.boolean(_focus); + helpers.checkSingularityAccess(ctx); + const programName = helpers.string(ctx, "programName", _programName).toLowerCase(); + const focus = !!_focus; const wasFocusing = player.focus; const p = Object.values(Programs).find((p) => p.name.toLowerCase() === programName); if (p == null) { - _ctx.log(() => `The specified program does not exist: '${programName}`); + helpers.log(ctx, () => `The specified program does not exist: '${programName}`); return false; } if (player.hasProgram(p.name)) { - _ctx.log(() => `You already have the '${p.name}' program`); + helpers.log(ctx, () => `You already have the '${p.name}' program`); return false; } const create = p.create; if (create === null) { - _ctx.log(() => `You cannot create the '${p.name}' program`); + helpers.log(ctx, () => `You cannot create the '${p.name}' program`); return false; } if (!create.req(player)) { - _ctx.log(() => `Hacking level is too low to create '${p.name}' (level ${create.level} req)`); + helpers.log(ctx, () => `Hacking level is too low to create '${p.name}' (level ${create.level} req)`); return false; } @@ -1132,14 +1155,14 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.stopFocusing(); Router.toTerminal(); } - _ctx.log(() => `Began creating program: '${programName}'`); + helpers.log(ctx, () => `Began creating program: '${programName}'`); return true; }, - commitCrime: (_ctx: NetscriptContext) => + commitCrime: (ctx: NetscriptContext) => function (_crimeRoughName: unknown, _focus: unknown = true): number { - _ctx.helper.checkSingularityAccess(); - const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName); - const focus = _ctx.helper.boolean(_focus); + helpers.checkSingularityAccess(ctx); + const crimeRoughName = helpers.string(ctx, "crimeRoughName", _crimeRoughName); + const focus = !!_focus; const wasFocusing = player.focus; if (player.currentWork !== null) { @@ -1152,10 +1175,10 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript const crime = findCrime(crimeRoughName.toLowerCase()); if (crime == null) { // couldn't find crime - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid crime: '${crimeRoughName}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeRoughName}'`); } - _ctx.log(() => `Attempting to commit ${crime.name}...`); - const crimeTime = crime.commit(player, 1, workerScript); + helpers.log(ctx, () => `Attempting to commit ${crime.name}...`); + const crimeTime = crime.commit(player, 1, ctx.workerScript); if (focus) { player.startFocusing(); Router.toWork(); @@ -1165,49 +1188,49 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript } return crimeTime; }, - getCrimeChance: (_ctx: NetscriptContext) => + getCrimeChance: (ctx: NetscriptContext) => function (_crimeRoughName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName); + helpers.checkSingularityAccess(ctx); + const crimeRoughName = helpers.string(ctx, "crimeRoughName", _crimeRoughName); const crime = findCrime(crimeRoughName.toLowerCase()); if (crime == null) { - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid crime: ${crimeRoughName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: ${crimeRoughName}`); } return crime.successRate(player); }, - getCrimeStats: (_ctx: NetscriptContext) => + getCrimeStats: (ctx: NetscriptContext) => function (_crimeRoughName: unknown): CrimeStats { - _ctx.helper.checkSingularityAccess(); - const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName); + helpers.checkSingularityAccess(ctx); + const crimeRoughName = helpers.string(ctx, "crimeRoughName", _crimeRoughName); const crime = findCrime(crimeRoughName.toLowerCase()); if (crime == null) { - throw _ctx.helper.makeRuntimeErrorMsg(`Invalid crime: ${crimeRoughName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: ${crimeRoughName}`); } return Object.assign({}, crime); }, - getDarkwebPrograms: (_ctx: NetscriptContext) => + getDarkwebPrograms: (ctx: NetscriptContext) => function (): string[] { - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); // If we don't have Tor, log it and return [] (empty list) if (!player.hasTorRouter()) { - _ctx.log(() => "You do not have the TOR router."); + helpers.log(ctx, () => "You do not have the TOR router."); return []; } return Object.values(DarkWebItems).map((p) => p.program); }, - getDarkwebProgramCost: (_ctx: NetscriptContext) => + getDarkwebProgramCost: (ctx: NetscriptContext) => function (_programName: unknown): number { - _ctx.helper.checkSingularityAccess(); - const programName = _ctx.helper.string("programName", _programName).toLowerCase(); + helpers.checkSingularityAccess(ctx); + const programName = helpers.string(ctx, "programName", _programName).toLowerCase(); // If we don't have Tor, log it and return -1 if (!player.hasTorRouter()) { - _ctx.log(() => "You do not have the TOR router."); + helpers.log(ctx, () => "You do not have the TOR router."); // returning -1 rather than throwing an error to be consistent with purchaseProgram // which returns false if tor has return -1; @@ -1220,25 +1243,26 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript // doesn't exist, it's the first time they've run the script. So throw an error to let them know // that they need to fix it. if (item == null) { - throw _ctx.helper.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, `No such exploit ('${programName}') found on the darkweb! ` + `\nThis function is not case-sensitive. Did you perhaps forget .exe at the end?`, ); } if (player.hasProgram(item.program)) { - _ctx.log(() => `You already have the '${item.program}' program`); + helpers.log(ctx, () => `You already have the '${item.program}' program`); return 0; } return item.price; }, b1tflum3: - (_ctx: NetscriptContext) => + (ctx: NetscriptContext) => (_nextBN: unknown, _callbackScript: unknown = ""): void => { - _ctx.helper.checkSingularityAccess(); - const nextBN = _ctx.helper.number("nextBN", _nextBN); - const callbackScript = _ctx.helper.string("callbackScript", _callbackScript); - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); + const nextBN = helpers.number(ctx, "nextBN", _nextBN); + const callbackScript = helpers.string(ctx, "callbackScript", _callbackScript); + helpers.checkSingularityAccess(ctx); enterBitNode(Router, true, player.bitNodeN, nextBN); if (callbackScript) setTimeout(() => { @@ -1246,12 +1270,12 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript }, 0); }, destroyW0r1dD43m0n: - (_ctx: NetscriptContext) => + (ctx: NetscriptContext) => (_nextBN: unknown, _callbackScript: unknown = ""): void => { - _ctx.helper.checkSingularityAccess(); - const nextBN = _ctx.helper.number("nextBN", _nextBN); - const callbackScript = _ctx.helper.string("callbackScript", _callbackScript); - _ctx.helper.checkSingularityAccess(); + helpers.checkSingularityAccess(ctx); + const nextBN = helpers.number(ctx, "nextBN", _nextBN); + const callbackScript = helpers.string(ctx, "callbackScript", _callbackScript); + helpers.checkSingularityAccess(ctx); const hackingRequirements = (): boolean => { const wd = GetServer(SpecialServers.WorldDaemon); @@ -1268,7 +1292,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript }; if (!hackingRequirements() && !bladeburnerRequirements()) { - _ctx.log(() => "Requirements not met to destroy the world daemon"); + helpers.log(ctx, () => "Requirements not met to destroy the world daemon"); return; } diff --git a/src/NetscriptFunctions/Sleeve.ts b/src/NetscriptFunctions/Sleeve.ts index eff3d882b..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"; @@ -17,11 +17,13 @@ import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { isSleeveBladeburnerWork } from "../PersonObjects/Sleeve/Work/SleeveBladeburnerWork"; import { isSleeveFactionWork } from "../PersonObjects/Sleeve/Work/SleeveFactionWork"; 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 ctx.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, "You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10", ); } @@ -30,8 +32,8 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number): void { if (sleeveNumber >= player.sleeves.length || sleeveNumber < 0) { const msg = `Invalid sleeve number: ${sleeveNumber}`; - ctx.log(() => msg); - throw ctx.makeRuntimeErrorMsg(msg); + helpers.log(ctx, () => msg); + throw helpers.makeRuntimeErrorMsg(ctx, msg); } }; @@ -58,7 +60,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { setToShockRecovery: (ctx: NetscriptContext) => (_sleeveNumber: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); return player.sleeves[sleeveNumber].shockRecovery(player); @@ -66,7 +68,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { setToSynchronize: (ctx: NetscriptContext) => (_sleeveNumber: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); return player.sleeves[sleeveNumber].synchronize(player); @@ -74,8 +76,8 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { setToCommitCrime: (ctx: NetscriptContext) => (_sleeveNumber: unknown, _crimeRoughName: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const crimeRoughName = ctx.helper.string("crimeName", _crimeRoughName); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const crimeRoughName = helpers.string(ctx, "crimeName", _crimeRoughName); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); const crime = findCrime(crimeRoughName); @@ -87,9 +89,9 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { setToUniversityCourse: (ctx: NetscriptContext) => (_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const universityName = ctx.helper.string("universityName", _universityName); - const className = ctx.helper.string("className", _className); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const universityName = helpers.string(ctx, "universityName", _universityName); + const className = helpers.string(ctx, "className", _className); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className); @@ -97,21 +99,21 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { travel: (ctx: NetscriptContext) => (_sleeveNumber: unknown, _cityName: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const cityName = ctx.helper.string("cityName", _cityName); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const cityName = helpers.string(ctx, "cityName", _cityName); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); if (checkEnum(CityName, cityName)) { return player.sleeves[sleeveNumber].travel(player, cityName); } else { - throw ctx.makeRuntimeErrorMsg(`Invalid city name: '${cityName}'.`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city name: '${cityName}'.`); } }, setToCompanyWork: (ctx: NetscriptContext) => (_sleeveNumber: unknown, acompanyName: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const companyName = ctx.helper.string("companyName", acompanyName); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const companyName = helpers.string(ctx, "companyName", acompanyName); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -122,7 +124,8 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { } const other = player.sleeves[i]; if (isSleeveCompanyWork(other.currentWork) && other.currentWork.companyName === companyName) { - throw ctx.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, `Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`, ); } @@ -133,9 +136,9 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { setToFactionWork: (ctx: NetscriptContext) => (_sleeveNumber: unknown, _factionName: unknown, _workType: unknown): boolean | undefined => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const factionName = ctx.helper.string("factionName", _factionName); - const workType = ctx.helper.string("workType", _workType); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const factionName = helpers.string(ctx, "factionName", _factionName); + const workType = helpers.string(ctx, "workType", _workType); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -146,14 +149,16 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { } const other = player.sleeves[i]; if (isSleeveFactionWork(other.currentWork) && other.currentWork.factionName === factionName) { - throw ctx.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, `Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`, ); } } if (player.gang && player.gang.facName == factionName) { - throw ctx.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, `Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`, ); } @@ -163,9 +168,9 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { setToGymWorkout: (ctx: NetscriptContext) => (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const gymName = ctx.helper.string("gymName", _gymName); - const stat = ctx.helper.string("stat", _stat); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const gymName = helpers.string(ctx, "gymName", _gymName); + const stat = helpers.string(ctx, "stat", _stat); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -174,7 +179,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { getSleeveStats: (ctx: NetscriptContext) => (_sleeveNumber: unknown): SleeveSkills => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); return getSleeveStats(sleeveNumber); @@ -182,7 +187,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { getTask: (ctx: NetscriptContext) => (_sleeveNumber: unknown): SleeveTask | null => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -193,7 +198,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { getInformation: (ctx: NetscriptContext) => (_sleeveNumber: unknown): SleeveInformation => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -229,7 +234,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { getSleeveAugmentations: (ctx: NetscriptContext) => (_sleeveNumber: unknown): string[] => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -242,7 +247,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { getSleevePurchasableAugs: (ctx: NetscriptContext) => (_sleeveNumber: unknown): AugmentPair[] => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -261,18 +266,18 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { purchaseSleeveAug: (ctx: NetscriptContext) => (_sleeveNumber: unknown, _augName: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const augName = ctx.helper.string("augName", _augName); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const augName = helpers.string(ctx, "augName", _augName); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); if (getSleeveStats(sleeveNumber).shock > 0) { - throw ctx.makeRuntimeErrorMsg(`Sleeve shock too high: Sleeve ${sleeveNumber}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Sleeve shock too high: Sleeve ${sleeveNumber}`); } const aug = StaticAugmentations[augName]; if (!aug) { - throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); } return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug); @@ -281,7 +286,7 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { (ctx: NetscriptContext) => (_augName: unknown): number => { checkSleeveAPIAccess(ctx); - const augName = ctx.helper.string("augName", _augName); + const augName = helpers.string(ctx, "augName", _augName); const aug: Augmentation = StaticAugmentations[augName]; return aug.baseCost; }, @@ -289,20 +294,20 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { (ctx: NetscriptContext) => (_augName: unknown): number => { checkSleeveAPIAccess(ctx); - const augName = ctx.helper.string("augName", _augName); + const augName = helpers.string(ctx, "augName", _augName); const aug: Augmentation = StaticAugmentations[augName]; return aug.getCost(player).repCost; }, setToBladeburnerAction: (ctx: NetscriptContext) => (_sleeveNumber: unknown, _action: unknown, _contract?: unknown): boolean => { - const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); - const action = ctx.helper.string("action", _action); + const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); + const action = helpers.string(ctx, "action", _action); let contract: string; if (typeof _contract === "undefined") { contract = "------"; } else { - contract = ctx.helper.string("contract", _contract); + contract = helpers.string(ctx, "contract", _contract); } checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); @@ -315,7 +320,8 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { } const other = player.sleeves[i]; if (isSleeveBladeburnerWork(other.currentWork) && other.currentWork.actionName === contract) { - throw ctx.helper.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, `Sleeve ${sleeveNumber} cannot take on contracts because Sleeve ${i} is already performing that action.`, ); } diff --git a/src/NetscriptFunctions/Stanek.ts b/src/NetscriptFunctions/Stanek.ts index e4e8f1008..c2e936f1b 100644 --- a/src/NetscriptFunctions/Stanek.ts +++ b/src/NetscriptFunctions/Stanek.ts @@ -1,7 +1,4 @@ -import { INetscriptHelper } from "./INetscriptHelper"; -import { IPlayer } from "../PersonObjects/IPlayer"; -import { WorkerScript } from "../Netscript/WorkerScript"; -import { netscriptDelay } from "../NetscriptEvaluator"; +import { Player as player } from "../Player"; import { staneksGift } from "../CotMG/Helper"; import { Fragments, FragmentById } from "../CotMG/Fragment"; @@ -18,111 +15,109 @@ import { applyAugmentation } from "../Augmentation/AugmentationHelpers"; import { FactionNames } from "../Faction/data/FactionNames"; import { joinFaction } from "../Faction/FactionHelpers"; import { Factions } from "../Faction/Factions"; +import { helpers } from "../Netscript/NetscriptHelpers"; -export function NetscriptStanek( - player: IPlayer, - workerScript: WorkerScript, - helper: INetscriptHelper, -): InternalAPI { - function checkStanekAPIAccess(func: string): void { +export function NetscriptStanek(): InternalAPI { + function checkStanekAPIAccess(ctx: NetscriptContext): void { if (!player.hasAugmentation(AugmentationNames.StaneksGift1, true)) { - throw helper.makeRuntimeErrorMsg(func, "Stanek's Gift is not installed"); + throw helpers.makeRuntimeErrorMsg(ctx, "Stanek's Gift is not installed"); } } return { - giftWidth: () => + giftWidth: (ctx: NetscriptContext) => function (): number { - checkStanekAPIAccess("giftWidth"); + checkStanekAPIAccess(ctx); return staneksGift.width(); }, - giftHeight: () => + giftHeight: (ctx: NetscriptContext) => function (): number { - checkStanekAPIAccess("giftHeight"); + checkStanekAPIAccess(ctx); return staneksGift.height(); }, - chargeFragment: (_ctx: NetscriptContext) => + chargeFragment: (ctx: NetscriptContext) => function (_rootX: unknown, _rootY: unknown): Promise { //Get the fragment object using the given coordinates - const rootX = _ctx.helper.number("rootX", _rootX); - const rootY = _ctx.helper.number("rootY", _rootY); - checkStanekAPIAccess("chargeFragment"); + const rootX = helpers.number(ctx, "rootX", _rootX); + const rootY = helpers.number(ctx, "rootY", _rootY); + checkStanekAPIAccess(ctx); const fragment = staneksGift.findFragment(rootX, rootY); //Check whether the selected fragment can ge charged - if (!fragment) throw _ctx.makeRuntimeErrorMsg(`No fragment with root (${rootX}, ${rootY}).`); + if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `No fragment with root (${rootX}, ${rootY}).`); if (fragment.fragment().type == FragmentType.Booster) { - throw _ctx.makeRuntimeErrorMsg( + throw helpers.makeRuntimeErrorMsg( + ctx, `The fragment with root (${rootX}, ${rootY}) is a Booster Fragment and thus cannot be charged.`, ); } //Charge the fragment const time = staneksGift.inBonus() ? 200 : 1000; - return netscriptDelay(time, workerScript).then(function () { - staneksGift.charge(player, fragment, workerScript.scriptRef.threads); - _ctx.log(() => `Charged fragment with ${_ctx.workerScript.scriptRef.threads} threads.`); + return helpers.netscriptDelay(ctx, time).then(function () { + staneksGift.charge(player, fragment, ctx.workerScript.scriptRef.threads); + helpers.log(ctx, () => `Charged fragment with ${ctx.workerScript.scriptRef.threads} threads.`); return Promise.resolve(); }); }, - fragmentDefinitions: (_ctx: NetscriptContext) => + fragmentDefinitions: (ctx: NetscriptContext) => function (): IFragment[] { - checkStanekAPIAccess("fragmentDefinitions"); - _ctx.log(() => `Returned ${Fragments.length} fragments`); + checkStanekAPIAccess(ctx); + helpers.log(ctx, () => `Returned ${Fragments.length} fragments`); return Fragments.map((f) => f.copy()); }, - activeFragments: (_ctx: NetscriptContext) => + activeFragments: (ctx: NetscriptContext) => function (): IActiveFragment[] { - checkStanekAPIAccess("activeFragments"); - _ctx.log(() => `Returned ${staneksGift.fragments.length} fragments`); + checkStanekAPIAccess(ctx); + helpers.log(ctx, () => `Returned ${staneksGift.fragments.length} fragments`); return staneksGift.fragments.map((af) => { return { ...af.copy(), ...af.fragment().copy() }; }); }, - clearGift: (_ctx: NetscriptContext) => + clearGift: (ctx: NetscriptContext) => function (): void { - checkStanekAPIAccess("clearGift"); - _ctx.log(() => `Cleared Stanek's Gift.`); + checkStanekAPIAccess(ctx); + helpers.log(ctx, () => `Cleared Stanek's Gift.`); staneksGift.clear(); }, - canPlaceFragment: (_ctx: NetscriptContext) => + canPlaceFragment: (ctx: NetscriptContext) => function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean { - const rootX = _ctx.helper.number("rootX", _rootX); - const rootY = _ctx.helper.number("rootY", _rootY); - const rotation = _ctx.helper.number("rotation", _rotation); - const fragmentId = _ctx.helper.number("fragmentId", _fragmentId); - checkStanekAPIAccess("canPlaceFragment"); + const rootX = helpers.number(ctx, "rootX", _rootX); + const rootY = helpers.number(ctx, "rootY", _rootY); + const rotation = helpers.number(ctx, "rotation", _rotation); + const fragmentId = helpers.number(ctx, "fragmentId", _fragmentId); + checkStanekAPIAccess(ctx); const fragment = FragmentById(fragmentId); - if (!fragment) throw _ctx.makeRuntimeErrorMsg(`Invalid fragment id: ${fragmentId}`); + if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid fragment id: ${fragmentId}`); const can = staneksGift.canPlace(rootX, rootY, rotation, fragment); return can; }, - placeFragment: (_ctx: NetscriptContext) => + placeFragment: (ctx: NetscriptContext) => function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean { - const rootX = _ctx.helper.number("rootX", _rootX); - const rootY = _ctx.helper.number("rootY", _rootY); - const rotation = _ctx.helper.number("rotation", _rotation); - const fragmentId = _ctx.helper.number("fragmentId", _fragmentId); - checkStanekAPIAccess("placeFragment"); + const rootX = helpers.number(ctx, "rootX", _rootX); + const rootY = helpers.number(ctx, "rootY", _rootY); + const rotation = helpers.number(ctx, "rotation", _rotation); + const fragmentId = helpers.number(ctx, "fragmentId", _fragmentId); + checkStanekAPIAccess(ctx); const fragment = FragmentById(fragmentId); - if (!fragment) throw _ctx.makeRuntimeErrorMsg(`Invalid fragment id: ${fragmentId}`); + if (!fragment) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid fragment id: ${fragmentId}`); return staneksGift.place(rootX, rootY, rotation, fragment); }, - getFragment: (_ctx: NetscriptContext) => + getFragment: (ctx: NetscriptContext) => function (_rootX: unknown, _rootY: unknown): IActiveFragment | undefined { - const rootX = _ctx.helper.number("rootX", _rootX); - const rootY = _ctx.helper.number("rootY", _rootY); - checkStanekAPIAccess("getFragment"); + const rootX = helpers.number(ctx, "rootX", _rootX); + const rootY = helpers.number(ctx, "rootY", _rootY); + checkStanekAPIAccess(ctx); const fragment = staneksGift.findFragment(rootX, rootY); if (fragment !== undefined) return fragment.copy(); return undefined; }, - removeFragment: (_ctx: NetscriptContext) => + removeFragment: (ctx: NetscriptContext) => function (_rootX: unknown, _rootY: unknown): boolean { - const rootX = _ctx.helper.number("rootX", _rootX); - const rootY = _ctx.helper.number("rootY", _rootY); - checkStanekAPIAccess("removeFragment"); + const rootX = helpers.number(ctx, "rootX", _rootX); + const rootY = helpers.number(ctx, "rootY", _rootY); + checkStanekAPIAccess(ctx); return staneksGift.delete(rootX, rootY); }, - acceptGift: (_ctx: NetscriptContext) => + acceptGift: (ctx: NetscriptContext) => function (): boolean { //Check if the player is eligible to join the church if ( @@ -138,7 +133,8 @@ export function NetscriptStanek( !player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1) ) { applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 }); - _ctx.log( + helpers.log( + ctx, () => `'${FactionNames.ChurchOfTheMachineGod}' joined and '${AugmentationNames.StaneksGift1}' installed.`, ); } diff --git a/src/NetscriptFunctions/StockMarket.ts b/src/NetscriptFunctions/StockMarket.ts index 7236e617c..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"; @@ -14,25 +13,26 @@ import { } from "../StockMarket/StockMarketCosts"; import { Stock } from "../StockMarket/Stock"; import { StockOrder, TIX } from "../ScriptEditor/NetscriptDefinitions"; -import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper"; +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 */ const checkTixApiAccess = function (ctx: NetscriptContext): void { if (!player.hasWseAccount) { - throw ctx.makeRuntimeErrorMsg(`You don't have WSE Access! Cannot use ${ctx.function}()`); + throw helpers.makeRuntimeErrorMsg(ctx, `You don't have WSE Access! Cannot use ${ctx.function}()`); } if (!player.hasTixApiAccess) { - throw ctx.makeRuntimeErrorMsg(`You don't have TIX API Access! Cannot use ${ctx.function}()`); + throw helpers.makeRuntimeErrorMsg(ctx, `You don't have TIX API Access! Cannot use ${ctx.function}()`); } }; const getStockFromSymbol = function (ctx: NetscriptContext, symbol: string): Stock { const stock = SymbolToStockMap[symbol]; if (stock == null) { - throw ctx.makeRuntimeErrorMsg(`Invalid stock symbol: '${symbol}'`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid stock symbol: '${symbol}'`); } return stock; @@ -58,7 +58,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getPrice: (ctx: NetscriptContext) => (_symbol: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); + const symbol = helpers.string(ctx, "symbol", _symbol); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); @@ -67,7 +67,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getAskPrice: (ctx: NetscriptContext) => (_symbol: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); + const symbol = helpers.string(ctx, "symbol", _symbol); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); @@ -76,7 +76,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getBidPrice: (ctx: NetscriptContext) => (_symbol: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); + const symbol = helpers.string(ctx, "symbol", _symbol); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); @@ -85,18 +85,18 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getPosition: (ctx: NetscriptContext) => (_symbol: unknown): [number, number, number, number] => { - const symbol = ctx.helper.string("symbol", _symbol); + const symbol = helpers.string(ctx, "symbol", _symbol); checkTixApiAccess(ctx); const stock = SymbolToStockMap[symbol]; if (stock == null) { - throw ctx.makeRuntimeErrorMsg(`Invalid stock symbol: ${symbol}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid stock symbol: ${symbol}`); } return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx]; }, getMaxShares: (ctx: NetscriptContext) => (_symbol: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); + const symbol = helpers.string(ctx, "symbol", _symbol); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); @@ -105,9 +105,9 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getPurchaseCost: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown, _posType: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); - let shares = ctx.helper.number("shares", _shares); - const posType = ctx.helper.string("posType", _posType); + const symbol = helpers.string(ctx, "symbol", _symbol); + let shares = helpers.number(ctx, "shares", _shares); + const posType = helpers.string(ctx, "posType", _posType); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); shares = Math.round(shares); @@ -132,9 +132,9 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getSaleGain: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown, _posType: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); - let shares = ctx.helper.number("shares", _shares); - const posType = ctx.helper.string("posType", _posType); + const symbol = helpers.string(ctx, "symbol", _symbol); + let shares = helpers.number(ctx, "shares", _shares); + const posType = helpers.string(ctx, "posType", _posType); checkTixApiAccess(ctx); const stock = getStockFromSymbol(ctx, symbol); shares = Math.round(shares); @@ -159,68 +159,77 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript buyStock: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); - const shares = ctx.helper.number("shares", _shares); + const symbol = helpers.string(ctx, "symbol", _symbol); + 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: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); - const shares = ctx.helper.number("shares", _shares); + const symbol = helpers.string(ctx, "symbol", _symbol); + 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; }, buyShort: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); - const shares = ctx.helper.number("shares", _shares); + const symbol = helpers.string(ctx, "symbol", _symbol); + const shares = helpers.number(ctx, "shares", _shares); checkTixApiAccess(ctx); if (player.bitNodeN !== 8) { if (player.sourceFileLvl(8) <= 1) { - throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2."); + throw helpers.makeRuntimeErrorMsg( + ctx, + "You must either be in BitNode-8 or you must have Source-File 8 Level 2.", + ); } } const stock = getStockFromSymbol(ctx, symbol); - const res = shortStock(stock, shares, workerScript, {}); + const res = shortStock(stock, shares, ctx.workerScript, {}); return res ? stock.getBidPrice() : 0; }, sellShort: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); - const shares = ctx.helper.number("shares", _shares); + const symbol = helpers.string(ctx, "symbol", _symbol); + const shares = helpers.number(ctx, "shares", _shares); checkTixApiAccess(ctx); if (player.bitNodeN !== 8) { if (player.sourceFileLvl(8) <= 1) { - throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2."); + throw helpers.makeRuntimeErrorMsg( + ctx, + "You must either be in BitNode-8 or you must have Source-File 8 Level 2.", + ); } } const stock = getStockFromSymbol(ctx, symbol); - const res = sellShort(stock, shares, workerScript, {}); + const res = sellShort(stock, shares, ctx.workerScript, {}); return res ? stock.getAskPrice() : 0; }, placeOrder: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => { - const symbol = ctx.helper.string("symbol", _symbol); - const shares = ctx.helper.number("shares", _shares); - const price = ctx.helper.number("price", _price); - const type = ctx.helper.string("type", _type); - const pos = ctx.helper.string("pos", _pos); + const symbol = helpers.string(ctx, "symbol", _symbol); + const shares = helpers.number(ctx, "shares", _shares); + const price = helpers.number(ctx, "price", _price); + const type = helpers.string(ctx, "type", _type); + const pos = helpers.string(ctx, "pos", _pos); checkTixApiAccess(ctx); if (player.bitNodeN !== 8) { if (player.sourceFileLvl(8) <= 2) { - throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3."); + throw helpers.makeRuntimeErrorMsg( + ctx, + "You must either be in BitNode-8 or you must have Source-File 8 Level 3.", + ); } } const stock = getStockFromSymbol(ctx, symbol); @@ -237,7 +246,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript } else if (ltype.includes("stop") && ltype.includes("sell")) { orderType = OrderTypes.StopSell; } else { - throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`); } const lpos = pos.toLowerCase(); @@ -246,28 +255,34 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript } else if (lpos.includes("s")) { orderPos = PositionTypes.Short; } else { - throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`); + 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) => (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => { - const symbol = ctx.helper.string("symbol", _symbol); - const shares = ctx.helper.number("shares", _shares); - const price = ctx.helper.number("price", _price); - const type = ctx.helper.string("type", _type); - const pos = ctx.helper.string("pos", _pos); + const symbol = helpers.string(ctx, "symbol", _symbol); + const shares = helpers.number(ctx, "shares", _shares); + const price = helpers.number(ctx, "price", _price); + const type = helpers.string(ctx, "type", _type); + const pos = helpers.string(ctx, "pos", _pos); checkTixApiAccess(ctx); if (player.bitNodeN !== 8) { if (player.sourceFileLvl(8) <= 2) { - throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3."); + throw helpers.makeRuntimeErrorMsg( + ctx, + "You must either be in BitNode-8 or you must have Source-File 8 Level 3.", + ); } } const stock = getStockFromSymbol(ctx, symbol); if (isNaN(shares) || isNaN(price)) { - throw ctx.makeRuntimeErrorMsg(`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`); + throw helpers.makeRuntimeErrorMsg( + ctx, + `Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`, + ); } let orderType; let orderPos; @@ -281,7 +296,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript } else if (ltype.includes("stop") && ltype.includes("sell")) { orderType = OrderTypes.StopSell; } else { - throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`); } const lpos = pos.toLowerCase(); @@ -290,7 +305,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript } else if (lpos.includes("s")) { orderPos = PositionTypes.Short; } else { - throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`); + throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`); } const params = { stock: stock, @@ -299,13 +314,13 @@ 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); if (player.bitNodeN !== 8) { if (player.sourceFileLvl(8) <= 2) { - throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or have Source-File 8 Level 3."); + throw helpers.makeRuntimeErrorMsg(ctx, "You must either be in BitNode-8 or have Source-File 8 Level 3."); } } @@ -332,9 +347,9 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getVolatility: (ctx: NetscriptContext) => (_symbol: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); + const symbol = helpers.string(ctx, "symbol", _symbol); if (!player.has4SDataTixApi) { - throw ctx.makeRuntimeErrorMsg("You don't have 4S Market Data TIX API Access!"); + throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!"); } const stock = getStockFromSymbol(ctx, symbol); @@ -343,9 +358,9 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript getForecast: (ctx: NetscriptContext) => (_symbol: unknown): number => { - const symbol = ctx.helper.string("symbol", _symbol); + const symbol = helpers.string(ctx, "symbol", _symbol); if (!player.has4SDataTixApi) { - throw ctx.makeRuntimeErrorMsg("You don't have 4S Market Data TIX API Access!"); + throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!"); } const stock = getStockFromSymbol(ctx, symbol); @@ -355,69 +370,69 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript }, purchase4SMarketData: (ctx: NetscriptContext) => (): boolean => { if (player.has4SData) { - ctx.log(() => "Already purchased 4S Market Data."); + helpers.log(ctx, () => "Already purchased 4S Market Data."); return true; } if (player.money < getStockMarket4SDataCost()) { - ctx.log(() => "Not enough money to purchase 4S Market Data."); + helpers.log(ctx, () => "Not enough money to purchase 4S Market Data."); return false; } player.has4SData = true; player.loseMoney(getStockMarket4SDataCost(), "stock"); - ctx.log(() => "Purchased 4S Market Data"); + helpers.log(ctx, () => "Purchased 4S Market Data"); return true; }, purchase4SMarketDataTixApi: (ctx: NetscriptContext) => (): boolean => { checkTixApiAccess(ctx); if (player.has4SDataTixApi) { - ctx.log(() => "Already purchased 4S Market Data TIX API"); + helpers.log(ctx, () => "Already purchased 4S Market Data TIX API"); return true; } if (player.money < getStockMarket4STixApiCost()) { - ctx.log(() => "Not enough money to purchase 4S Market Data TIX API"); + helpers.log(ctx, () => "Not enough money to purchase 4S Market Data TIX API"); return false; } player.has4SDataTixApi = true; player.loseMoney(getStockMarket4STixApiCost(), "stock"); - ctx.log(() => "Purchased 4S Market Data TIX API"); + helpers.log(ctx, () => "Purchased 4S Market Data TIX API"); return true; }, purchaseWseAccount: (ctx: NetscriptContext) => (): boolean => { if (player.hasWseAccount) { - ctx.log(() => "Already purchased WSE Account"); + helpers.log(ctx, () => "Already purchased WSE Account"); return true; } if (player.money < getStockMarketWseCost()) { - ctx.log(() => "Not enough money to purchase WSE Account Access"); + helpers.log(ctx, () => "Not enough money to purchase WSE Account Access"); return false; } player.hasWseAccount = true; initStockMarketFn(); player.loseMoney(getStockMarketWseCost(), "stock"); - ctx.log(() => "Purchased WSE Account Access"); + helpers.log(ctx, () => "Purchased WSE Account Access"); return true; }, purchaseTixApi: (ctx: NetscriptContext) => (): boolean => { if (player.hasTixApiAccess) { - ctx.log(() => "Already purchased TIX API"); + helpers.log(ctx, () => "Already purchased TIX API"); return true; } if (player.money < getStockMarketTixApiCost()) { - ctx.log(() => "Not enough money to purchase TIX API Access"); + helpers.log(ctx, () => "Not enough money to purchase TIX API Access"); return false; } player.hasTixApiAccess = true; player.loseMoney(getStockMarketTixApiCost(), "stock"); - ctx.log(() => "Purchased TIX API"); + helpers.log(ctx, () => "Purchased TIX API"); return true; }, }; diff --git a/src/NetscriptFunctions/UserInterface.ts b/src/NetscriptFunctions/UserInterface.ts index 9175ffcf4..530247ba0 100644 --- a/src/NetscriptFunctions/UserInterface.ts +++ b/src/NetscriptFunctions/UserInterface.ts @@ -10,8 +10,9 @@ import { defaultTheme } from "../Themes/Themes"; import { defaultStyles } from "../Themes/Styles"; import { CONSTANTS } from "../Constants"; import { hash } from "../hash/hash"; -import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper"; +import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { Terminal } from "../../src/Terminal"; +import { helpers } from "../Netscript/NetscriptHelpers"; export function NetscriptUserInterface(): InternalAPI { return { @@ -43,9 +44,9 @@ export function NetscriptUserInterface(): InternalAPI { if (errors.length === 0) { Object.assign(Settings.theme, currentTheme); ThemeEvents.emit(); - ctx.log(() => `Successfully set theme`); + helpers.log(ctx, () => `Successfully set theme`); } else { - ctx.log(() => `Failed to set theme. Errors: ${errors.join(", ")}`); + helpers.log(ctx, () => `Failed to set theme. Errors: ${errors.join(", ")}`); } }, @@ -66,22 +67,22 @@ export function NetscriptUserInterface(): InternalAPI { if (errors.length === 0) { Object.assign(Settings.styles, currentStyles); ThemeEvents.emit(); - ctx.log(() => `Successfully set styles`); + helpers.log(ctx, () => `Successfully set styles`); } else { - ctx.log(() => `Failed to set styles. Errors: ${errors.join(", ")}`); + helpers.log(ctx, () => `Failed to set styles. Errors: ${errors.join(", ")}`); } }, resetTheme: (ctx: NetscriptContext) => (): void => { Settings.theme = { ...defaultTheme }; ThemeEvents.emit(); - ctx.log(() => `Reinitialized theme to default`); + helpers.log(ctx, () => `Reinitialized theme to default`); }, resetStyles: (ctx: NetscriptContext) => (): void => { Settings.styles = { ...defaultStyles }; ThemeEvents.emit(); - ctx.log(() => `Reinitialized styles to default`); + helpers.log(ctx, () => `Reinitialized styles to default`); }, getGameInfo: () => (): GameInfo => { @@ -99,7 +100,7 @@ export function NetscriptUserInterface(): InternalAPI { }, clearTerminal: (ctx: NetscriptContext) => (): void => { - ctx.log(() => `Clearing terminal`); + helpers.log(ctx, () => `Clearing terminal`); Terminal.clear(); }, }; diff --git a/src/NetscriptJSEvaluator.ts b/src/NetscriptJSEvaluator.ts index cb5102b55..d3e31f1d2 100644 --- a/src/NetscriptJSEvaluator.ts +++ b/src/NetscriptJSEvaluator.ts @@ -5,7 +5,7 @@ import * as walk from "acorn-walk"; import { parse } from "acorn"; -import { makeRuntimeRejectMsg } from "./NetscriptEvaluator"; +import { helpers } from "./Netscript/NetscriptHelpers"; import { ScriptUrl } from "./Script/ScriptUrl"; import { WorkerScript } from "./Netscript/WorkerScript"; import { Script } from "./Script/Script"; @@ -74,18 +74,21 @@ export async function executeJSScript( const ns = workerScript.env.vars; if (!loadedModule) { - throw makeRuntimeRejectMsg(workerScript, `${script.filename} cannot be run because the script module won't load`); + throw helpers.makeRuntimeRejectMsg( + workerScript, + `${script.filename} cannot be run because the script module won't load`, + ); } // TODO: putting await in a non-async function yields unhelpful // "SyntaxError: unexpected reserved word" with no line number information. if (!loadedModule.main) { - throw makeRuntimeRejectMsg( + throw helpers.makeRuntimeRejectMsg( workerScript, `${script.filename} cannot be run because it does not have a main function.`, ); } if (!ns) { - throw makeRuntimeRejectMsg( + throw helpers.makeRuntimeRejectMsg( workerScript, `${script.filename} cannot be run because the NS object hasn't been constructed properly.`, ); diff --git a/src/NetscriptWorker.ts b/src/NetscriptWorker.ts index 5c8f8ad93..cff1b83d2 100644 --- a/src/NetscriptWorker.ts +++ b/src/NetscriptWorker.ts @@ -11,7 +11,6 @@ import { generateNextPid } from "./Netscript/Pid"; import { CONSTANTS } from "./Constants"; import { Interpreter } from "./ThirdParty/JSInterpreter"; -import { isScriptErrorMessage, makeRuntimeRejectMsg } from "./NetscriptEvaluator"; import { NetscriptFunctions } from "./NetscriptFunctions"; import { executeJSScript, Node } from "./NetscriptJSEvaluator"; import { NetscriptPort, IPort } from "./NetscriptPort"; @@ -29,7 +28,6 @@ import { dialogBoxCreate } from "./ui/React/DialogBox"; import { arrayToString } from "./utils/helpers/arrayToString"; import { roundToTwo } from "./utils/helpers/roundToTwo"; import { isString } from "./utils/helpers/isString"; -import { sprintf } from "sprintf-js"; import { parse } from "acorn"; import { simple as walksimple } from "acorn-walk"; @@ -38,6 +36,8 @@ import { Player } from "./Player"; import { Terminal } from "./Terminal"; import { IPlayer } from "./PersonObjects/IPlayer"; import { ScriptArg } from "./Netscript/ScriptArg"; +import { helpers } from "./Netscript/NetscriptHelpers"; +import { NS } from "./ScriptEditor/NetscriptDefinitions"; // Netscript Ports are instantiated here export const NetscriptPorts: IPort[] = []; @@ -63,83 +63,6 @@ export function prestigeWorkerScripts(): void { // that resolves or rejects when the corresponding worker script is done. function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise { workerScript.running = true; - - // The name of the currently running netscript function, to prevent concurrent - // calls to hack, grow, etc. - let runningFn: string | null = null; - - // We need to go through the environment and wrap each function in such a way that it - // can be called at most once at a time. This will prevent situations where multiple - // hack promises are outstanding, for example. - function wrap(propName: string, f: (...args: unknown[]) => Promise): (...args: unknown[]) => Promise { - // This function unfortunately cannot be an async function, because we don't - // know if the original one was, and there's no way to tell. - return function (...args: unknown[]) { - // Wrap every netscript function with a check for the stop flag. - // This prevents cases where we never stop because we are only calling - // netscript functions that don't check this. - // This is not a problem for legacy Netscript because it also checks the - // stop flag in the evaluator. - if (workerScript.env.stopFlag) { - throw new ScriptDeath(workerScript); - } - - if (propName === "asleep") return f(...args); // OK for multiple simultaneous calls to sleep. - - const msg = - "Concurrent calls to Netscript functions are not allowed! " + - "Did you forget to await hack(), grow(), or some other " + - "promise-returning function? (Currently running: %s tried to run: %s)"; - if (runningFn) { - workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, sprintf(msg, runningFn, propName)); - throw new ScriptDeath(workerScript); - } - runningFn = propName; - - // If the function throws an error, clear the runningFn flag first, and then re-throw it - // This allows people to properly catch errors thrown by NS functions without getting - // the concurrent call error above - let result; - try { - result = f(...args); - } catch (e: unknown) { - runningFn = null; - throw e; - } - - if (result && result.finally !== undefined) { - return result.finally(function () { - runningFn = null; - }); - } else { - runningFn = null; - return result; - } - }; - } - - function wrapObject(vars: unknown, ...tree: string[]): void { - const isObject = (x: unknown): x is { [key: string]: unknown } => typeof x === "object"; - if (!isObject(vars)) throw new Error("wrong argument sent to wrapObject"); - for (const prop of Object.keys(vars)) { - let e = vars[prop]; - switch (typeof e) { - case "function": { - e = wrap([...tree, prop].join("."), e as any); - break; - } - case "object": { - if (Array.isArray(e)) continue; - wrapObject(e, ...tree, prop); - break; - } - } - } - } - wrapObject(workerScript.env.vars); - - // Note: the environment that we pass to the JS script only needs to contain the functions visible - // to that script, which env.vars does at this point. return new Promise((resolve, reject) => { executeJSScript(player, workerScript.getServer().scripts, workerScript) .then(() => { @@ -149,15 +72,18 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro }).catch((e) => { if (e instanceof Error) { if (e instanceof SyntaxError) { - workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, e.message + " (sorry we can't be more helpful)"); + workerScript.errorMessage = helpers.makeRuntimeRejectMsg( + workerScript, + e.message + " (sorry we can't be more helpful)", + ); } else { - workerScript.errorMessage = makeRuntimeRejectMsg( + workerScript.errorMessage = helpers.makeRuntimeRejectMsg( workerScript, e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""), ); } throw new ScriptDeath(workerScript); - } else if (isScriptErrorMessage(e)) { + } else if (helpers.isScriptErrorMessage(e)) { workerScript.errorMessage = e; throw new ScriptDeath(workerScript); } else if (e instanceof ScriptDeath) { @@ -165,7 +91,7 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro } // Don't know what to do with it, let's try making an error message out of it - workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, "" + e); + workerScript.errorMessage = helpers.makeRuntimeRejectMsg(workerScript, "" + e); throw new ScriptDeath(workerScript); }); } @@ -189,8 +115,11 @@ function startNetscript1Script(workerScript: WorkerScript): Promise { } const interpreterInitialization = function (int: Interpreter, scope: unknown): void { + interface NS1 extends NS { + [key: string]: any; + } //Add the Netscript environment - const ns = NetscriptFunctions(workerScript); + const ns = NetscriptFunctions(workerScript) as NS1; for (const name of Object.keys(ns)) { const entry = ns[name]; if (typeof entry === "function") { @@ -242,7 +171,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise { name === "vsprintf" || name === "scp" || name == "write" || - name === "tryWrite" || + name === "tryWritePort" || name === "run" || name === "exec" ) { @@ -319,8 +248,8 @@ function startNetscript1Script(workerScript: WorkerScript): Promise { } } catch (_e: unknown) { let e = String(_e); - if (!isScriptErrorMessage(e)) { - e = makeRuntimeRejectMsg(workerScript, e); + if (!helpers.isScriptErrorMessage(e)) { + e = helpers.makeRuntimeRejectMsg(workerScript, e); } workerScript.errorMessage = e; return reject(new ScriptDeath(workerScript)); @@ -598,7 +527,7 @@ function createAndAddWorkerScript( console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + e.toString()); return; } else if (e instanceof ScriptDeath) { - if (isScriptErrorMessage(workerScript.errorMessage)) { + if (helpers.isScriptErrorMessage(workerScript.errorMessage)) { const errorTextArray = workerScript.errorMessage.split("|DELIMITER|"); if (errorTextArray.length != 4) { console.error("ERROR: Something wrong with Error text in evaluator..."); @@ -622,7 +551,7 @@ function createAndAddWorkerScript( workerScript.log("", () => "Script killed"); return; // Already killed, so stop here } - } else if (isScriptErrorMessage(e)) { + } else if (helpers.isScriptErrorMessage(e)) { dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer"); console.error( "ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " + diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 45525b50e..b20415c1c 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -6924,27 +6924,27 @@ export interface Corporation extends WarehouseAPI, OfficeAPI { * Get list of materials * @returns material names */ - getMaterialNames():string[]; + getMaterialNames(): string[]; /** * Get list of industry types * @returns industry names */ - getIndustryTypes():string[]; + getIndustryTypes(): string[]; /** * Get list of one-time unlockable upgrades * @returns unlockable upgrades names */ - getUnlockables():string[]; + getUnlockables(): string[]; /** * Get list of upgrade names * @returns upgrade names */ - getUpgradeNames():string[]; + getUpgradeNames(): string[]; /** * Get list of research names * @returns research names */ - getResarchNames():string[]; + getResearchNames(): string[]; /** * Accept investment based on you companies current valuation * @remarks 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 {}; } diff --git a/src/UncaughtPromiseHandler.ts b/src/UncaughtPromiseHandler.ts index 775f92fca..e37404a33 100644 --- a/src/UncaughtPromiseHandler.ts +++ b/src/UncaughtPromiseHandler.ts @@ -1,10 +1,10 @@ import { ScriptDeath } from "./Netscript/ScriptDeath"; -import { isScriptErrorMessage } from "./NetscriptEvaluator"; +import { helpers } from "./Netscript/NetscriptHelpers"; import { dialogBoxCreate } from "./ui/React/DialogBox"; export function setupUncaughtPromiseHandler(): void { window.addEventListener("unhandledrejection", function (e) { - if (isScriptErrorMessage(e.reason)) { + if (helpers.isScriptErrorMessage(e.reason)) { const errorTextArray = e.reason.split("|DELIMITER|"); const hostname = errorTextArray[1]; const scriptName = errorTextArray[2];